1 16 17 import java.io.File ; 18 import java.io.FileWriter ; 19 import java.io.IOException ; 20 import java.net.MalformedURLException ; 21 import java.util.ArrayList ; 22 import java.util.HashMap ; 23 import java.util.Iterator ; 24 import java.util.List ; 25 import java.util.Map ; 26 27 import javax.xml.transform.TransformerException ; 28 29 import org.apache.tools.ant.BuildException; 30 import org.apache.tools.ant.Project; 31 import org.apache.tools.ant.types.FileSet; 32 import org.apache.xpath.XPathAPI; 33 import org.w3c.dom.Document ; 34 import org.w3c.dom.Element ; 35 import org.w3c.dom.Node ; 36 import org.w3c.dom.NodeList ; 37 38 import com.thoughtworks.qdox.ant.AbstractQdoxTask; 39 import com.thoughtworks.qdox.model.DocletTag; 40 import com.thoughtworks.qdox.model.JavaClass; 41 import com.thoughtworks.qdox.parser.ParseException; 42 43 54 public final class SitemapTask extends AbstractQdoxTask { 55 56 57 public static final String NAME_TAG = "cocoon.sitemap.component.name"; 58 59 public static final String LOGGER_TAG = "cocoon.sitemap.component.logger"; 60 61 public static final String LABEL_TAG = "cocoon.sitemap.component.label"; 62 63 public static final String MIMETYPE_TAG = "cocoon.sitemap.component.mimetype"; 64 65 public static final String HIDDEN_TAG = "cocoon.sitemap.component.hide"; 66 67 public static final String NO_DOC_TAG = "cocoon.sitemap.component.documentation.disabled"; 68 69 public static final String DOC_TAG = "cocoon.sitemap.component.documentation"; 70 71 public static final String CONF_TAG = "cocoon.sitemap.component.configuration"; 72 73 public static final String CACHING_INFO_TAG = "cocoon.sitemap.component.documentation.caching"; 74 75 76 public static final String POOL_MAX_TAG = "cocoon.sitemap.component.pooling.max"; 77 78 private static final String LINE_SEPARATOR = "\n"; 79 80 81 private File sitemap; 82 83 84 private File docDir; 85 86 87 private static Map cache = new HashMap (); 88 89 90 private String directory; 91 92 93 protected String blockName; 94 95 96 protected boolean deprecated = false; 97 98 99 protected boolean stable = true; 100 101 105 public void setSource(File dir) { 106 try { 107 this.directory = dir.toURL().toExternalForm(); 108 if ( !dir.isDirectory() ) { 109 throw new BuildException("Source is not a directory."); 110 } 111 } catch (IOException ioe) { 112 throw new BuildException(ioe); 113 } 114 FileSet set = new FileSet(); 115 set.setDir(dir); 116 set.setIncludes("**/*.java"); 117 super.addFileset(set); 118 } 119 120 123 public void setSitemap( final File sitemap ) { 124 this.sitemap = sitemap; 125 } 126 127 130 public void setDocDir( final File dir ) { 131 this.docDir = dir; 132 } 133 134 137 public void setBlock(String value) { 138 this.blockName = value; 139 } 140 141 144 public void setDeprecated(boolean value) { 145 this.deprecated = value; 146 } 147 148 151 public void setStable(boolean value) { 152 this.stable = value; 153 } 154 155 158 private String classType(JavaClass clazz) { 159 if ( clazz.isA(GENERATOR) ) { 160 return "generator"; 161 } else if ( clazz.isA(TRANSFORMER) ) { 162 return "transformer"; 163 } else if ( clazz.isA(READER) ) { 164 return "reader"; 165 } else if ( clazz.isA(SERIALIZER) ) { 166 return "serializer"; 167 } else if ( clazz.isA(ACTION) ) { 168 return "action"; 169 } else if ( clazz.isA(MATCHER) ) { 170 return "matcher"; 171 } else if ( clazz.isA(SELECTOR) ) { 172 return "selector"; 173 } else if ( clazz.isA(PIPELINE) ) { 174 return "pipe"; 175 } else if ( clazz.getPackage().equals("org.apache.cocoon.generation") && (clazz.isA("Generator") || clazz.isA("ServiceableGenerator")) ) { 177 return "generator"; 178 } else if ( clazz.isA("org.apache.cocoon.generation.ServiceableGenerator") ) { 179 return "generator"; 180 } else if ( clazz.getPackage().equals("org.apache.cocoon.transformation") && clazz.isA("Transformer") ) { 181 return "transformer"; 182 } else if ( clazz.getPackage().equals("org.apache.cocoon.reading") && clazz.isA("Reader") ) { 183 return "reader"; 184 } else if ( clazz.getPackage().equals("org.apache.cocoon.serialization") && clazz.isA("Serializer") ) { 185 return "serializer"; 186 } else if ( clazz.getPackage().equals("org.apache.cocoon.acting") && clazz.isA("Action") ) { 187 return "action"; 188 } else if ( clazz.getPackage().equals("org.apache.cocoon.matching") && clazz.isA("Matcher") ) { 189 return "matcher"; 190 } else if ( clazz.getPackage().equals("org.apache.cocoon.selection") && clazz.isA("Selector") ) { 191 return "selector"; 192 } else if ( clazz.getPackage().equals("org.apache.cocoon.components.pipeline") && clazz.isA("ProcessingPipeline") ) { 193 return "pipe"; 194 } else { 195 return null; 196 } 197 } 198 199 204 public void execute() 205 throws BuildException { 206 validate(); 207 208 List components = (List )cache.get(this.directory); 209 if ( components == null ) { 210 try { 212 super.execute(); 213 } 214 catch (ParseException pe) { 215 log("ParseException: " + pe); 216 } 217 components = this.collectInfo(); 218 cache.put(this.directory, components); 219 } 220 221 try { 222 223 if ( this.sitemap != null ) { 224 this.processSitemap(components); 225 } 226 if ( this.docDir != null ) { 227 this.processDocDir(components); 228 } 229 230 } catch ( final BuildException e ) { 231 throw e; 232 } catch ( final Exception e ) { 233 throw new BuildException( e.toString(), e ); 234 } 235 } 236 237 240 private void validate() { 241 if ( this.directory == null ) { 242 throw new BuildException("Source is not specified."); 243 } 244 if ( this.sitemap == null && this.docDir == null ) { 245 throw new BuildException("Sitemap or DocDir is not specified."); 246 } 247 248 if ( this.sitemap != null && this.sitemap.isDirectory() ) { 249 throw new BuildException( "Sitemap (" + this.sitemap + ") is not a file." ); 250 } 251 if ( this.docDir != null && !this.docDir.isDirectory() ) { 252 throw new BuildException( "DocDir (" + this.docDir + ") is not a directory." ); 253 } 254 } 255 256 259 private List collectInfo() { 260 log("Collecting sitemap components info"); 261 final List components = new ArrayList (); 262 final List allComponentNames = new ArrayList (); 263 264 final Iterator it = super.allClasses.iterator(); 265 266 while ( it.hasNext() ) { 267 final JavaClass javaClass = (JavaClass) it.next(); 268 269 final DocletTag tag = javaClass.getTagByName( NAME_TAG ); 270 if (classType(javaClass) != null) { 271 allComponentNames.add(javaClass.getFullyQualifiedName()); 272 } 273 274 if ( null != tag ) { 275 final SitemapComponent comp = new SitemapComponent( javaClass ); 276 277 log("Found component: " + comp, Project.MSG_DEBUG); 278 components.add(comp); 279 } 280 } 281 282 final String fileSeparator = System.getProperty("file.separator", "/"); 284 final String matchString = "blocks" + fileSeparator; 285 final String outputFname = "build" + fileSeparator + "all-sitemap-components" + 286 (directory.endsWith(matchString) ? "-blocks" : "") + ".txt"; 287 log("Listing all sitemap components to " + outputFname); 288 try { 289 File outputFile = new File (outputFname); 290 FileWriter out = new FileWriter (outputFile); 291 final String lineSeparator = System.getProperty("line.separator", "\n"); 292 final Iterator iter = allComponentNames.iterator(); 293 while ( iter.hasNext() ) { 294 out.write((String )iter.next()); 295 out.write(lineSeparator); 296 } 297 out.close(); 298 } 299 catch (IOException ioe) { 300 log("IOException: " + ioe); 301 } 302 303 return components; 304 } 305 306 309 private void processSitemap(List components) 310 throws Exception { 311 log("Adding sitemap components"); 312 Document document; 313 314 document = DocumentCache.getDocument(this.sitemap, this); 315 316 boolean changed = false; 317 318 Iterator iter = components.iterator(); 319 while ( iter.hasNext() ) { 320 SitemapComponent component = (SitemapComponent)iter.next(); 321 final String type = component.getType(); 322 final String section = type + 's'; 323 324 NodeList nodes = XPathAPI.selectNodeList(document, "/sitemap/components/" + section); 325 326 if (nodes.getLength() != 1 ) { 327 throw new BuildException("Unable to find section for component type " + type); 328 } 329 NodeList oldNodes = XPathAPI.selectNodeList(document, 331 "/sitemap/components/" + section + '/' + type + "[@name='" + component.getName() + "']"); 332 for(int i=0; i < oldNodes.getLength(); i++ ) { 333 final Node node = oldNodes.item(i); 334 node.getParentNode().removeChild(node); 335 } 336 337 if (component.append(nodes.item(0)) ) { 339 changed = true; 340 } 341 342 } 343 344 if ( changed ) { 345 DocumentCache.writeDocument(this.sitemap, document, this); 346 } 347 DocumentCache.storeDocument(this.sitemap, document, this); 348 } 349 350 353 private void processDocDir(List components) 354 throws Exception { 355 log("Generating documentation"); 356 357 Iterator iter = components.iterator(); 358 while ( iter.hasNext() ) { 359 final SitemapComponent component = (SitemapComponent)iter.next(); 360 361 final File templateFile = this.getProject().resolveFile("src/documentation/templates/sitemap-component.xml"); 363 Document template = DocumentCache.getDocument(templateFile, this); 364 365 final File componentsDir = new File (this.docDir, component.getType()+'s'); 367 componentsDir.mkdir(); 368 369 String fileName = component.getName() + "-" + component.getType() + ".xml"; 371 if ( this.blockName != null ) { 372 fileName = this.blockName + "-" + fileName; 373 } 374 final File docFile = new File (componentsDir, fileName); 375 376 component.generateDocs(template, docFile, this.getProject()); 378 379 final File indexFile = new File (componentsDir, "book.xml"); 381 final Document indexDoc = DocumentCache.getDocument(indexFile, this); 382 final String section; 383 if ( this.blockName == null ) { 384 section = "Core"; 385 } else { 386 section = this.blockName + " Block"; 387 } 388 Node sectionNode = XPathAPI.selectSingleNode(indexDoc, "/book/menu[@label='"+section+"']"); 389 if ( sectionNode == null ) { 390 sectionNode = indexDoc.createElement("menu"); 391 ((Element )sectionNode).setAttribute("label", section); 392 indexDoc.getDocumentElement().appendChild(sectionNode); 393 } 394 final String htmlName = docFile.getName().substring(0, docFile.getName().length()-3) + "html"; 395 Node oldEntry = XPathAPI.selectSingleNode(sectionNode, "menu-item[@href='"+htmlName+"']"); 396 Node newEntry = indexDoc.createElement("menu-item"); 397 ((Element )newEntry).setAttribute("href", htmlName); 398 final String label = capitalize(component.getName()) + " " + capitalize(component.getType()); 399 ((Element )newEntry).setAttribute("label", label); 400 if ( oldEntry != null ) { 401 oldEntry.getParentNode().replaceChild(newEntry, oldEntry); 402 } else { 403 Node nextLabel = null; 404 final NodeList childs = sectionNode.getChildNodes(); 405 int i = 0; 406 while ( nextLabel == null && i < childs.getLength() ) { 407 final Node current = childs.item(i); 408 if ( current instanceof Element ) { 409 final String currentLabel = ((Element )current).getAttribute("label"); 410 if ( label.compareTo(currentLabel) < 0 ) { 411 nextLabel = current; 412 } 413 } 414 i++; 415 } 416 if ( nextLabel == null ) { 417 sectionNode.appendChild(newEntry); 418 } else { 419 sectionNode.insertBefore(newEntry, nextLabel); 420 } 421 } 422 DocumentCache.writeDocument(indexFile, indexDoc, this); 423 } 424 425 } 426 427 432 public static String capitalize(String str) { 433 int strLen; 434 if (str == null || (strLen = str.length()) == 0) { 435 return str; 436 } 437 return new StringBuffer (strLen) 438 .append(Character.toTitleCase(str.charAt(0))) 439 .append(str.substring(1)) 440 .toString(); 441 } 442 443 final class SitemapComponent { 444 445 final protected JavaClass javaClass; 446 final String name; 447 final String type; 448 449 public SitemapComponent(JavaClass javaClass) { 450 this.javaClass = javaClass; 451 452 this.name = this.javaClass.getTagByName( NAME_TAG ).getValue(); 453 System.out.println("Name: " + this.name); 455 System.out.println("className: " + this.javaClass.getName()); 456 System.out.println(); 457 458 459 JavaClass[] jc = this.javaClass.getImplementedInterfaces(); 460 if (jc.length> 0) { 461 System.out.println("Implemented interfaces:"); 462 } 463 for (int i = 0; i < jc.length; i++) { 464 System.out.println(jc[i].getName() + ". Full name: " + jc[i].getFullyQualifiedName()); 465 } 466 System.out.println("==== END of implements ==="); 467 this.type = classType(this.javaClass); 469 } 470 471 474 public String toString() { 475 return "Sitemap component: " + this.javaClass.getName(); 476 } 477 478 public String getType() { 479 return this.type; 480 } 481 482 public String getName() { 483 return this.name; 484 } 485 486 public boolean append(Node parent) { 487 if ( this.getTagValue(HIDDEN_TAG, null) != null ) { 488 return false; 489 } 490 Document doc = parent.getOwnerDocument(); 491 Node node; 492 493 if ( SitemapTask.this.deprecated || this.getTagValue("deprecated", null) != null ) { 495 indent(parent, 3); 496 StringBuffer buffer = new StringBuffer ("The "); 497 buffer.append(this.type) 498 .append(" ") 499 .append(this.name) 500 .append(" is deprecated"); 501 node = doc.createComment(buffer.toString()); 502 parent.appendChild(node); 503 newLine(parent); 504 } 505 if ( !SitemapTask.this.stable ) { 507 indent(parent, 3); 508 StringBuffer buffer = new StringBuffer ("The "); 509 buffer.append(this.type) 510 .append(" ") 511 .append(this.name) 512 .append(" is in an unstable block"); 513 node = doc.createComment(buffer.toString()); 514 parent.appendChild(node); 515 newLine(parent); 516 } 517 518 indent(parent, 3); 519 node = doc.createElement("map:" + this.type); 520 ((Element )node).setAttribute("name", this.name); 521 ((Element )node).setAttribute("src", this.javaClass.getFullyQualifiedName()); 522 523 if ( this.javaClass.isA(LOG_ENABLED) ) { 526 this.addAttribute(node, LOGGER_TAG, "logger", null); 527 } 528 529 this.addAttribute(node, LABEL_TAG, "label", null); 531 532 if ( this.javaClass.isA(POOLABLE) ) { 534 this.addAttribute(node, POOL_MAX_TAG, "pool-max", null); 536 } 537 538 if ( this.javaClass.isA(OUTPUT_COMPONENT) ) { 540 this.addAttribute(node, MIMETYPE_TAG, "mime-type", null); 541 } 542 543 parent.appendChild(node); 545 newLine(parent); 546 547 String configuration = this.getTagValue(CONF_TAG, null); 549 if ( configuration != null ) { 550 configuration = "<root>" + configuration + "</root>"; 551 final Document confDoc = DocumentCache.getDocument(configuration, null); 552 setValue(node, null, confDoc.getDocumentElement().getChildNodes()); 553 } 554 555 return true; 556 } 557 558 private void addAttribute(Node node, String tag, String attributeName, String defaultValue) { 559 final String tagValue = this.getTagValue(tag, defaultValue); 560 if ( tagValue != null ) { 561 ((Element )node).setAttribute(attributeName, tagValue); 562 } 563 } 564 565 private void newLine(Node node) { 566 final Node n = node.getOwnerDocument().createTextNode(LINE_SEPARATOR); 567 node.appendChild(n); 568 } 569 570 private void indent(Node node, int depth) { 571 final StringBuffer buffer = new StringBuffer (); 572 for(int i=0; i < depth*2; i++ ) { 573 buffer.append(' '); 574 } 575 final Node n = node.getOwnerDocument().createTextNode(buffer.toString()); 576 node.appendChild(n); 577 } 578 579 public void generateDocs(Document template, File docFile, Project project) 580 throws TransformerException { 581 582 final String doc = this.getDocumentation(); 583 if ( doc == null ) { 584 if ( docFile.exists() ) { 585 docFile.delete(); 586 } 587 return; 588 } 589 final Node body = XPathAPI.selectSingleNode(template, "/document/body"); 591 592 final String description = "<root><p>" + doc + "</p></root>"; 594 String systemURI = null; 595 try { 596 systemURI = docFile.toURL().toExternalForm(); 597 } catch (MalformedURLException mue) { 598 } 600 final Document descriptionDoc = DocumentCache.getDocument(description, systemURI); 601 602 setValue(template, "/document/header/title", 604 "Description of the " + this.name + " " + this.type); 605 606 setValue(template, "/document/header/version", 608 project.getProperty("version")); 609 610 setValue(body, "s1[@title='Description']", 612 descriptionDoc.getDocumentElement().getChildNodes()); 613 614 if ( SitemapTask.this.deprecated || this.getTagValue("deprecated", null) != null ) { 616 Node node = XPathAPI.selectSingleNode(body, "s1[@title='Description']"); 617 Element e = node.getOwnerDocument().createElement("note"); 619 node.appendChild(e); 620 e.appendChild(node.getOwnerDocument().createTextNode("This component is deprecated.")); 621 final String info = this.getTagValue("deprecated", null); 622 if ( info != null ) { 623 e.appendChild(node.getOwnerDocument().createTextNode(info)); 624 } 625 } 626 if ( !SitemapTask.this.stable ) { 628 Node node = XPathAPI.selectSingleNode(body, "s1[@title='Description']"); 629 Element e = node.getOwnerDocument().createElement("note"); 631 node.appendChild(e); 632 e.appendChild(node.getOwnerDocument().createTextNode("This component is in an unstable block.")); 633 } 634 635 final Node tableNode = XPathAPI.selectSingleNode(body, "s1[@title='Info']/table"); 637 638 this.addRow(tableNode, "Name", this.name); 640 641 if ( SitemapTask.this.blockName != null ) { 643 this.addRow(tableNode, "Block", SitemapTask.this.blockName); 644 } 645 646 if ( this.javaClass.isA(GENERATOR) 648 || this.javaClass.isA(TRANSFORMER) 649 || this.javaClass.isA(SERIALIZER) 650 || this.javaClass.isA(READER)) { 651 652 String cacheInfo; 653 if ( this.javaClass.isA(CACHEABLE) ) { 654 cacheInfo = this.getTagValue(CACHING_INFO_TAG, null); 655 if ( cacheInfo != null ) { 656 cacheInfo = "Yes - " + cacheInfo; 657 } else { 658 cacheInfo = "Yes"; 659 } 660 } else if ( this.javaClass.isA(DEPRECATED_CACHEABLE) ) { 661 cacheInfo = this.getTagValue(CACHING_INFO_TAG, null); 662 if ( cacheInfo != null ) { 663 cacheInfo = "Yes (2.0 Caching) - " + cacheInfo; 664 } else { 665 cacheInfo = "Yes (2.0 Caching)"; 666 } 667 } else { 668 cacheInfo = "No"; 669 } 670 this.addRow(tableNode, "Cacheable", cacheInfo); 671 } 672 673 if ( this.javaClass.isA(OUTPUT_COMPONENT) ) { 675 final String value = this.getTagValue(MIMETYPE_TAG, "-"); 676 this.addRow(tableNode, "Mime-Type", value); 677 } 678 679 this.addRow(tableNode, "Class", this.javaClass.getFullyQualifiedName()); 681 682 this.merge(body, docFile); 684 685 DocumentCache.writeDocument(docFile, template, SitemapTask.this); 687 } 688 689 694 private void merge(Node body, File docFile) throws TransformerException { 695 final Document mergeDocument; 696 try { 697 mergeDocument = DocumentCache.getDocument(docFile, SitemapTask.this); 698 } catch (Exception ignore) { 699 return; 700 } 701 NodeList sections = XPathAPI.selectNodeList(mergeDocument, "/document/body/s1"); 702 if ( sections != null ) { 703 for(int i=0; i<sections.getLength(); i++) { 704 final Element current = (Element )sections.item(i); 705 final String title = current.getAttribute("title"); 706 707 if (XPathAPI.selectSingleNode(body, "s1[@title='"+title+"']") == null ) { 709 body.appendChild(body.getOwnerDocument().importNode(current, true)); 710 } 711 } 712 } 713 } 714 715 719 private String getDocumentation() { 720 if ( this.getTagValue(NO_DOC_TAG, null) != null ) { 721 return null; 722 } 723 return this.getTagValue(DOC_TAG, null); 724 } 725 726 private String getTagValue(String tagName, String defaultValue) { 727 final DocletTag tag = this.javaClass.getTagByName( tagName ); 728 if ( tag != null ) { 729 return tag.getValue(); 730 } 731 return defaultValue; 732 } 733 734 private void addRow(Node table, String title, String value) { 735 final Element row = table.getOwnerDocument().createElement("tr"); 736 final Element firstColumn = table.getOwnerDocument().createElement("td"); 737 firstColumn.appendChild(table.getOwnerDocument().createTextNode(title)); 738 final Element secondColumn = table.getOwnerDocument().createElement("td"); 739 secondColumn.appendChild(table.getOwnerDocument().createTextNode(value)); 740 row.appendChild(firstColumn); 741 row.appendChild(secondColumn); 742 table.appendChild(row); 743 } 744 745 private void setValue(Node node, String xpath, String value) { 746 try { 747 final Node insertNode = (xpath == null ? node : XPathAPI.selectSingleNode(node, xpath)); 748 if ( insertNode == null ) { 749 throw new BuildException("Node (" + xpath + ") not found."); 750 } 751 Node text = insertNode.getOwnerDocument().createTextNode(value); 752 while (insertNode.hasChildNodes() ) { 753 insertNode.removeChild(insertNode.getFirstChild()); 754 } 755 insertNode.appendChild(text); 756 } catch (TransformerException e) { 757 throw new BuildException(e); 758 } 759 } 760 761 private void setValue(Node node, String xpath, NodeList nodes) { 762 try { 763 final Node insertNode = (xpath == null ? node : XPathAPI.selectSingleNode(node, xpath)); 764 if ( insertNode == null ) { 765 throw new BuildException("Node (" + xpath + ") not found."); 766 } 767 while (insertNode.hasChildNodes() ) { 768 insertNode.removeChild(insertNode.getFirstChild()); 769 } 770 for(int i=0; i<nodes.getLength(); i++) { 771 final Node current = nodes.item(i); 772 insertNode.appendChild(insertNode.getOwnerDocument().importNode(current, true)); 773 } 774 } catch (TransformerException e) { 775 throw new BuildException(e); 776 } 777 } 778 } 779 780 private static final String LOG_ENABLED = "org.apache.avalon.framework.logger.LogEnabled"; 782 private static final String POOLABLE = "org.apache.avalon.excalibur.pool.Poolable"; 783 784 private static final String CACHEABLE = "org.apache.cocoon.caching.CacheableProcessingComponent"; 785 private static final String DEPRECATED_CACHEABLE = "org.apache.cocoon.caching.Cacheable"; 786 787 private static final String OUTPUT_COMPONENT = "org.apache.cocoon.sitemap.SitemapOutputComponent"; 788 789 private static final String GENERATOR = "org.apache.cocoon.generation.Generator"; 790 private static final String TRANSFORMER = "org.apache.cocoon.transformation.Transformer"; 791 private static final String SERIALIZER = "org.apache.cocoon.serialization.Serializer"; 792 private static final String READER = "org.apache.cocoon.reading.Reader"; 793 private static final String MATCHER = "org.apache.cocoon.matching.Matcher"; 794 private static final String SELECTOR = "org.apache.cocoon.selection.Selector"; 795 private static final String ACTION = "org.apache.cocoon.acting.Action"; 796 private static final String PIPELINE = "org.apache.cocoon.components.pipeline.ProcessingPipeline"; 797 } 798 | Popular Tags |