001/* Utility methods to handle HTML Viewer about: calls 002 003 Copyright (c) 2003-2018 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.actor.gui; 028 029import java.io.BufferedReader; 030import java.io.File; 031import java.io.FileWriter; 032import java.io.IOException; 033import java.io.InputStreamReader; 034import java.net.URI; 035import java.net.URISyntaxException; 036import java.net.URL; 037import java.util.Enumeration; 038import java.util.HashSet; 039import java.util.Iterator; 040import java.util.LinkedList; 041import java.util.List; 042import java.util.Set; 043 044import javax.swing.JFrame; 045import javax.swing.event.HyperlinkEvent; 046 047import ptolemy.actor.CompositeActor; 048import ptolemy.actor.Manager; 049import ptolemy.data.ArrayToken; 050import ptolemy.data.StringToken; 051import ptolemy.data.expr.FileParameter; 052import ptolemy.data.expr.Parameter; 053import ptolemy.kernel.CompositeEntity; 054import ptolemy.kernel.attributes.VersionAttribute; 055import ptolemy.kernel.util.Attribute; 056import ptolemy.kernel.util.IllegalActionException; 057import ptolemy.kernel.util.InternalErrorException; 058import ptolemy.kernel.util.NamedObj; 059import ptolemy.kernel.util.StringAttribute; 060import ptolemy.kernel.util.Workspace; 061import ptolemy.moml.MoMLParser; 062import ptolemy.moml.filter.BackwardCompatibility; 063import ptolemy.util.ClassUtilities; 064import ptolemy.util.FileUtilities; 065import ptolemy.util.StringUtilities; 066 067/////////////////////////////////////////////////////////////////// 068//// HTMLAbout 069 070/** 071 This class contains static methods that are called 072 by when HTMLViewer.hyperlinkUpdate() is invoked on a hyperlink 073 that starts with <code>about:</code>. This facility is primarily 074 used for testing. 075 076 @author Christopher Hylands 077 @version $Id$ 078 @since Ptolemy II 3.0 079 @Pt.ProposedRating Red (cxh) 080 @Pt.AcceptedRating Red (cxh) 081 @see HTMLViewer#hyperlinkUpdate(HyperlinkEvent) 082 */ 083public class HTMLAbout { 084 // This class is separate from HTMLViewer because this class 085 // import lots of Ptolemy specify classes that HTMLViewer does 086 // otherwise need to import 087 /////////////////////////////////////////////////////////////////// 088 //// public methods //// 089 090 /** Return a string containing HTML that describes the about: 091 * features. 092 * 093 * <p>If the configuration contains an _applicationName attribute 094 * then that attribute is used as the name of the application 095 * in the generated text. If _applicationName is not present, 096 * then the default name is "Ptolemy II". 097 * 098 * <p>If the configuration contains an _applicationDemos Parameter 099 * then that parameter is assumed to be an array of strings name 100 * naming HTML files that should be searched for demos and expanded. 101 102 * @param configuration The configuration to look for the 103 * _applicationName attribute in 104 * @return A string containing HTML that describes the about: features. 105 */ 106 public static String about(Configuration configuration) { 107 // Use an explicit version here - the name of the whatsNew file 108 // does not changes as quickly as the version. 109 String version = VersionAttribute.majorCurrentVersion(); 110 111 String applicationName = "Ptolemy II"; 112 113 try { 114 StringAttribute applicationNameAttribute = (StringAttribute) configuration 115 .getAttribute("_applicationName", StringAttribute.class); 116 117 if (applicationNameAttribute != null) { 118 applicationName = applicationNameAttribute.getExpression(); 119 } 120 } catch (Throwable throwable) { 121 // Ignore and use the default applicationName 122 } 123 124 StringBuffer htmlBuffer = new StringBuffer(); 125 htmlBuffer.append("<html><head><title>About " + applicationName 126 + "</title></head>" + "<body><h1>About " + applicationName 127 + "</h1>\n" + "The HTML Viewer in " + applicationName 128 + " handles the <code>about:</code>\n" + "tag specially.\n" 129 + "<br>The following urls are handled:\n" + "<ul>\n" 130 + "<li><a href=\"about:configuration\">" 131 + "<code>about:configuration</code></a> " 132 + "Expand the configuration (good way to test for " 133 + "missing classes).\n" + "<li><a href=\"about:expandLibrary\">" 134 + "<code>about:expandLibrary</code></a> " 135 + "Open a model and expand library tree (good way to test for " 136 + "missing classes, check standard out).\n" 137 + "<li><a href=\"about:copyright\">" 138 + "<code>about:copyright</code></a> " 139 + " Display information about the copyrights.\n"); 140 141 if (_configurationExists("full")) { 142 htmlBuffer.append("<li><a href=\"about:checkCompleteDemos\">" 143 + "<code>about:checkCompleteDemos</code></a> " 144 + "Check that each of the demos listed in the individual " 145 + "files is present in " 146 + "<code>ptolemy/configs/doc/completeDemos.htm</code>.\n"); 147 } 148 149 htmlBuffer.append("</ul>\n<table>\n"); 150 151 _demosURLs = new LinkedList(); 152 if (_configurationExists("full")) { 153 htmlBuffer 154 .append("<tr rowspan=4><center><b>Full</b></center></tr>\n" 155 + _aboutHTML( 156 "ptolemy/configs/doc/completeDemos.htm") 157 + _aboutHTML("ptolemy/configs/doc/demos.htm") 158 + _aboutHTML("ptolemy/configs/doc/whatsNew" 159 + version + ".htm") 160 + _aboutHTML("ptolemy/configs/doc/whatsNew11.0.htm") 161 + _aboutHTML("ptolemy/configs/doc/whatsNew10.0.htm") 162 + _aboutHTML("ptolemy/configs/doc/whatsNew8.0.htm") 163 + _aboutHTML("ptolemy/configs/doc/whatsNew7.0.htm") 164 + _aboutHTML("ptolemy/configs/doc/whatsNew6.0.htm") 165 + _aboutHTML("ptolemy/configs/doc/whatsNew5.1.htm") 166 + _aboutHTML("ptolemy/configs/doc/whatsNew5.0.htm") 167 + _aboutHTML("ptolemy/configs/doc/whatsNew4.0.htm") 168 + _aboutHTML( 169 "ptolemy/configs/doc/whatsNew3.0.2.htm")); 170 } 171 172 if (_configurationExists("bcvtb")) { 173 htmlBuffer 174 .append("<tr rowspan=4><center><b>BCVTB</b></center></tr>\n" 175 + _aboutHTML("ptolemy/configs/bcvtb/intro.htm") 176 + _aboutHTML( 177 "ptolemy/configs/doc/completeDemosBcvtb.htm") 178 + _aboutHTML("ptolemy/configs/doc/demosBcvtb.htm") 179 + _aboutHTML("ptolemy/configs/doc/docsBcvtb.htm")); 180 } 181 if (_configurationExists("capecode")) { 182 htmlBuffer.append( 183 "<tr rowspan=4><center><b>CapeCode</b></center></tr>\n" 184 + _aboutHTML("ptolemy/configs/capecode/intro.htm") 185 + _aboutHTML("ptolemy/configs/capecode/docs.htm") 186 + _aboutHTML( 187 "ptolemy/configs/capecode/demonstrations.htm") 188 + _aboutHTML("ptolemy/configs/capecode/tour.htm")); 189 } 190 if (_configurationExists("cyphysim")) { 191 htmlBuffer.append( 192 "<tr rowspan=4><center><b>CyPhySim</b></center></tr>\n" 193 + _aboutHTML("ptolemy/configs/cyphysim/intro.htm") 194 + _aboutHTML( 195 "ptolemy/configs/cyphysim/demonstrations.htm") 196 + _aboutHTML("ptolemy/configs/cyphysim/docs.htm") 197 + _aboutHTML("ptolemy/configs/doc/docs.htm")); 198 } 199 // Don't include DSP here, it uses the Ptiny demos anyway. 200 if (_configurationExists("hyvisual")) { 201 htmlBuffer.append( 202 "<tr rowspan=4><center><b>HyVisual</b></center></tr>\n" 203 + _aboutHTML("ptolemy/configs/hyvisual/intro.htm")); 204 } 205 206 if (_configurationExists("ptiny")) { 207 htmlBuffer 208 .append("<tr rowspan=4><center><b>Ptiny</b></center></tr>\n" 209 + _aboutHTML( 210 "ptolemy/configs/doc/completeDemosPtiny.htm") 211 + _aboutHTML("ptolemy/configs/doc/demosPtiny.htm") 212 213 + _aboutHTML("doc/mainVergilPtiny.htm")); 214 } 215 216 if (_configurationExists("ptinyKepler")) { 217 htmlBuffer.append( 218 "<tr rowspan=4><center><b>Ptiny for Kepler</b></center></tr>\n" 219 + _aboutHTML("ptolemy/configs/kepler/doc-index.htm") 220 + _aboutHTML("doc/mainVergilPtinyKepler.htm") 221 + _aboutHTML( 222 "ptolemy/configs/doc/demosPtinyKepler.htm") 223 + _aboutHTML( 224 "ptolemy/configs/doc/docsPtinyKepler.htm") 225 + _aboutHTML( 226 "ptolemy/configs/doc/completeDemosPtinyKepler.htm")); 227 228 } 229 if (_configurationExists("visualsense")) { 230 htmlBuffer.append( 231 "<tr rowspan=4><center><b>VisualSense</b></center></tr>\n" 232 + _aboutHTML( 233 "ptolemy/configs/visualsense/intro.htm")); 234 } 235 236 try { 237 // Check for the _applicationDemos parameter 238 Parameter applicationDemos = (Parameter) configuration 239 .getAttribute("_applicationDemos", Parameter.class); 240 241 if (applicationDemos != null) { 242 htmlBuffer.append("<tr rowspan=4><center><b>" + applicationName 243 + "</b></center></tr>\n"); 244 245 ArrayToken demoTokens = (ArrayToken) applicationDemos 246 .getToken(); 247 248 for (int i = 0; i < demoTokens.length(); i++) { 249 StringToken demoToken = (StringToken) demoTokens 250 .getElement(i); 251 htmlBuffer.append(_aboutHTML(demoToken.stringValue())); 252 System.out.println( 253 "HTMLAbout: adding " + demoToken.stringValue()); 254 } 255 } 256 } catch (Exception ex) { 257 throw new InternalErrorException(configuration, ex, 258 "Bad configuration for " + applicationName); 259 } 260 261 htmlBuffer.append("</table>\n"); 262 htmlBuffer.append("</body>\n</html>\n"); 263 return htmlBuffer.toString(); 264 } 265 266 /** Check that all the demos in otherDemos are in completeDemos. 267 * Be sure to call {@link #about(Configuration)} before calling this method. 268 * @param completeDemos A URL pointing to the completeDemos.htm file 269 * @return HTML listing demos in otherDemos that are not in completeDemos. 270 * @exception IOException If there is a problem reading the 271 * completeDemos.htm file. 272 */ 273 public static String checkCompleteDemos(String completeDemos) 274 throws IOException { 275 URL demosURL = _getDemoURL(completeDemos); 276 if (demosURL == null) { 277 throw new IOException("Could not find any demos in " + completeDemos 278 + ". Does that file exist?"); 279 } 280 StringBuffer results = new StringBuffer( 281 "<h1>Results of checking for demos not listed in full " 282 + "demos</h1>\n" 283 + "For each of the files below, we list demos that are " 284 + "not included in <a href=\"" + demosURL + "\">" 285 + "<code>" + demosURL + "</code></a>\n"); 286 List completeDemosList = _getURLs(demosURL, ".*.xml$", true, 1); 287 if (_demosURLs == null) { 288 throw new NullPointerException( 289 "_demosURLs is null. Call HTMLAbout.about(Configuration) first."); 290 } 291 Iterator demosFileNames = _demosURLs.iterator(); 292 while (demosFileNames.hasNext()) { 293 String demosFileName = (String) demosFileNames.next(); 294 URL demoURL = _getDemoURL(demosFileName); 295 if (demoURL != null) { 296 results.append("<h2><a href=\"" + demoURL + "\"><code>" 297 + demoURL + "</code></a></h2>\n<ul>\n"); 298 299 List demosList = _getURLs(demoURL, ".*.xml$", true, 0); 300 Iterator demos = demosList.iterator(); 301 while (demos.hasNext()) { 302 String demo = (String) demos.next(); 303 if (!completeDemosList.contains(demo)) { 304 try { 305 URL missingDemoURL = ConfigurationApplication 306 .specToURL(demo); 307 results.append(" <li><a href=\"" + missingDemoURL 308 + "\">" + missingDemoURL + "</a></li>\n"); 309 } catch (IOException ex) { 310 results.append(" <li><a href=\"file:/" + demo 311 + "\">" + demo + "</a></li>\n"); 312 } 313 } 314 } 315 results.append("</ul>\n"); 316 } 317 } 318 return results.toString(); 319 } 320 321 /** Call Configuration.openModel() on relative URLs that match a regexp. 322 * files are linked to from an HTML file. 323 * @param demosFileName The name of the HTML file that contains links 324 * to the .xml, .htm and .html files. 325 * If this argument is the empty string, then 326 * "ptolemy/configs/doc/completeDemos.htm" is used. 327 * @param regexp The regular expression of the links we are interested 328 * in. 329 * @param configuration The configuration to open the files in. 330 * @return the URL of the HTML file that was searched or null 331 * if demosFileName does not exist. 332 * @exception Exception If there is a problem opening a model. 333 */ 334 public static URL generateLinks(String demosFileName, String regexp, 335 Configuration configuration) throws Exception { 336 URL demosURL = _getDemoURL(demosFileName); 337 if (demosURL == null) { 338 return null; 339 } 340 List modelList = _getURLs(demosURL, regexp); 341 Iterator models = modelList.iterator(); 342 343 while (models.hasNext()) { 344 String model = (String) models.next(); 345 if (model.startsWith("ptdoc:")) { 346 Effigy context = configuration.getDirectory() 347 .entityList(Effigy.class).iterator().next(); 348 HTMLViewer.getDocumentation(configuration, model.substring(6), 349 context); 350 } else { 351 URL modelURL = new URL(demosURL, model); 352 353 try { 354 configuration.openModel(demosURL, modelURL, 355 modelURL.toExternalForm()); 356 } catch (Throwable throwable) { 357 throw new Exception("Failed to open '" + modelURL + "'", 358 throwable); 359 } 360 } 361 } 362 363 return demosURL; 364 } 365 366 /** Process an "about:" HyperlinkEvent. 367 * @param event The HyperlinkEvent to process. The description of 368 * the event should start with "about:". If there are no specific 369 * matches for the description, then a general usage message is 370 * returned. 371 * @param configuration The configuration in which we are operating. 372 * @return A URL that points to the results. 373 * @exception Throwable If there is a problem invoking the about 374 * task. 375 */ 376 public static URL hyperlinkUpdate(HyperlinkEvent event, 377 Configuration configuration) throws Throwable { 378 URL newURL = null; 379 380 if (event.getDescription().equals("about:allcopyrights")) { 381 // Note that if we have a link that is 382 // <a href="about:copyright">about:allcopyrights</a> 383 // then event.getURL() will return null, so we have 384 // to use getDescription() 385 try { 386 newURL = _temporaryHTMLFile("generatecopyright", ".htm", 387 GenerateCopyrights.generateHTML(configuration)); 388 } catch (SecurityException ex) { 389 // Could be that we were running with -sandbox and 390 // cannot write the temporary file. 391 newURL = FileUtilities.nameToURL( 392 "$CLASSPATH/ptolemy/configs/doc/copyright.htm", null, 393 null); 394 } 395 396 } else if (event.getDescription() 397 .startsWith("about:checkCompleteDemos")) { 398 newURL = _temporaryHTMLFile("checkCompleteDemos", ".htm", 399 checkCompleteDemos( 400 "ptolemy/configs/doc/completeDemos.htm")); 401 } else if (event.getDescription().startsWith("about:checkModelSizes")) { 402 // Expand all the local .xml files in the fragment 403 // and check their sizes and locations 404 URI aboutURI = new URI(event.getDescription()); 405 URL demosURL = _getDemoURL(aboutURI.getFragment()); 406 // Third arg is true so modelList should contain absolute URLs. 407 List modelList = _getURLs(demosURL, ".*(.htm|.html|.xml)", true, 2); 408 // Convert the list to a Set and avoid duplicates. 409 Set modelSet = new HashSet(modelList); 410 newURL = _temporaryHTMLFile("checkModelSizes", ".htm", 411 CheckModelSize.checkModelSize(configuration, 412 (String[]) modelSet 413 .toArray(new String[modelSet.size()]))); 414 } else if (event.getDescription().equals("about:copyright")) { 415 // Note that if we have a link that is 416 // <a href="about:copyright">about:copyright</a> 417 // then event.getURL() will return null, so we have 418 // to use getDescription() 419 try { 420 newURL = _temporaryHTMLFile("copyright", ".htm", 421 GenerateCopyrights 422 .generatePrimaryCopyrightHTML(configuration) 423 + "<p>Other <a href=\"about:allcopyrights\">copyrights</a>\n" 424 + "about this configuration \n" 425 + "(<i>may take a moment to run</i>).\n" 426 + "</body>\n</html>"); 427 } catch (SecurityException ex) { 428 // Could be that we were running with -sandbox and 429 // cannot write the temporary file. 430 newURL = FileUtilities.nameToURL( 431 "$CLASSPATH/ptolemy/configs/doc/copyright.htm", null, 432 null); 433 } 434 } else if (event.getDescription().equals("about:configuration")) { 435 // about:configuration will expand the configuration 436 // and report any problems such as missing classes. 437 // Open up the configuration as a .txt file because if 438 // we open it up as a .xml file, we get a graphical browser 439 // that does not tell us much. If we open it up as a .htm, 440 // then the output is confusing. 441 newURL = _temporaryHTMLFile("configuration", ".txt", 442 configuration.check() + configuration.exportMoML()); 443 } else if (event.getDescription().startsWith("about:demos")) { 444 // Expand all the local .xml files in the fragment 445 // and return a URL pointing to the fragment. 446 // If there is no fragment, then use 447 // "ptolemy/configs/doc/completeDemos.htm" 448 URI aboutURI = new URI(event.getDescription()); 449 newURL = generateLinks(aboutURI.getFragment(), ".*.xml$", 450 configuration); 451 } else if (event.getDescription().startsWith("about:links")) { 452 // Expand all the local .html, .htm, .pdf, .xml files in 453 // the fragment and return a URL pointing to the fragment. 454 // If there is no fragment, then use 455 // "ptolemy/configs/doc/completeDemos.htm" 456 URI aboutURI = new URI(event.getDescription()); 457 newURL = generateLinks(aboutURI.getFragment(), 458 "(ptdoc:.*|.*(.htm|.html|.pdf|.xml))", configuration); 459 } else if (event.getDescription().startsWith("about:runAllDemos")) { 460 URI aboutURI = new URI(event.getDescription()); 461 newURL = runAllDemos(aboutURI.getFragment(), configuration); 462 } else if (event.getDescription().startsWith("about:expandLibrary")) { 463 //URI aboutURI = new URI(event.getDescription()); 464 newURL = _expandLibrary(".*.xml", configuration); 465 } else { 466 // Display a message about the about: facility 467 newURL = _temporaryHTMLFile("about", ".htm", about(configuration)); 468 } 469 470 return newURL; 471 } 472 473 /** Generate a file that contains urls of models. 474 * @param args The optional name of the file containing the demos 475 * followed by the optional name of the output file. The default 476 * demo file is ptolemy/configs/doc/completeDemos.htm, the default 477 * output file is models.txt. 478 * @exception IOException If there is a problem reading the demo 479 * file or writing the model file. 480 */ 481 public static void main(String[] args) throws IOException { 482 String demoFileName = "ptolemy/configs/doc/completeDemos.htm"; 483 String outputFileName = "models.txt"; 484 485 if (System.getenv("DISPLAY") != null 486 && System.getenv("DISPLAY").length() > 0) { 487 System.err.println( 488 "Because of issues with webcam-capture hanging during discovery " 489 + "it is best if HTMLAbout is invoked with DISPLAY=\"\". " 490 + "See https://wiki.eecs.berkeley.edu/ptexternal/Main/Main/Finalizers."); 491 } 492 if (args.length > 2) { 493 System.err.println("Usage: [demoFileName [outputFilename]\n" 494 + "demoFileName defaults to " + demoFileName + "\n" 495 + "outputFileName defaults to " + outputFileName + "\n"); 496 StringUtilities.exit(3); 497 } 498 if (args.length >= 1) { 499 demoFileName = args[0]; 500 } 501 if (args.length == 2) { 502 outputFileName = args[1]; 503 } 504 System.out.println( 505 "Printing '.' for regular models, 'P' for models with LiveLinks."); 506 writeDemoURLs(demoFileName, outputFileName); 507 ptolemy.moml.MoMLSimpleApplication.closeVertx(); 508 } 509 510 /** Run all the local .xml files that are linked to from an HTML file. 511 * @param demosFileName The name of the HTML file that contains links 512 * to the .xml files. If this argument is the empty string, then 513 * "ptolemy/configs/doc/completeDemos.htm" is used. 514 * @param configuration The configuration to run the files in. 515 * @return the URL of the HTML file that was searched. 516 * @exception Exception If there is a problem running a demo. 517 */ 518 public static URL runAllDemos(String demosFileName, 519 Configuration configuration) throws Exception { 520 URL demosURL = _getDemoURL(demosFileName); 521 List modelList = _getURLs(demosURL, ".*.xml$"); 522 Iterator models = modelList.iterator(); 523 524 while (models.hasNext()) { 525 String model = (String) models.next(); 526 URL modelURL = new URL(demosURL, model); 527 528 Tableau tableau = configuration.openModel(demosURL, modelURL, 529 modelURL.toExternalForm()); 530 531 if ((Effigy) tableau.getContainer() instanceof PtolemyEffigy) { 532 PtolemyEffigy effigy = (PtolemyEffigy) tableau.getContainer(); 533 CompositeActor actor = (CompositeActor) effigy.getModel(); 534 535 // Create a manager if necessary. 536 Manager manager = actor.getManager(); 537 538 if (manager == null) { 539 manager = new Manager(actor.workspace(), "manager"); 540 actor.setManager(manager); 541 } 542 543 //manager.addExecutionListener(this); 544 manager.execute(); 545 } 546 } 547 548 return demosURL; 549 } 550 551 /** Write the urls of the demo urls. The HTML file referred to by 552 * demoURLName is scanned for links to .xml files and for links to 553 * other .htm* files. The children of demoURLName are scanned, 554 * but not the grandchildren. The names of the demos will have 555 * $CLASSPATH/ prepended. This method is used to generate a list of all 556 * demos in ptolemy/configs/doc/models.txt. 557 * @param demosFileName The name of the demo file. 558 * @param outputFileName The name of the file that is generated. 559 * @exception IOException If there is a problem reading the demo file 560 * or writing the output file. 561 */ 562 public static void writeDemoURLs(String demosFileName, 563 String outputFileName) throws IOException { 564 // Get PTII as C:/cxh/ptII 565 String ptII = null; 566 try { 567 ptII = new URI(StringUtilities.getProperty("ptolemy.ptII.dirAsURL")) 568 .normalize().getPath(); 569 // Under Windows, convert /C:/foo/bar to C:/foo/bar 570 ptII = new File(ptII).getCanonicalPath().replace('\\', '/'); 571 } catch (URISyntaxException ex) { 572 throw new InternalErrorException(null, ex, 573 "Failed to process PTII " + ptII); 574 } 575 if (ptII.length() == 0) { 576 throw new InternalErrorException("Failed to process " 577 + "ptolemy.ptII.dirAsURL property, ptII = null?"); 578 } 579 URL demoURL = ConfigurationApplication.specToURL(demosFileName); 580 List demosList = _getURLs(demoURL, ".*.xml", true, 2); 581 Set demosSet = new HashSet(demosList); 582 FileWriter fileWriter = null; 583 try { 584 fileWriter = new FileWriter(outputFileName); 585 Iterator demos = demosSet.iterator(); 586 while (demos.hasNext()) { 587 String demo = (String) demos.next(); 588 // Look for the value of $PTII and substitute in $CLASSPATH 589 // so that we can use FileUtilities.nameToURL() from within 590 // ptolemy.moml.filter.ActorIndex 591 fileWriter.write( 592 StringUtilities.substitute(demo, ptII, "$CLASSPATH") 593 + "\n"); 594 try { 595 // Open the model, look for any LiveLinks and write them. 596 System.out.print("."); 597 writeLiveLinks(fileWriter, demo, ptII); 598 } catch (Throwable throwable) { 599 System.err.println("Warning: Could not open " + demo 600 + ". This means that any LiveLinks in that model " 601 + "will not be added to the list of all demos. " 602 + "This is not a big problem and can be safely ignored.: " 603 + throwable); 604 } 605 } 606 } finally { 607 if (fileWriter != null) { 608 fileWriter.close(); 609 } 610 } 611 } 612 613 /** Open the model, look for any LiveLinks and write their names. 614 * @param fileWriter The FileWriter to write the file names to.. 615 * @param demo The string path to the demo to be searched for live links. 616 * @param ptII The Ptolemy II home directory. 617 * @exception Throwable If there is a problem opening the demo. 618 */ 619 public static void writeLiveLinks(FileWriter fileWriter, String demo, 620 String ptII) throws Throwable { 621 // Read the model file and look for LiveLinks. This may 622 // miss some LiveLinks that are not in the toplevel model file, but 623 // it is much faster than parsing each model. 624 boolean matches = false; 625 BufferedReader in = null; 626 try { 627 in = new BufferedReader(new InputStreamReader( 628 new File(demo).toURI().toURL().openStream())); 629 630 String inputLine; 631 632 while ((inputLine = in.readLine()) != null) { 633 if (inputLine.matches(".*ptolemy.actor.gui.LiveLink.*")) { 634 matches = true; 635 break; 636 } 637 } 638 } finally { 639 if (in != null) { 640 in.close(); 641 } 642 } 643 644 if (matches) { 645 Workspace workspace = new Workspace("MyWorkspace"); 646 MoMLParser parser = new MoMLParser(); 647 parser.resetAll(); 648 List myFilters = BackwardCompatibility.allFilters(); 649 MoMLParser.addMoMLFilters(myFilters, workspace); 650 System.out.print("P"); 651 //System.out.println("Parsing " + demo); 652 NamedObj namedObj = parser.parseFile(demo); 653 if (namedObj instanceof CompositeEntity) { 654 CompositeEntity model = (CompositeEntity) namedObj; 655 656 // Look for LiveLinks inside regular models, where 657 // the LiveLink is inside an Attribute. 658 Enumeration attributes = model.getAttributes(); 659 while (attributes.hasMoreElements()) { 660 Object object = attributes.nextElement(); 661 // We could check only TextAttributes, that would 662 // make a dependency on 663 // ptolemy.vergil.kernel.attributes.TextAttribute. 664 // Actor.gui does not depend on vergil. 665 // This method is for LiveLink, which is in actor.gui, 666 // so we should not add the dependency. 667 if (object instanceof Attribute) { 668 Enumeration innerAttributes = ((Attribute) object) 669 .getAttributes(); 670 671 _writeLiveLinksAttributes(fileWriter, demo, ptII, 672 innerAttributes); 673 } 674 } 675 676 // Look for LiveLinks inside Ontologies, like 677 // $PTII/ptolemy/demo/ElectricPowerSystem/Overview.xml 678 // where the LiveLink is inside a FiniteConcept, which is 679 // a CompositeEntity. 680 Enumeration entities = model.getEntities(); 681 while (entities.hasMoreElements()) { 682 Object entity = entities.nextElement(); 683 if (entity instanceof NamedObj) { 684 attributes = ((NamedObj) entity).getAttributes(); 685 _writeLiveLinksAttributes(fileWriter, demo, ptII, 686 attributes); 687 } 688 } 689 model.setContainer(null); 690 } 691 } 692 } 693 694 /** Look for LiveLinks in the Enumeration of attributes. 695 * @param fileWriter The FileWriter to write the file names to.. 696 * @param demo The string path to the demo to be searched for live links. 697 * @param ptII The Ptolemy II home directory. 698 * @param innerAttributes An enumeration of attributes 699 * @exception Throwable If there is a problem opening the demo. 700 */ 701 private static void _writeLiveLinksAttributes(FileWriter fileWriter, 702 String demo, String ptII, Enumeration innerAttributes) 703 throws Throwable { 704 while (innerAttributes.hasMoreElements()) { 705 Object innerObject = innerAttributes.nextElement(); 706 // System.out.println("Attribute: " + ((Attribute)innerObject).getFullName()); 707 if (innerObject instanceof LiveLink) { 708 LiveLink liveLink = (LiveLink) innerObject; 709 File liveLinkFile = liveLink.asFile(); 710 String liveLinkValue = liveLink.stringValue(); 711 712 if (liveLinkValue.contains("CLASSPATH") 713 && liveLinkValue.contains(".xml")) { 714 // Look for the value of $PTII and substitute in $CLASSPATH 715 // so that we can use FileUtilities.nameToURL() from within 716 // ptolemy.moml.filter.ActorIndex 717 String demoPath = StringUtilities.substitute( 718 liveLinkFile.getCanonicalPath(), ptII, 719 "$CLASSPATH"); 720 // Remove anything after the .xml so as to support 721 // references to internal entities. 722 demoPath = demoPath.substring(0, 723 demoPath.lastIndexOf(".xml") + 4); 724 // System.out.println( demo + ": contains a LiveLink: " + demoPath); 725 fileWriter.write(demoPath + "\n"); 726 } else if (!liveLinkValue.contains("/") 727 && !liveLinkValue.contains("\\") 728 && liveLinkValue.contains(".xml")) { 729 // The link target is in the same directory as the model. 730 // FIXME: It could be that models with relative LiveLinks will not be found when 731 // the system is using jar files. 732 String demoPath = StringUtilities.substitute( 733 liveLinkFile.getCanonicalPath(), ptII, 734 "$CLASSPATH"); 735 // Remove anything after the .xml. 736 demoPath = demoPath.substring(0, 737 demoPath.lastIndexOf(".xml") + 4); 738 // System.out.println( demo + ": contains a LiveLink: " + demoPath); 739 fileWriter.write(demoPath + "\n"); 740 } 741 } 742 } 743 } 744 745 /////////////////////////////////////////////////////////////////// 746 //// private methods //// 747 // Return a string containing HTML with links the the about:demos 748 // and about:links pages 749 private static String _aboutHTML(String fileName) { 750 _demosURLs.add(fileName); 751 return " <tr>\n" + " <code>" + fileName + "</code>\n" 752 + " <td><a href=\"about:demos#" + fileName 753 + "\"> Open the .xml </a></td>\n" 754 + " <td><a href=\"about:links#" + fileName 755 + "\"> Open the ptdoc: .htm, .html, .xml and .pdf </a></td>\n" 756 + " <td><a href=\"about:checkModelSizes#" + fileName 757 + "\"> Check the sizes/centering of the models </a></td>\n" 758 // RunAllDemos does not work, it runs in the wrong thread? 759 // + " <td><a href=\"about:runAllDemos#" + fileName 760 // + "\"> Run all demos </a></td>\n" 761 + " </tr>\n"; 762 } 763 764 /** Expand the left hand library pane. We search for a model, 765 * first in the intro.htm file, then the complete demos page and 766 * then on the Ptolemy website. The model is opened and then the 767 * left hand library pane is expanded. This test is useful for 768 * looking for problem with icons, such as icons that cause 769 * ChangeRequests. 770 * @param regexp The regular expression of the links we are interested 771 * in. 772 * @param configuration The configuration to open the files in. 773 * @return the URL of the HTML file that was searched. 774 * @exception Exception If there is a problem opening a model. 775 */ 776 public static URL _expandLibrary(String regexp, Configuration configuration) 777 throws Exception { 778 FileParameter aboutAttribute = (FileParameter) configuration 779 .getAttribute("_about", FileParameter.class); 780 781 URL baseURL = null; 782 URL modelURL = null; 783 784 // HyVisual, VisualSense: Search for models in the _about 785 // attribute of the configuration. 786 // If we don't have an _about, or _about returns 787 // no models, then look in the complete demos location 788 789 if (aboutAttribute != null) { 790 baseURL = aboutAttribute.asURL(); 791 String aboutURLString = baseURL.toExternalForm(); 792 String base = aboutURLString.substring(0, 793 aboutURLString.lastIndexOf("/")); 794 baseURL = ConfigurationApplication.specToURL(base + "/intro.htm"); 795 System.out.println( 796 "HTMLAbout._expandLibrary(): looking in about URL: " 797 + baseURL); 798 List modelList = _getURLs(baseURL, regexp); 799 if (modelList.size() > 0) { 800 // Get the first model and open it 801 String model = (String) modelList.get(0); 802 System.out.println( 803 "HTMLAbout._expandLibrary(): looking for model relative to about URL: " 804 + model); 805 modelURL = new URL(baseURL, model); 806 } else { 807 // Get the first url from intro.htm, look in it and get the 808 // first model 809 System.out.println("HTMLAbout._expandLibrary(): looking inside " 810 + baseURL + " for .htm files"); 811 List urlList = _getURLs(baseURL, ".*.htm"); 812 Iterator urls = urlList.iterator(); 813 while (urls.hasNext() && modelURL == null) { 814 // Looping through files, looking for a model 815 String model = (String) urls.next(); 816 System.out.println( 817 "HTMLAbout._expandLibrary(): looking inside " 818 + model); 819 URL possibleModelURL = new URL(baseURL, model); 820 modelList = _getURLs(possibleModelURL, regexp); 821 if (modelList.size() > 0) { 822 model = (String) modelList.get(0); 823 // Get the first model and open it 824 System.out.println( 825 "HTMLAbout._expandLibrary(): looking for model relative to first URL: " 826 + model); 827 modelURL = new URL(baseURL, model); 828 } 829 } 830 } 831 } 832 if (modelURL == null) { 833 // Get completeDemos.htm 834 baseURL = _getDemoURL(null); 835 System.out.println( 836 "HTMLAbout._expandLibrary(): looking in completeDemos URL: " 837 + baseURL); 838 List modelList = _getURLs(baseURL, regexp); 839 if (modelList.size() > 0) { 840 // Get the first model and open it 841 String model = (String) modelList.get(0); 842 System.out.println( 843 "HTMLAbout._expandLibrary(): looking for model relative to completeDemos URL: " 844 + model); 845 modelURL = new URL(baseURL, model); 846 } else { 847 String model = "http://ptolemy.eecs.berkeley.edu/ptolemyII/ptIIlatest/ptII/ptolemy/domains/sdf/demo/Butterfly/Butterfly.xml"; 848 System.out.println( 849 "HTMLAbout._expandLibrary(): looking for model relative to completeDemos URL: " 850 + model); 851 modelURL = new URL(model); 852 } 853 } 854 855 System.out.println("HTMLAbout._expandLibrary(): baseURL: " + baseURL); 856 System.out.println("HTMLAbout._expandLibrary(): modelURL: " + modelURL); 857 Tableau tableau = configuration.openModel(baseURL, modelURL, 858 modelURL.toExternalForm()); 859 final JFrame jFrame = tableau.getFrame(); 860 //jFrame.show(); 861 862 String errorMessage = "Expanding the library <b>should</b> result in expanding " 863 + "everything in the left hand tree pane. " 864 + "<p>If the left hand tree pane expands and then contracts, " 865 + "there is a problem with one of the leaves of the tree " 866 + "such as invoking a change request in an " 867 + "<i>XXX</i>Icon.xml. " 868 + "<p>The quickest way to find this is to restart vergil " 869 + "and expand each branch in the tree by hand."; 870 try { 871 ((PtolemyFrame) jFrame).expandAllLibraryRows(); 872 } catch (Throwable throwable) { 873 throw new IllegalActionException(tableau, throwable, 874 "Failed to expand library.\n" + errorMessage); 875 } 876 877 return _temporaryHTMLFile("expandLibrary", ".htm", errorMessage); 878 879 } 880 881 // Return the URL of the file that contains links to .xml files 882 private static URL _getDemoURL(String demosFileName) throws IOException { 883 // Open the completeDemos.htm file and read the contents into 884 // a String 885 if (demosFileName == null || demosFileName.length() == 0) { 886 demosFileName = "ptolemy/configs/doc/completeDemos.htm"; 887 } 888 889 URL url = null; 890 try { 891 url = ConfigurationApplication.specToURL(demosFileName); 892 } catch (Exception ex) { 893 System.out 894 .println("Warning: " + demosFileName + " not found: " + ex); 895 } 896 return url; 897 } 898 899 /** Open up a file, return a list of relative URLs that match a regexp. 900 * @param demosURL The URL of the file containing URLs. 901 * @param regexp The regular expression, for example ".*.xml$". 902 * @return a list of relative URLS 903 */ 904 private static List _getURLs(URL demosURL, String regexp) 905 throws IOException { 906 // Return relative URLS 907 return _getURLs(demosURL, regexp, false, 0); 908 } 909 910 /** Open up a file, return a list of relative or absolute URLs 911 * that match a regexp. 912 * @param demosURL The URL of the file containing URLs. 913 * @param regexp The regular expression, for example ".*.xml$". 914 * @param absoluteURLs True if we should return absolute URLs. 915 * @param depth Depth to recurse. Depth of 0 do not recurse. 916 * Recursing only makes sense if the regexp argument includes .htm* files: 917 * ".*(.htm|.html|.xml)" 918 * @return a list of Strings naming absolute or relative URLs. 919 */ 920 private static List _getURLs(URL demosURL, String regexp, 921 boolean absoluteURLs, int depth) throws IOException { 922 //System.out.println("HTMLAbout._getURLs(" + demosURL + ", " + regexp + ", " + absoluteURLs + ", " + depth); 923 StringBuffer demosBuffer = new StringBuffer(); 924 BufferedReader in = null; 925 String demosURLParent = demosURL.toString().substring(0, 926 demosURL.toString().lastIndexOf("/") + 1); 927 try { 928 in = new BufferedReader( 929 new InputStreamReader(demosURL.openStream())); 930 931 String inputLine; 932 933 while ((inputLine = in.readLine()) != null) { 934 demosBuffer.append(inputLine); 935 } 936 } catch (Exception ex) { 937 System.out.println( 938 "HTMLAbout: failed to open " + demosURL + "\n" + ex); 939 return new LinkedList(); 940 } finally { 941 if (in != null) { 942 in.close(); 943 } 944 } 945 946 // demos contains the contents of the html file that has 947 // links to the demos we are interested in. 948 String demos = demosBuffer.toString(); 949 950 // All the models we find go here. 951 List modelList = new LinkedList(); 952 953 // Loop through the html file that contains links to the demos 954 // and pull out all the links by looking for href=" and then 955 // for the closing " 956 int modelStartIndex = demos.indexOf("href=\""); 957 958 while (modelStartIndex != -1) { 959 int modelEndIndex = demos.indexOf("\"", modelStartIndex + 6); 960 961 if (modelEndIndex != -1) { 962 String modelLink = demos.substring(modelStartIndex + 6, 963 modelEndIndex); 964 965 if (!modelLink.startsWith("http://") 966 && modelLink.matches(regexp)) { 967 // If the link does not start with http://, but ends 968 // with .xml, then we add it to the list 969 String model = modelLink; 970 //System.out.println("HTMLAbout: modelLink: " + modelLink); 971 if (absoluteURLs) { 972 model = demosURLParent + modelLink; 973 Exception ex1 = null; 974 try { 975 model = new URI(demosURLParent + modelLink) 976 .normalize().getPath(); 977 // Under Windows, convert /C:/foo/bar to C:/foo/bar 978 model = new File(model).toString().replace('\\', 979 '/'); 980 } catch (URISyntaxException ex) { 981 ex1 = ex; 982 } catch (NullPointerException ex2) { 983 // model == null, probably a jar url in Webstart 984 } 985 if (model == null) { 986 try { 987 // Could be a jar url 988 model = ConfigurationApplication 989 .specToURL(modelLink).toString(); 990 } catch (Exception ex) { 991 if (modelLink.startsWith("/")) { 992 modelLink = modelLink.substring(1); 993 try { 994 model = ConfigurationApplication 995 .specToURL(modelLink) 996 .toString(); 997 } catch (Exception ex2) { 998 System.out.println("Failed to look up " 999 + demosURLParent + modelLink 1000 + " and " + modelLink + "\n" 1001 + ex2); 1002 } 1003 } else { 1004 String absoluteModelLink = demosURLParent 1005 + modelLink; 1006 try { 1007 model = ConfigurationApplication 1008 .specToURL(absoluteModelLink) 1009 .toString(); 1010 } catch (Exception ex3) { 1011 System.out.println("Failed to look up " 1012 + demosURLParent + modelLink 1013 + " and " + modelLink + " and " 1014 + absoluteModelLink + "\n" + ex1 1015 + "\n" + ex3); 1016 } 1017 1018 } 1019 } 1020 } 1021 } 1022 if (model != null) { 1023 URL modelURL = null; 1024 if (model.startsWith("jar:file:/")) { 1025 modelURL = new URL(model); 1026 //System.out.print("HTMLAbout._getURLs(): jar:file:/: " + model); 1027 } else { 1028 if (model.startsWith("file:/")) { 1029 model = model.substring("file:/".length()); 1030 } 1031 //System.out.print("HTMLAbout._getURLs(): file:/: " + model); 1032 modelURL = new File(model).toURI().toURL(); 1033 } 1034 //System.out.println(" " + modelURL); 1035 boolean sawModel = modelList.contains(model); 1036 if (!sawModel) { 1037 modelList.add(model); 1038 if (depth > 0 && model.matches(".*(.htm|.html)")) { 1039 modelList.addAll(_getURLs(modelURL, regexp, 1040 absoluteURLs, depth - 1)); 1041 } 1042 } 1043 } 1044 } 1045 } 1046 1047 modelStartIndex = demos.indexOf("href=\"", modelEndIndex); 1048 } 1049 1050 return modelList; 1051 } 1052 1053 // Return true if a configuration can be found 1054 // @param configurationName The name of the configuration, for 1055 // example "full" 1056 // @return True if ptolemy/configs/<i>configuration</i>/configuration.xml 1057 // can be found 1058 private static boolean _configurationExists(String configurationName) { 1059 boolean configurationExists = false; 1060 1061 try { 1062 String spec = "ptolemy/configs/" + configurationName 1063 + "/configuration.xml"; 1064 URL url = ClassUtilities.getResource(spec); 1065 if (url != null) { 1066 configurationExists = true; 1067 } 1068 } catch (Throwable throwable) { 1069 // Ignored, the configuration does not exist. 1070 } 1071 1072 return configurationExists; 1073 } 1074 1075 // Save a string in a temporary html file and return a URL to it. 1076 // @param prefix The prefix string to be used in generating the temporary 1077 // file name; must be at least three characters long. 1078 // @param suffix The suffix string to be used in generating the temporary 1079 // file name. 1080 // @param contents The contents of the temporary file 1081 // @return A URL pointing to a temporary file. 1082 /*private*/static URL _temporaryHTMLFile(String prefix, String suffix, 1083 String contents) throws IOException { 1084 // Generate a copyright page in a temporary file 1085 File temporaryFile = File.createTempFile(prefix, suffix); 1086 temporaryFile.deleteOnExit(); 1087 1088 FileWriter fileWriter = null; 1089 1090 try { 1091 fileWriter = new FileWriter(temporaryFile); 1092 fileWriter.write(contents, 0, contents.length()); 1093 } finally { 1094 if (fileWriter != null) { 1095 fileWriter.close(); 1096 } 1097 } 1098 1099 return temporaryFile.toURI().toURL(); 1100 } 1101 1102 /////////////////////////////////////////////////////////////////// 1103 //// private variables //// 1104 1105 private static List _demosURLs; 1106}