001/* A parser for DocML (Doc Markup Language). 002 003 Copyright (c) 2006-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 027 */ 028package ptolemy.vergil.actor; 029 030import java.io.BufferedReader; 031import java.io.File; 032import java.io.IOException; 033import java.io.InputStream; 034import java.io.InputStreamReader; 035import java.io.Reader; 036import java.io.StringReader; 037import java.net.URL; 038import java.util.Date; 039import java.util.HashMap; 040import java.util.Iterator; 041import java.util.LinkedList; 042import java.util.List; 043import java.util.Stack; 044 045import com.microstar.xml.HandlerBase; 046import com.microstar.xml.XmlException; 047import com.microstar.xml.XmlParser; 048 049import ptolemy.actor.TypedCompositeActor; 050import ptolemy.actor.gui.Configuration; 051import ptolemy.data.expr.Parameter; 052import ptolemy.kernel.Entity; 053import ptolemy.kernel.Port; 054import ptolemy.kernel.util.Attribute; 055import ptolemy.kernel.util.Instantiable; 056import ptolemy.kernel.util.NamedObj; 057import ptolemy.kernel.util.Settable; 058import ptolemy.kernel.util.StringAttribute; 059import ptolemy.util.FileUtilities; 060import ptolemy.vergil.basic.DocAttribute; 061 062/////////////////////////////////////////////////////////////////// 063//// DocManager 064 065/** 066 A manager for documentation for an associated Ptolemy II object. 067 The constructor specifies the associated Ptolemy II object, and 068 then various methods provide access to portions of the documentation. 069 For example, the getDescription() method returns a description of 070 the object. The getPortDoc() method returns a description of the 071 specified port. 072 <p> 073 The documentation is constructed by a multi-tiered method. 074 The first level of information is provided by an 075 attribute named DOC_ATTRIBUTE_NAME contained by the object. 076 The second level of information is provided by an XML file 077 in the same package as the class of the associated object. 078 The name of the XML file is "xDoc.xml", where x is the name 079 of the class of the object. 080 The third level of information is provided by an XML file 081 associated with the base class of the associated object. 082 The remaining levels are provided by searching up the 083 inheritance hierarchy. 084 When a method of the DocManager class is invoked to get 085 documentation information, this class looks first in the 086 first tier for the information. If the information is 087 not present in the first tier, then it looks in the second 088 tier, etc. If the information is not present in any tier, 089 then it returns a string indicating that there is no 090 information. Except for the first tier, the 091 documentation information is constructed 092 lazily, only when the methods to access the information 093 are invoked, and only if the first tier has not 094 provided the information. 095 <p> 096 If the information is found but is malformed, then 097 all information methods return a description of the error. 098 <p> 099 At all tiers, the documentation information is given in XML 100 with a specified DTD. 101 <p> 102 A doc file should be an XML file beginning with 103 <pre> 104 <?xml version="1.0" standalone="yes"?> 105 <!DOCTYPE doc PUBLIC "-//UC Berkeley//DTD DocML 1//EN" 106 "http://ptolemy.eecs.berkeley.edu/xml/dtd/DocML_1.dtd"> 107 </pre> 108 and should then have a top-level element of the form 109 <pre> 110 <doc name="<i>actorName</i>" class="<i>actorClass</i>"> 111 </pre> 112 The main description is text included within the description 113 element, as in 114 <pre> 115 <description> 116 <i>description here</i> 117 </description> 118 </pre> 119 The description can include HTML formatting, although any 120 < and > should be escaped and represented as &lt; 121 and &gt;. 122 <p> 123 Additional information can be provided in the author, version, 124 since, Pt.ProposedRating, and Pt.AcceptedRating elements. 125 These are, like the description, simple text that gets rendered 126 (and HTML formatted) in the documentation. 127 <p> 128 Documentation for ports and parameters is given using the 129 following forms: 130 <pre> 131 <port name="<i>portName</i>" 132 <i>documentation</i> 133 </port> 134 <property name="<i>parameterName</i>" 135 <i>documentation</i> 136 </property> 137 </pre> 138 The use of the "property" keyword matches MoML. 139 140 @author Edward A. Lee 141 @version $Id$ 142 @since Ptolemy II 5.2 143 @Pt.ProposedRating Red (eal) 144 @Pt.AcceptedRating Red (cxh) 145 */ 146public class DocManager extends HandlerBase { 147 148 /** Construct a manager to handle documentation for the specified target. 149 * @param configuration The configuration in which to look up the 150 * _docApplicationSpecializer and _applicationName parameters 151 * @param target The object to be documented. 152 */ 153 public DocManager(Configuration configuration, NamedObj target) { 154 super(); 155 _configuration = configuration; 156 _target = target; 157 _targetClass = target.getClass(); 158 _className = target.getClassName(); 159 //try { 160 List docAttributes = _target.attributeList(DocAttribute.class); 161 // Get the last doc attribute. 162 if (docAttributes.size() > 0) { 163 DocAttribute instanceDoc = (DocAttribute) docAttributes 164 .get(docAttributes.size() - 1); 165 // Populate fields from the attribute. 166 //String descriptionValue = instanceDoc.description.stringValue(); 167 String descriptionValue = instanceDoc.description.getExpression(); 168 if (descriptionValue != null 169 && !descriptionValue.trim().equals("")) { 170 _isInstanceDoc = true; 171 _description = descriptionValue; 172 } 173 /* No rating fields in instance documentation. 174 String acceptedRatingValue = instanceDoc.acceptedRating.getExpression(); 175 if (!acceptedRatingValue.trim().equals("")) { 176 _isInstanceDoc = true; 177 _ptAcceptedRating = acceptedRatingValue; 178 } 179 */ 180 String authorValue = instanceDoc.author.getExpression(); 181 if (authorValue != null && !authorValue.trim().equals("")) { 182 _isInstanceDoc = true; 183 _author = authorValue; 184 } 185 /* No rating fields in instance documentation. 186 String proposedRatingValue = instanceDoc.proposedRating.getExpression(); 187 if (!proposedRatingValue.trim().equals("")) { 188 _isInstanceDoc = true; 189 _ptProposedRating = proposedRatingValue; 190 } 191 */ 192 String sinceValue = instanceDoc.since.getExpression(); 193 if (sinceValue != null && !sinceValue.trim().equals("")) { 194 _isInstanceDoc = true; 195 _since = sinceValue; 196 } 197 String versionValue = instanceDoc.version.getExpression(); 198 if (versionValue != null && !versionValue.trim().equals("")) { 199 _isInstanceDoc = true; 200 _version = versionValue; 201 } 202 203 // Next look for attributes. 204 Iterator attributes = target.attributeList(Settable.class) 205 .iterator(); 206 while (attributes.hasNext()) { 207 NamedObj attribute = (NamedObj) attributes.next(); 208 if (((Settable) attribute).getVisibility() != Settable.NONE) { 209 String attributeDoc = instanceDoc 210 .getParameterDoc(attribute.getName()); 211 if (attributeDoc != null 212 && !attributeDoc.trim().equals("")) { 213 _isInstanceDoc = true; 214 _properties.put(attribute.getName(), attributeDoc); 215 } 216 } 217 } 218 // Next look for ports. 219 if (target instanceof Entity) { 220 Iterator ports = ((Entity) target).portList().iterator(); 221 while (ports.hasNext()) { 222 Port port = (Port) ports.next(); 223 String portDoc = instanceDoc.getPortDoc(port.getName()); 224 if (portDoc != null && !portDoc.trim().equals("")) { 225 _isInstanceDoc = true; 226 _ports.put(port.getName(), portDoc); 227 } 228 } 229 } 230 } 231 //} catch (IllegalActionException e) { 232 //_exception = "Error evaluating DocAttribute parameter:\n" + e + ptolemy.kernel.util.KernelException.stackTraceToString(e); 233 //} 234 } 235 236 /** Construct a manager to handle documentation for the specified target 237 * class. 238 * @param configuration The configuration in which to look up the 239 * _docApplicationSpecializer and _applicationName parameters 240 * @param targetClass The class to be documented. 241 */ 242 public DocManager(Configuration configuration, Class targetClass) { 243 super(); 244 _configuration = configuration; 245 _targetClass = targetClass; 246 _className = _targetClass.getName(); 247 } 248 249 /** Construct a manager for documentation at the specified URL. 250 * @param configuration The configuration in which to look up the 251 * _docApplicationSpecializer and _applicationName parameters 252 * @param url The URL. 253 */ 254 public DocManager(Configuration configuration, URL url) { 255 super(); 256 _configuration = configuration; 257 try { 258 parse(null, url.openStream()); 259 if (_className != null) { 260 _targetClass = Class.forName(_className); 261 } else { 262 System.err.println("DocManager: while reading " + url 263 + ", _className was null?"); 264 } 265 } catch (Exception ex) { 266 ex.printStackTrace(); 267 _exception = "Error reading URL: " + url.toExternalForm() 268 + "\n<pre>\n" + ex + "\n</pre>\n"; 269 } 270 _docFileHasBeenRead = true; 271 } 272 273 /////////////////////////////////////////////////////////////////// 274 //// public methods //// 275 276 /** Handle an attribute assignment that is part of an XML element. 277 * This method is called prior to the corresponding startElement() 278 * call, so it simply accumulates attributes in a hashtable for 279 * use by startElement(). 280 * @param name The name of the attribute. 281 * @param value The value of the attribute, or null if the attribute 282 * is <code>#IMPLIED</code> and not specified. 283 * @param specified True if the value is specified, false if the 284 * value comes from the default value in the DTD rather than from 285 * the XML file. 286 * @exception XmlException If the name or value is null. 287 */ 288 @Override 289 public void attribute(String name, String value, boolean specified) 290 throws XmlException { 291 if (name == null) { 292 throw new XmlException("Attribute has no name", 293 _currentExternalEntity(), _parser.getLineNumber(), 294 _parser.getColumnNumber()); 295 } 296 297 // NOTE: value may be null if attribute default is #IMPLIED. 298 if (value != null) { 299 _attributes.put(name, value); 300 } 301 } 302 303 /** Given a dot separated class name, return the URL of the 304 * documentation. 305 * 306 * <p>If the configuration has a parameter 307 * _docApplicationSpecializer and that parameter names a class 308 * that that implements the {@link DocApplicationSpecializer} 309 * interface, then we pass the class name to 310 * {@link DocApplicationSpecializer#docClassNameToURL(String, String, boolean, boolean, boolean, boolean)} 311 * and if a non-null is returned from docClassNameToURL(), we 312 * return that value. 313 * 314 * <p>If the configuration has a parameter _applicationName, then 315 * we search in <code>doc/codeDoc<i>applicationName</i></code> for 316 * the PtDoc, Javadoc and Actor Index files. Otherwise, we search 317 * in <code>doc/codeDoc</code>. Source files are searched for 318 * in the classpath by using getResource(). 319 * 320 * <p>The <i>lookForPtDoc</i>, <i>lookForJavadoc</i>, 321 * <i>lookForSource</i> and <i>lookForActorIndex</i> parameters 322 * control which documents are searched for. The documents are 323 * searched in the same order as the parameters that have true 324 * values, that is if the parameters are true, true, false, 325 * false, then if the the PtDoc .xml file is searched for 326 * locally, then the Javadoc .html file is search for locally and 327 * then if the _remoteDocumentation base attribute is set the 328 * PtDoc .xml file is searched for on the remote host and then 329 * the Javadoc .html file is search for on the remote host. 330 * 331 * @param configuration The configuration in which to look up the 332 * _docApplicationSpecializer and _applicationName parameters 333 * @param className The dot separated class name. 334 * @param lookForPtDoc True if we should look for ptdoc .xml files. 335 * @param lookForJavadoc True if we should look for javadoc files. 336 * @param lookForSource True if we should look for source files. 337 * Note that lookForPtDoc and lookForJavadoc must both be false for 338 * the source code to be found. 339 * @param lookForActorIndex True if we should look for the actor index. 340 * @return The URL of the documentation, if any. If no documentation 341 * was found, return null. 342 */ 343 public static URL docClassNameToURL(Configuration configuration, 344 String className, boolean lookForPtDoc, boolean lookForJavadoc, 345 boolean lookForSource, boolean lookForActorIndex) { 346 URL toRead = null; 347 try { 348 // If the configuration has a parameter _docApplicationSpecializer 349 // and that parameter names a class that that implements the 350 // DocApplicationSpecializer interface, then we call 351 // docClassNameToURL(). 352 353 Parameter docApplicationSpecializerParameter = (Parameter) configuration 354 .getAttribute("_docApplicationSpecializer", 355 Parameter.class); 356 if (docApplicationSpecializerParameter != null) { 357 String docApplicationSpecializerClassName = docApplicationSpecializerParameter 358 .getExpression(); 359 360 try { 361 Class docApplicationSpecializerClass = Class 362 .forName(docApplicationSpecializerClassName); 363 DocApplicationSpecializer docApplicationSpecializer = (DocApplicationSpecializer) docApplicationSpecializerClass 364 .newInstance(); 365 toRead = docApplicationSpecializer.docClassNameToURL( 366 _remoteDocumentationURLBase, className, 367 lookForPtDoc, lookForJavadoc, lookForSource, 368 lookForActorIndex); 369 } catch (Throwable throwable) { 370 throw new Exception( 371 "Failed to call doc application initializer " 372 + "class \"" 373 + docApplicationSpecializerClassName 374 + "\" on class \"" + className + "\"."); 375 } 376 } 377 378 String applicationName = ""; 379 380 // We handle the applicationName specially so that we open 381 // only the docs for the app we are running. 382 try { 383 StringAttribute applicationNameAttribute = (StringAttribute) configuration 384 .getAttribute("_applicationName", 385 StringAttribute.class); 386 387 if (applicationNameAttribute != null) { 388 applicationName = applicationNameAttribute.getExpression(); 389 } 390 } catch (Throwable throwable) { 391 // Ignore and use the default applicationName: "", 392 // which means we look in doc.codeDoc. 393 } 394 395 // We search first on the local machine and then ask 396 // the user and then possibly look on the remote machine. 397 // So, we define the strings in an array for ease of reuse. 398 399 String docNames[] = { 400 "doc/codeDoc" 401 + (applicationName.equals("") ? "/" 402 : applicationName + "/doc/codeDoc/") 403 + className.replace('.', '/') + ".xml", 404 405 "doc/codeDoc/" + className.replace('.', '/') + ".xml", 406 407 "doc/codeDoc" 408 + (applicationName.equals("") ? "/" 409 : applicationName + "/doc/codeDoc/") 410 + className.replace('.', '/') + ".html", 411 412 "doc/codeDoc/" + className.replace('.', '/') + ".html", 413 414 className.replace('.', '/') + ".java", 415 416 "doc/codeDoc" 417 + (applicationName.equals("") ? "/" 418 : applicationName + "/doc/codeDoc/") 419 420 + className.replace('.', '/') + "Idx.htm" }; 421 422 // List of docNames we use if we don't find anything locally. 423 List docNameList = new LinkedList(); 424 425 // We look for the documentation relative to this classLoader.n 426 ClassLoader referenceClassLoader = Class 427 .forName("ptolemy.vergil.actor.DocManager") 428 .getClassLoader(); 429 430 // Rather than using a deeply nested set of if/else's, we 431 // just keep checking toRead == null.p 432 433 // If applicationName is not "", then look in 434 // doc/codeDoc_applicationName/doc/codeDoc. 435 if (toRead == null && lookForPtDoc) { 436 docNameList.add(docNames[0]); 437 toRead = referenceClassLoader.getResource(docNames[0]); 438 } 439 440 if (toRead == null && lookForPtDoc && !applicationName.equals("")) { 441 // applicationName was set, try looking in the 442 // documentation for the default application (vergil). 443 docNameList.add(docNames[1]); 444 toRead = referenceClassLoader.getResource(docNames[1]); 445 } 446 447 if (toRead == null && lookForJavadoc) { 448 // If the class does not extend NamedObj, try to open 449 // the javadoc .html 450 451 //If we are searching for documentation and the ptdoc 452 //.xml file is not present, but the javadoc .html file 453 //is present, then display the .html file. This is 454 //necessary to support links to classes that are not 455 //present in the models because we only generate ptdoc 456 //.xml files for classes that are in the models. 457 458 try { 459 460 Class.forName(className); 461 462 //if (!_namedObjClass.isAssignableFrom(targetClass) 463 //|| !lookForPtDoc) { 464 465 // Look in the Application specific codeDoc directory. 466 docNameList.add(docNames[2]); 467 toRead = referenceClassLoader.getResource(docNames[2]); 468 if (toRead == null) { 469 // Try looking in the documentation for vergil. 470 docNameList.add(docNames[3]); 471 toRead = referenceClassLoader.getResource(docNames[3]); 472 } 473 //} 474 } catch (ClassNotFoundException ex) { 475 // Ignore, we could have the Sinewave Actor oriented class. 476 } 477 } 478 479 if (toRead == null && lookForSource && !lookForPtDoc 480 && !lookForJavadoc) { 481 // Look for the source _ONLY_ if we are not looking for 482 // ptdoc or javadoc. 483 docNameList.add(docNames[4]); 484 toRead = referenceClassLoader.getResource(docNames[4]); 485 } 486 487 if (toRead == null && lookForActorIndex) { 488 // Look for the list of demos 489 if (!className.equals("org.terraswarm.accessor.JSAccessor")) { 490 docNameList.add(docNames[5]); 491 // } else { 492 // // Get the script parameter and look for an adjacent *Idx.htm file. 493 // try { 494 // Parameter accessorSourceParameter = (Parameter) _target.getProperty("accessorSource"); 495 // if (accessorSourceParameter != null) { 496 // String acccessorSource = accessorSourceParameter.getExpression(); 497 // docNameList.add(accessorSource.substring(0, accessorSource.length() - 3 + "Idx.html")); 498 // } 499 // } catch (Throwable throwable) { 500 // docNameList.add(docNames[5]); 501 // } 502 } 503 504 toRead = referenceClassLoader.getResource(docNames[5]); 505 } 506 507 if (toRead == null && _remoteDocumentationURLBase != null) { 508 // Try searching on a remote host. 509 // Loop through each docNamesIterator and try to open 510 // a stream. Stop if once we open a stream. 511 Iterator docNameIterator = docNameList.iterator(); 512 while (docNameIterator.hasNext()) { 513 String docName = (String) docNameIterator.next(); 514 // Handle redirects for http -> https 515 toRead = FileUtilities.followRedirects( 516 new URL(_remoteDocumentationURLBase + docName)); 517 518 if (toRead != null) { 519 InputStream toReadStream = null; 520 try { 521 // In an Exception, this may throw a SecurityException. 522 toReadStream = toRead.openStream(); 523 } catch (Exception ex) { 524 toRead = null; 525 } finally { 526 if (toReadStream != null) { 527 try { 528 toReadStream.close(); 529 } catch (IOException ex2) { 530 // Ignore. 531 } 532 } 533 } 534 if (toRead != null) { 535 break; 536 } 537 } 538 } 539 } 540 } catch (Exception ex) { 541 // Ignore, we did not find the class. 542 ex.printStackTrace(); 543 return null; 544 } 545 return toRead; 546 } 547 548 /** Handle character data. In this implementation, the 549 * character data is accumulated in a buffer until the 550 * end element. 551 * Ælfred will call this method once for each chunk of 552 * character data found in the contents of elements. Note that 553 * the parser may break up a long sequence of characters into 554 * smaller chunks and call this method once for each chunk. 555 * @param chars The character data. 556 * @param offset The starting position in the array. 557 * @param length The number of characters available. 558 */ 559 @Override 560 public void charData(char[] chars, int offset, int length) { 561 _currentCharData.append(chars, offset, length); 562 } 563 564 /** End the document. In this implementation, do nothing. 565 * Ælfred will call this method once, when it has 566 * finished parsing the XML document. 567 * It is guaranteed that this will be the last method called. 568 */ 569 @Override 570 public void endDocument() throws Exception { 571 } 572 573 /** End an element. 574 * Ælfred will call this method at the end of each element 575 * (including EMPTY elements). 576 * @param elementName The element type name. 577 * @exception Exception Not thrown in this base class. 578 */ 579 @Override 580 public void endElement(String elementName) throws Exception { 581 if (elementName.equals("author")) { 582 _author = _currentCharData.toString(); 583 } else if (elementName.equals("description")) { 584 _description = _currentCharData.toString(); 585 } else if (elementName.equals("port")) { 586 _ports.put(_name, _currentCharData.toString()); 587 } else if (elementName.equals("property")) { 588 _properties.put(_name, _currentCharData.toString()); 589 } else if (elementName.equals("Pt.AcceptedRating")) { 590 _ptAcceptedRating = _currentCharData.toString(); 591 } else if (elementName.equals("Pt.ProposedRating")) { 592 _ptProposedRating = _currentCharData.toString(); 593 } else if (elementName.equals("since")) { 594 _since = _currentCharData.toString(); 595 } else if (elementName.equals("version")) { 596 _version = _currentCharData.toString(); 597 } 598 } 599 600 /** Indicate a fatal XML parsing error. 601 * Ælfred will call this method whenever it encounters 602 * a serious error. This method simply throws an XmlException. 603 * @param message The error message. 604 * @param systemID The URI of the entity that caused the error. 605 * @param line The approximate line number of the error. 606 * @param column The approximate column number of the error. 607 * @exception XmlException If called. 608 */ 609 @Override 610 public void error(String message, String systemID, int line, int column) 611 throws XmlException { 612 throw new XmlException(message, _currentExternalEntity(), line, column); 613 } 614 615 /** Return the Pt.AcceptedRating field, or null 616 * if none has been given. Note that unlike some of the other 617 * fields, this does not delegate to the next tier if no 618 * since field has been given. 619 * @return The Pt.AcceptedRating field. 620 */ 621 public String getAcceptedRating() { 622 if (_ptAcceptedRating == null) { 623 _readDocFile(); 624 } 625 return _ptAcceptedRating; 626 } 627 628 /** Return the author field, or the string "No author given" 629 * if none has been given. Note that unlike some of the other 630 * fields, this does not delegate to the next tier if no 631 * author has been given. 632 * @return The author field. 633 */ 634 public String getAuthor() { 635 if (_author == null) { 636 _readDocFile(); 637 if (_author == null) { 638 return "No author given"; 639 } 640 } 641 return _author; 642 } 643 644 /** Return the class name, or null if none has been given. 645 * @return The class name. 646 */ 647 public String getClassName() { 648 if (_className == null && _exception != null) { 649 return _exception; 650 } 651 return _className; 652 } 653 654 /** Return the description, or null if none has been given. 655 * @return The description. 656 */ 657 public String getDescription() { 658 if (_exception != null) { 659 return _exception; 660 } else if (_description == null) { 661 _readDocFile(); 662 if (_description == null) { 663 _createNextTier(); 664 if (_nextTier != null) { 665 return _nextTier.getDescription(); 666 } else { 667 return "No description"; 668 } 669 } 670 } 671 return _description; 672 } 673 674 /** Return next tier, if there is one. 675 * If this is an instance, then the next tier 676 * is the documentation for the class. If it is a 677 * class, then the next tier is the documentation for 678 * the superclass. 679 * @return The next tier, or null if there isn't one. 680 */ 681 public DocManager getNextTier() { 682 _createNextTier(); 683 return _nextTier; 684 } 685 686 /** Return the documentation for the specified port, or null 687 * if there is none. 688 * @param name The name of the port. 689 * @return The documentation for the specified port, or null 690 * if there is none. 691 */ 692 public String getPortDoc(String name) { 693 _readDocFile(); 694 String result = (String) _ports.get(name); 695 if (result == null) { 696 result = (String) _ports.get(name + " (port)"); 697 if (result == null) { 698 _createNextTier(); 699 if (_nextTier != null) { 700 return _nextTier.getPortDoc(name); 701 } 702 } 703 } 704 return result; 705 } 706 707 /** Return the documentation for the specified property 708 * (parameter or attribute), or null if there is none. 709 * @param name The name of the property. 710 * @return The documentation for the specified property, or null 711 * if there is none. 712 */ 713 public String getPropertyDoc(String name) { 714 _readDocFile(); 715 String result = (String) _properties.get(name); 716 if (result == null) { 717 result = (String) _properties.get(name + " (parameter)"); 718 if (result == null) { 719 _createNextTier(); 720 if (_nextTier != null) { 721 return _nextTier.getPropertyDoc(name); 722 } 723 } 724 } 725 return result; 726 } 727 728 /** Return the Pt.ProposedRating field, or null 729 * if none has been given. Note that unlike some of the other 730 * fields, this does not delegate to the next tier if no 731 * since field has been given. 732 * @return The Pt.ProposedRating field. 733 */ 734 public String getProposedRating() { 735 if (_ptProposedRating == null) { 736 _readDocFile(); 737 } 738 return _ptProposedRating; 739 } 740 741 /** Return the since field, or null 742 * if none has been given. Note that unlike some of the other 743 * fields, this does not delegate to the next tier if no 744 * since field has been given. 745 * @return The since field. 746 */ 747 public String getSince() { 748 if (_since == null) { 749 _readDocFile(); 750 } 751 return _since; 752 } 753 754 /** Get the location of the website documentation. 755 * The website documentation is set by the 756 * _remoteDocumentationURLBase attribute in the configuration. 757 * That attribute, if present, should be a parameter that whose 758 * value is a string that represents the URL where the 759 * documentation may be found. If the 760 * _remoteDocumentationURLBase attribute is not set, then the 761 * location of the website documentation defaults to 762 * <code>http://ptolemy.eecs.berkeley.edu/ptolemyII/ptII/<i>Major.Version</i></code>, 763 * where <code><i>Major.Version</i></code> is the value returned by 764 * {@link 765 * ptolemy.kernel.attributes.VersionAttribute#majorCurrentVersion()}. 766 * @return The URL location of the website documentation. 767 * @see #setRemoteDocumentationURLBase(String) 768 */ 769 public static String getRemoteDocumentationURLBase() { 770 return _remoteDocumentationURLBase; 771 } 772 773 /** Return the class of the target. 774 * @return The class of the target. 775 */ 776 public Class getTargetClass() { 777 return _targetClass; 778 } 779 780 /** Return the version field, or null 781 * if none has been given. Note that unlike some of the other 782 * fields, this does not delegate to the next tier if no 783 * version has been given. If the version field is the standard 784 * CVS version, then return only the version number and date. 785 * @return The version field. 786 */ 787 public String getVersion() { 788 if (_version == null) { 789 _readDocFile(); 790 } 791 if (_version != null) { 792 if (_version.startsWith("$Id:")) { 793 // Standard CVS version. Extract the version number and date. 794 int index = _version.indexOf(",v "); 795 if (index > 4) { 796 String tail = _version.substring(index + 3); 797 // Find the first space after the start. 798 index = tail.indexOf(" "); 799 if (index > 0) { 800 // Find the second space. 801 index = tail.indexOf(" ", index + 1); 802 if (index > 0) { 803 _version = tail.substring(0, index); 804 } 805 } 806 } 807 } 808 } 809 return _version; 810 } 811 812 /** Return "see also" information. This includes a link 813 * to the javadoc documentation, the source code, and the 814 * superclass information. 815 * @return The "see also" information. 816 */ 817 public String getSeeAlso() { 818 StringBuffer result = new StringBuffer(); 819 820 // See whether there is Javadoc, and link to it if there is. 821 result.append("<b>See Also:</b><ul>\n"); 822 823 // The class name is either the name of the target class 824 // or the class name provided by the target. 825 String className; 826 if (_target == null) { 827 if (_targetClass == null) { 828 throw new NullPointerException( 829 "Both _target and _targetClass are null?"); 830 } 831 className = _targetClass.getName(); 832 } else { 833 className = _target.getClassName(); 834 } 835 if (_isInstanceDoc) { 836 // Create a link to the class documentation, 837 // if there is some. First not that the superclass 838 // may itself be an instance with instance documentation. 839 // In that case, the hyperlink is special and must be 840 // intercepted by the DocViewer class. 841 if (_target instanceof Instantiable 842 && ((Instantiable) _target).getParent() != null 843 && ((NamedObj) ((Instantiable) _target).getParent()) 844 .attributeList(DocAttribute.class).size() > 0) { 845 result.append( 846 "<li><a href=\"#parentClass\">Class documentation</a></li>"); 847 } 848 // Get either the PtDoc, javadoc, or source. 849 URL toRead = docClassNameToURL(_configuration, className, true, 850 true, true, false); 851 if (toRead != null) { 852 result.append("<li><a href=\"" + toRead.toExternalForm()); 853 if (toRead.toExternalForm().endsWith(".html")) { 854 // Sadly, Javadoc from Java 1.7 cannot be 855 // displayed using a JEditorPane, so we open 856 // javadoc in an external browser. To test this 857 // out, see 858 // http://docs.oracle.com/javase/tutorial/uiswing/components/editorpane.html#editorpane 859 // and modify the example so that it tries to view 860 // the Javadoc for Object. 861 result.append("#in_browser\">Javadoc Documentation"); 862 } else if (toRead.toExternalForm().endsWith(".java")) { 863 result.append("\">Java Source"); 864 } else if (toRead.toExternalForm().endsWith(".xml")) { 865 result.append("\">Class Documentation"); 866 } 867 result.append("</a></li>"); 868 } 869 } else { 870 URL docURL = null; 871 try { 872 // Get the javadoc 873 URL toRead = docClassNameToURL(_configuration, className, false, 874 true, false, false); 875 876 if (toRead != null) { 877 docURL = toRead; 878 result.append("<li><a href=\"" + toRead.toExternalForm() 879 // Sadly, Javadoc from Java 1.7 cannot be 880 // displayed using a JEditorPane, so we open 881 // javadoc in an external browser. To test this 882 // out, see 883 // http://docs.oracle.com/javase/tutorial/uiswing/components/editorpane.html#editorpane 884 // and modify the example so that it tries to view 885 // the Javadoc for Object. 886 + "#in_browser\">Javadoc Documentation</a></li>"); 887 } else { 888 // FIXME: Make this a hyperlink to a doc on how 889 // to create the javadocs. 890 result.append("<li>No javadocs found</li>"); 891 } 892 } catch (Exception ex) { 893 result.append("<li>Error opening javadoc file:\n<pre>" + ex 894 + "/n</pre></li>\n"); 895 } 896 897 // See whether the base class has a doc file, and if so, 898 // link to it. If not, try to link to the Javadoc for the 899 // base class. 900 try { 901 String baseClassName = _targetClass.getSuperclass().getName(); 902 URL toRead = docClassNameToURL(_configuration, baseClassName, 903 true, true, true, false); 904 905 // Display only the unqualified class name for compactness. 906 int lastDot = baseClassName.lastIndexOf("."); 907 if (lastDot >= 0) { 908 baseClassName = baseClassName.substring(lastDot + 1); 909 } 910 if (toRead != null 911 && toRead.toExternalForm().endsWith(".xml")) { 912 result.append("<li><a href=\"" + toRead.toExternalForm() 913 + "\">Base class (" + baseClassName + ")</a></li>"); 914 } else if (toRead != null 915 && toRead.toExternalForm().endsWith(".html")) { 916 result.append("<li><a href=\"" + toRead.toExternalForm() 917 + "#in_browser\">Base class Javadoc (" 918 + baseClassName + ")</a></li>"); 919 } else if (toRead != null 920 && toRead.toExternalForm().endsWith(".java")) { 921 result.append("<li><a href=\"" + toRead.toExternalForm() 922 + "\">Base class Java (" + baseClassName 923 + ")</a></li>"); 924 } 925 } catch (Exception ex) { 926 result.append("<li>Error opening javadoc file:\n<pre>" + ex 927 + "/n</pre></li>\n"); 928 } 929 930 // Link to the source code, if present. 931 try { 932 URL toRead = docClassNameToURL(_configuration, className, false, 933 false, true, false); 934 if (toRead != null) { 935 String modificationMessage = ""; 936 try { 937 if (toRead.toExternalForm().startsWith("file:/") 938 && docURL.toExternalForm() 939 .startsWith("file:/")) { 940 // Check the mod times and print a message if the doc file 941 // it out of date. 942 File sourceFile = new File(toRead.getFile()); 943 File docFile = new File(docURL.getFile()); 944 if (sourceFile.lastModified() > docFile 945 .lastModified()) { 946 modificationMessage = "<font color=\"red\">Documentation " 947 + "may be out of date when compared to source.</font> " 948 + "<br/>The source was last modified on <br/>" 949 + new Date(sourceFile.lastModified()) 950 + ",<br/> documentation was last modified on <br/>" 951 + new Date(docFile.lastModified()) 952 + ".<br/> To rebuild the documentation use the " 953 + "Build menu choice."; 954 } 955 } 956 } catch (Exception ex) { 957 // Ignore 958 } 959 result.append("<li><a href=\"" + toRead.toExternalForm() 960 + "\">Source code</a>" + modificationMessage 961 + "</li>"); 962 } 963 } catch (Exception ex) { 964 // Do not report anything. 965 } 966 } 967 968 // FIXME: Need see also fields from the doclet analysis. 969 // FIXME: Include demos? How? 970 971 try { 972 URL toRead = docClassNameToURL(_configuration, className, false, 973 false, false, true); 974 System.out.println("DocManager: Trying to find demos for " 975 + className + ", _target: " + _target + " _targetClass: " 976 + _targetClass + ": toRead: " + toRead); 977 if (toRead != null) { 978 result.append("<li><a href=\"" + toRead.toExternalForm() 979 + "\">Demo Usage</a></li>"); 980 } else { 981 result.append("<li>Not found in any demos</li>"); 982 } 983 } catch (Exception ex) { 984 // Do not report anything. 985 986 } 987 result.append("</ul>"); 988 return result.toString(); 989 } 990 991 /** Return true if an exception was encountered parsing 992 * the DocML data. 993 * @return True if an exception was encountered. 994 */ 995 public boolean hadException() { 996 return _exception != null; 997 } 998 999 /** Return true if the primary source of documentation is 1000 * the instance. That is, return true if the target has 1001 * an instance of DocAttribute in it, and at least one 1002 * of the fields of the DocAttribute is not empty. 1003 * @return True if this documents an instance (vs. a class). 1004 */ 1005 public boolean isInstanceDoc() { 1006 return _isInstanceDoc; 1007 } 1008 1009 /** Return true if the target class is a subclass of Attribute 1010 * that has a two-argument constructor compatible where the 1011 * first argument is a CompositeEntity and the second is a 1012 * String. This will return true if the target is itself 1013 * an instance of Attribute or a subclass. 1014 * @return True if the target is an instantiable attribute. 1015 */ 1016 public boolean isTargetInstantiableAttribute() { 1017 if (_target != null) { 1018 return _target instanceof Attribute; 1019 } else { 1020 Class targetClass = _targetClass; 1021 while (targetClass != null) { 1022 if (targetClass.equals(Attribute.class)) { 1023 return _hasMoMLConstructor(); 1024 } 1025 targetClass = targetClass.getSuperclass(); 1026 } 1027 return false; 1028 } 1029 } 1030 1031 /** Return true if the target class is a subclass of Entity 1032 * that has a two-argument constructor compatible where the 1033 * first argument is a CompositeEntity and the second is a 1034 * String. This will return true if the target is itself 1035 * an instance of Entity or a subclass. 1036 * @return True if the target is an instantiable entity. 1037 */ 1038 public boolean isTargetInstantiableEntity() { 1039 if (_target != null) { 1040 return _target instanceof Entity; 1041 } else { 1042 Class targetClass = _targetClass; 1043 while (targetClass != null) { 1044 if (targetClass.equals(Entity.class)) { 1045 return _hasMoMLConstructor(); 1046 } 1047 targetClass = targetClass.getSuperclass(); 1048 } 1049 return false; 1050 } 1051 } 1052 1053 /** Return true if the target class is a subclass of Port 1054 * that has a two-argument constructor compatible where the 1055 * first argument is a CompositeEntity and the second is a 1056 * String. This will return true if the target is itself 1057 * an instance of Port or a subclass. 1058 * @return True if the target is an instantiable port. 1059 */ 1060 public boolean isTargetInstantiablePort() { 1061 if (_target != null) { 1062 return _target instanceof Port; 1063 } else { 1064 Class targetClass = _targetClass; 1065 while (targetClass != null) { 1066 if (targetClass.equals(Port.class)) { 1067 return _hasMoMLConstructor(); 1068 } 1069 targetClass = targetClass.getSuperclass(); 1070 } 1071 return false; 1072 } 1073 } 1074 1075 /** Parse the given stream as a DocML file. 1076 * For example, a user might use this method as follows: 1077 * <pre> 1078 * DocManager parser = new DocManager(); 1079 * URL xmlFile = new URL(null, docURL); 1080 * parser.parse(xmlFile.openStream()); 1081 * </pre> 1082 * A variety of exceptions might be thrown if the parsed 1083 * data does not represent a valid DocML file. 1084 * @param base The base URL from which the XML is read. 1085 * @param input The stream from which to read XML. 1086 * @exception Exception If the parser fails. 1087 */ 1088 public void parse(URL base, InputStream input) throws Exception { 1089 parse(base, new InputStreamReader(input, 1090 java.nio.charset.Charset.defaultCharset())); 1091 } 1092 1093 /** Parse the given stream as a DocML file. 1094 * A variety of exceptions might be thrown if the parsed 1095 * data does not represent a valid DocML file. 1096 * @param base The base URL from which the XML is read. 1097 * @param reader The stream from which to read XML. 1098 * @exception Exception If the parser fails. 1099 */ 1100 public void parse(URL base, Reader reader) throws Exception { 1101 _parser.setHandler(this); 1102 1103 Reader buffered = new BufferedReader(reader); 1104 1105 if (base == null) { 1106 _parser.parse(null, null, buffered); 1107 } else { 1108 _parser.parse(base.toExternalForm(), null, buffered); 1109 } 1110 } 1111 1112 /** Parse the given text as DocML. 1113 * A variety of exceptions might be thrown if the parsed 1114 * data does not represent valid DocML data. 1115 * @param text The DocML data. 1116 * @exception Exception If the parser fails. 1117 */ 1118 public void parse(String text) throws Exception { 1119 parse(null, new StringReader(text)); 1120 } 1121 1122 /** Resolve an external entity. If the first argument is the 1123 * name of the DocML PUBLIC DTD ("-//UC Berkeley//DTD DocML 1//EN"), 1124 * then return a StringReader 1125 * that will read the locally cached version of this DTD 1126 * (the public variable DocML_DTD_1). Otherwise, return null, 1127 * which has the effect of deferring to Ælfred for 1128 * resolution of the URI. Derived classes may return a 1129 * a modified URI (a string), an InputStream, or a Reader. 1130 * In the latter two cases, the input character stream is 1131 * provided. 1132 * @param publicID The public identifier, or null if none was supplied. 1133 * @param systemID The system identifier. 1134 * @return Null, indicating to use the default system identifier. 1135 */ 1136 @Override 1137 public Object resolveEntity(String publicID, String systemID) { 1138 if (publicID != null 1139 && publicID.equals("-//UC Berkeley//DTD DocML 1//EN")) { 1140 // This is the generic DocML DTD. 1141 return new StringReader(DocML_DTD_1); 1142 } else { 1143 return null; 1144 } 1145 } 1146 1147 /** Set the location of the remote documentation. 1148 * @param remoteDocumentationURLBase The remote location of the class 1149 * documentation. 1150 * @see #getRemoteDocumentationURLBase() 1151 */ 1152 public static void setRemoteDocumentationURLBase( 1153 String remoteDocumentationURLBase) { 1154 System.out.println("DocManager.setRemoteDocumentationURLBase: " 1155 + remoteDocumentationURLBase); 1156 _remoteDocumentationURLBase = remoteDocumentationURLBase; 1157 } 1158 1159 /** Start a document. This method is called just before the parser 1160 * attempts to read the first entity (the root of the document). 1161 * It is guaranteed that this will be the first method called. 1162 */ 1163 @Override 1164 public void startDocument() { 1165 _attributes = new HashMap(); 1166 } 1167 1168 /** Start an element. 1169 * This is called at the beginning of each XML 1170 * element. By the time it is called, all of the attributes 1171 * for the element will already have been reported using the 1172 * attribute() method. Unrecognized elements are ignored. 1173 * @param elementName The element type name. 1174 * @exception XmlException If the element produces an error 1175 * in constructing the model. 1176 */ 1177 @Override 1178 public void startElement(String elementName) throws XmlException { 1179 try { 1180 // NOTE: The elements are alphabetical below... 1181 if (elementName.equals("author") 1182 || elementName.equals("description") 1183 || elementName.equals("Pt.AcceptedRating") 1184 || elementName.equals("Pt.ProposedRating") 1185 || elementName.equals("since") 1186 || elementName.equals("version")) { 1187 _currentCharData = new StringBuffer(); 1188 } else if (elementName.equals("doc")) { 1189 _className = (String) _attributes.get("class"); 1190 _checkForNull(_className, 1191 "No class argument for element \"doc\""); 1192 Class specifiedClass = Class.forName(_className); 1193 if (_targetClass != null && _targetClass != specifiedClass) { 1194 throw new Exception("Classes don't match: " + _targetClass 1195 + "\n and \n" + specifiedClass); 1196 } 1197 _targetClass = specifiedClass; 1198 } else if (elementName.equals("port")) { 1199 _currentCharData = new StringBuffer(); 1200 _name = (String) _attributes.get("name"); 1201 _checkForNull(_name, "No name argument for element \"port\""); 1202 } else if (elementName.equals("property")) { 1203 _currentCharData = new StringBuffer(); 1204 _name = (String) _attributes.get("name"); 1205 _checkForNull(_name, 1206 "No name argument for element \"property\""); 1207 } 1208 } catch (Exception ex) { 1209 if (ex instanceof XmlException) { 1210 throw (XmlException) ex; 1211 } else { 1212 String msg = "XML element \"" + elementName 1213 + "\" triggers exception:\n " + ex.toString(); 1214 throw new XmlException(msg, _currentExternalEntity(), 1215 _parser.getLineNumber(), _parser.getColumnNumber()); 1216 } 1217 } 1218 _attributes.clear(); 1219 } 1220 1221 /** Handle the start of an external entity. This pushes the stack so 1222 * that error reporting correctly reports the external entity that 1223 * causes the error. 1224 * @param systemID The URI for the external entity. 1225 */ 1226 @Override 1227 public void startExternalEntity(String systemID) { 1228 _externalEntities.push(systemID); 1229 } 1230 1231 /////////////////////////////////////////////////////////////////// 1232 //// public members //// 1233 1234 /** The standard DocML DTD, represented as a string. This is used 1235 * to parse DocML data when a compatible PUBLIC DTD is specified. 1236 */ 1237 public static final String DocML_DTD_1 = "<!ELEMENT doc (author | description | port | property | Pt.AcceptedRating | Pt.ProposedRating | since | version)*><!ATTLIST doc name CDATA #REQUIRED class CDATA #REQUIRED><!ELEMENT author (#PCDATA)><!ELEMENT description (#PCDATA)><!ELEMENT port (#PCDATA)><!ATTLIST port name CDATA #REQUIRED><!ELEMENT property (#PCDATA)><!ATTLIST property name CDATA #REQUIRED><!ELEMENT Pt.acceptedRating (#PCDATA)><!ELEMENT Pt.proposedRating (#PCDATA)><!ELEMENT since (#PCDATA)><!ELEMENT version (#PCDATA)>"; 1238 1239 // NOTE: The master file for the above DTD is at 1240 // $PTII/ptolemy/vergil/basic/DocML_1.dtd. If modified, it needs to be also 1241 // updated at http://ptolemy.eecs.berkeley.edu/xml/dtd/MoML_1.dtd 1242 1243 /////////////////////////////////////////////////////////////////// 1244 //// protected methods //// 1245 1246 /** If the argument is null, throw an exception with the given message. 1247 * @param object The reference to check for null. 1248 * @param message The message to issue if the reference is null. 1249 * @exception XmlException If the object parameter is null. 1250 */ 1251 protected void _checkForNull(Object object, String message) 1252 throws XmlException { 1253 if (object == null) { 1254 throw new XmlException(message, _currentExternalEntity(), 1255 _parser.getLineNumber(), _parser.getColumnNumber()); 1256 } 1257 } 1258 1259 /** Get the the URI for the current external entity. 1260 * @return A string giving the URI of the external entity being read, 1261 * or null if none. 1262 */ 1263 protected String _currentExternalEntity() { 1264 return (String) _externalEntities.peek(); 1265 } 1266 1267 /////////////////////////////////////////////////////////////////// 1268 //// private methods //// 1269 1270 /** Create next tier, if possible. */ 1271 private void _createNextTier() { 1272 if (_nextTier != null) { 1273 return; 1274 } 1275 if (_isInstanceDoc) { 1276 _nextTier = new DocManager(_configuration, _targetClass); 1277 } else { 1278 Class superClass = _targetClass.getSuperclass(); 1279 if (_isNamedObj(superClass)) { 1280 _nextTier = new DocManager(_configuration, superClass); 1281 } 1282 } 1283 } 1284 1285 /** Return true if the target class has a two argument 1286 * constructor compatible with MoML instantiation. 1287 * @return True if the target class can be instantiated 1288 * by MoML in a CompositeEntity. 1289 */ 1290 private boolean _hasMoMLConstructor() { 1291 // Check for a suitable constructor. 1292 Class[] parameters = { TypedCompositeActor.class, String.class }; 1293 while (parameters[0] != null) { 1294 try { 1295 _targetClass.getConstructor(parameters); 1296 // If we get here, then there is such a constructor. 1297 return true; 1298 } catch (Exception e) { 1299 // Ignore and try the superclass. 1300 } 1301 if (parameters[0].equals(NamedObj.class)) { 1302 break; 1303 } 1304 parameters[0] = parameters[0].getSuperclass(); 1305 } 1306 return false; 1307 } 1308 1309 /** Initialize fields. We assume _target was set by the caller 1310 1311 private void _init() { 1312 1313 _isInstanceDoc = false; 1314 } 1315 */ 1316 1317 /** Return true if the specified class is either equal to 1318 * NamedObj or is a subclass of NamedObj. 1319 */ 1320 private boolean _isNamedObj(Class candidate) { 1321 if (candidate == NamedObj.class) { 1322 return true; 1323 } else { 1324 candidate = candidate.getSuperclass(); 1325 if (candidate == null) { 1326 return false; 1327 } 1328 return _isNamedObj(candidate); 1329 } 1330 } 1331 1332 /** Read the doc file, if one is found, for the target. */ 1333 private void _readDocFile() { 1334 if (_docFileHasBeenRead || _isInstanceDoc) { 1335 return; 1336 } 1337 // FIXME: If file is not found, then instead of an 1338 // exception, probably want to delegate to the base class. 1339 // Read the .xml file, but not the java or source 1340 URL toRead = docClassNameToURL(_configuration, _className, true, false, 1341 false, false); 1342 1343 try { 1344 if (toRead != null) { 1345 parse(null, toRead.openStream()); 1346 } 1347 } catch (Exception ex) { 1348 ex.printStackTrace(); 1349 _exception = "Error reading URL: " + toRead.toExternalForm() 1350 + "\n<pre>\n" + ex + "\n</pre>\n"; 1351 } 1352 _docFileHasBeenRead = true; 1353 } 1354 1355 /////////////////////////////////////////////////////////////////// 1356 //// private members //// 1357 1358 /** Attributes associated with an entity. */ 1359 private HashMap _attributes; 1360 1361 /** The author field. */ 1362 private String _author; 1363 1364 /** The class name. */ 1365 private String _className; 1366 1367 /** The configuration in which to look up the 1368 * _docApplicationSpecializer and _applicationName parameters. 1369 */ 1370 private Configuration _configuration; 1371 1372 /** The current character data for the current element. */ 1373 private StringBuffer _currentCharData = new StringBuffer(); 1374 1375 /** The description field. */ 1376 private String _description; 1377 1378 /** Indicator that the doc file has been read. */ 1379 private boolean _docFileHasBeenRead = false; 1380 1381 /** If an exception is encountered parsing, it is described here. */ 1382 private String _exception; 1383 1384 /** The external entities being parsed. */ 1385 private Stack _externalEntities = new Stack(); 1386 1387 /** Indicator that the primary source of documentation is the instance. */ 1388 private boolean _isInstanceDoc = false; 1389 1390 /** The name associated with the current port, parameter, etc. */ 1391 private String _name; 1392 1393 /** The next tier in the class hierarchy. */ 1394 private DocManager _nextTier; 1395 1396 /** The parser. */ 1397 private XmlParser _parser = new XmlParser(); 1398 1399 /** A table of property documents. */ 1400 private HashMap _properties = new HashMap(); 1401 1402 /** A table of port documents. */ 1403 private HashMap _ports = new HashMap(); 1404 1405 /** The Pt.AcceptedRating field. */ 1406 private String _ptAcceptedRating; 1407 1408 /** The Pt.ProposedRating field. */ 1409 private String _ptProposedRating; 1410 1411 /** The location of the website documentation. 1412 */ 1413 private static String _remoteDocumentationURLBase; 1414 1415 /** The since field. */ 1416 private String _since; 1417 1418 /** The object to be documented. */ 1419 private NamedObj _target; 1420 1421 /** The class of object to be documented. */ 1422 private Class _targetClass; 1423 1424 /** The version field. */ 1425 private String _version; 1426}