| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.myfaces.buildtools.maven2.plugin.tagdoc; |
| |
| //import org.apache.maven.doxia.sink.Sink; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStreamWriter; |
| import java.io.Writer; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.TreeSet; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.parsers.SAXParserFactory; |
| |
| import org.apache.commons.digester.AbstractObjectCreationFactory; |
| import org.apache.commons.digester.Digester; |
| import org.apache.maven.artifact.DependencyResolutionRequiredException; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.reporting.AbstractMavenMultiPageReport; |
| import org.apache.maven.reporting.MavenReportException; |
| import org.apache.maven.reporting.sink.SinkFactory; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.ComponentBean; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.ConverterBean; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.EventBean; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.EventRefBean; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.FacesConfigBean; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.FacesConfigParser; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.FacetBean; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.PropertyBean; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.parse.ValidatorBean; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.util.ComponentFilter; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.util.ConverterFilter; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.util.FilteredIterator; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.util.PropertyFilter; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.util.ValidatorFilter; |
| import org.apache.myfaces.buildtools.maven2.plugin.faces.util.XIncludeFilter; |
| import org.codehaus.doxia.sink.Sink; |
| import org.codehaus.doxia.site.renderer.SiteRenderer; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Report for generating JSF tagdoc based on faces-config.xml parsing. |
| * Note that this is not really an AbstractMavenMultiPageReport - the |
| * secondary pages are manually generated XDoc. I tried using the true |
| * multipage report approach, and ran into Maven bugs (as of Maven 2.0.2, |
| * site 2.0b4). |
| * |
| * @goal tagdoc |
| */ |
| public class TagdocReport extends AbstractMavenMultiPageReport |
| { |
| protected void executeReport( Locale locale ) |
| throws MavenReportException |
| { |
| // Why the heck doesn't Maven do this for me? |
| SinkFactory factory = new SinkFactory(); |
| factory.setSiteRenderer(getSiteRenderer()); |
| factory.setSiteDirectory(getOutputDirectory()); |
| setSinkFactory(factory); |
| |
| processIndex(project, resourcePath); |
| try |
| { |
| _generateTagDocs(); |
| } |
| catch (Exception e) |
| { |
| throw new MavenReportException("Couldn't generate tagdoc", e); |
| } |
| } |
| |
| private void _generateTagDocs() throws Exception |
| { |
| FacesConfigBean facesConfig = getFacesConfig(); |
| if (!facesConfig.hasComponents()) |
| { |
| getLog().info("Nothing to generate - no components found"); |
| return; |
| } |
| |
| |
| Iterator components = facesConfig.components(); |
| components = new FilteredIterator(components, new SkipFilter()); |
| components = new FilteredIterator(components, new ComponentTagFilter()); |
| components = new FilteredIterator(components, new ComponentNamespaceFilter()); |
| |
| Iterator validators = facesConfig.validators(); |
| validators = new FilteredIterator(validators, new ValidatorTagFilter()); |
| validators = new FilteredIterator(validators, new ValidatorNamespaceFilter()); |
| |
| Iterator converters = facesConfig.converters(); |
| converters = new FilteredIterator(converters, new ConverterTagFilter()); |
| converters = new FilteredIterator(converters, new ConverterNamespaceFilter()); |
| |
| // =-=AEW Note that only updating out-of-date components, etc. is |
| // permanently tricky, even if we had proper detection in place, |
| // because the index always has to have all docs |
| /* |
| if (!components.hasNext() && !converters.hasNext() && !validators.hasNext()) |
| { |
| getLog().info("Nothing to generate - all docs are up to date"); |
| return; |
| } |
| */ |
| |
| Set componentPages = new TreeSet(); |
| Set converterPages = new TreeSet(); |
| Set validatorPages = new TreeSet(); |
| |
| int count = 0; |
| while (components.hasNext()) |
| { |
| String pageName = _generateComponentDoc((ComponentBean)components.next()); |
| if (pageName != null) |
| { |
| componentPages.add(pageName); |
| count++; |
| } |
| } |
| while (converters.hasNext()) |
| { |
| String pageName = _generateConverterDoc((ConverterBean)converters.next()); |
| if (pageName != null) |
| { |
| converterPages.add(pageName); |
| count++; |
| } |
| } |
| while (validators.hasNext()) |
| { |
| String pageName = _generateValidatorDoc((ValidatorBean)validators.next()); |
| if (pageName != null) |
| { |
| validatorPages.add(pageName); |
| count++; |
| } |
| } |
| |
| |
| Set otherPages = _gatherOtherTags(); |
| |
| getLog().info("Generated " + count + " page(s)"); |
| |
| Sink sink = getSink(); |
| sink.head(); |
| sink.title(); |
| sink.text("Tag library documentation"); |
| sink.title_(); |
| sink.head_(); |
| sink.body(); |
| |
| sink.sectionTitle1(); |
| sink.text("Tag library information"); |
| sink.sectionTitle1_(); |
| sink.section1(); |
| |
| for (Iterator i = taglibs.entrySet().iterator(); i.hasNext(); ) |
| { |
| Map.Entry entry = (Map.Entry)i.next(); |
| sink.paragraph(); |
| |
| sink.bold(); |
| sink.text("Short name:"); |
| sink.bold_(); |
| sink.nonBreakingSpace(); |
| sink.text(entry.getKey().toString()); |
| sink.lineBreak(); |
| |
| sink.bold(); |
| sink.text("Namespace:"); |
| sink.bold_(); |
| sink.nonBreakingSpace(); |
| sink.text(entry.getValue().toString()); |
| sink.lineBreak(); |
| |
| sink.paragraph_(); |
| } |
| |
| sink.section1_(); |
| |
| _writeIndexSection(sink, componentPages, "Components"); |
| _writeIndexSection(sink, converterPages, "Converters"); |
| _writeIndexSection(sink, validatorPages, "Validators"); |
| _writeIndexSection(sink, otherPages, "Miscellaneous"); |
| |
| sink.body_(); |
| } |
| |
| private Set _gatherOtherTags() |
| { |
| TreeSet set = new TreeSet(); |
| String subDir = |
| _platformAgnosticPath(_platformAgnosticPath("xdoc/" + |
| _DOC_SUBDIRECTORY)); |
| File siteSubDir = new File(siteDirectory, subDir); |
| if (siteSubDir.exists()) |
| { |
| String[] files = siteSubDir.list(); |
| for (int i = 0; i < files.length; i++) |
| { |
| String file = files[i]; |
| if (file.endsWith(".xml")) |
| { |
| set.add(file.substring(0, file.length() - 4)); |
| } |
| } |
| } |
| |
| return set; |
| } |
| |
| private String _formatPropList( |
| String[] pList, |
| String header) |
| { |
| String[] nullList = {}; |
| return _formatPropList(pList, header, nullList); |
| } |
| |
| private String _formatPropList( |
| String[] pList, |
| String header, |
| String[] ignores) |
| { |
| String formatted = null; |
| if ((pList != null) && (pList.length > 0)) |
| { |
| // Don't know how long this will be, but 100 should be plenty. |
| StringBuffer sb = new StringBuffer(100); |
| sb.append("\n"); |
| sb.append("<b>"); |
| sb.append(header); |
| sb.append(":</b> "); |
| |
| boolean gotOne = false; |
| |
| for (int arrInd = 0; arrInd < pList.length; arrInd++) |
| { |
| String curStr = pList[arrInd]; |
| outer: |
| if (curStr != null) |
| { |
| for (int i = 0; i < ignores.length; i++) |
| { |
| String s = ignores[i]; |
| if ((s != null) && (s.equalsIgnoreCase(curStr))) |
| { |
| break outer; |
| } |
| } |
| |
| if (gotOne) |
| { |
| sb.append(", "); |
| } |
| gotOne = true; |
| sb.append(curStr); |
| } |
| } |
| if (gotOne) |
| { |
| sb.append("<br/>\n"); |
| formatted = sb.toString(); |
| } |
| } |
| return formatted; |
| } |
| |
| private void _writeIndexSection(Sink sink, Set pages, String title) |
| { |
| if (pages.isEmpty()) |
| { |
| return; |
| } |
| |
| sink.sectionTitle1(); |
| sink.text(title); |
| sink.sectionTitle1_(); |
| sink.section1(); |
| sink.table(); |
| sink.tableRow(); |
| sink.tableHeaderCell(); |
| sink.text("Tag Name"); |
| sink.tableHeaderCell_(); |
| sink.tableRow_(); |
| |
| Iterator iter = pages.iterator(); |
| while (iter.hasNext()) |
| { |
| sink.tableRow(); |
| sink.tableCell(); |
| |
| String name = (String) iter.next(); |
| String tagName = "<" + name.replace('_', ':') + ">"; |
| |
| sink.link(_DOC_SUBDIRECTORY + "/" + name + ".html"); |
| sink.text(tagName); |
| sink.link_(); |
| |
| sink.tableCell_(); |
| sink.tableRow_(); |
| } |
| |
| sink.table_(); |
| sink.section1_(); |
| } |
| |
| public boolean usePageLinkBar() |
| { |
| return false; |
| } |
| |
| private String _toPageName(QName qName) |
| { |
| return _getPrefix(qName) + "_" + qName.getLocalPart(); |
| } |
| |
| private String _getQualifiedName(QName qName) |
| { |
| return _getPrefix(qName) + ":" + qName.getLocalPart(); |
| } |
| |
| private String _getPrefix(QName qName) |
| { |
| if ((qName.getPrefix() != null) && !"".equals(qName.getPrefix())) |
| { |
| return qName.getPrefix(); |
| } |
| |
| String namespace = qName.getNamespaceURI(); |
| if (namespace == null) |
| { |
| return null; |
| } |
| |
| for (Iterator i = taglibs.entrySet().iterator(); i.hasNext(); ) |
| { |
| Map.Entry entry = (Map.Entry)i.next(); |
| if (namespace.equals(entry.getValue())) |
| { |
| return (String) entry.getKey(); |
| } |
| } |
| |
| return "unknown"; |
| } |
| |
| private String _generateComponentDoc(ComponentBean component) |
| throws Exception |
| { |
| if (component.getTagName() == null) |
| { |
| return null; |
| } |
| String pageName = _toPageName(component.getTagName()); |
| |
| File targetDir = new File(outputDirectory.getParentFile(), |
| _platformAgnosticPath("generated-site/xdoc/" + |
| _DOC_SUBDIRECTORY)); |
| targetDir.mkdirs(); |
| File targetFile = new File(targetDir, pageName + ".xml"); |
| |
| Writer out = new OutputStreamWriter(new FileOutputStream(targetFile), |
| "UTF-8"); |
| try |
| { |
| out.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); |
| out.write("<document>\n"); |
| out.write(" <properties>\n"); |
| out.write(" <title><" + _getQualifiedName(component.getTagName()) + "></title>\n"); |
| out.write(" </properties>\n"); |
| out.write(" <body>\n"); |
| |
| out.write(" <section name=\"Summary\">\n"); |
| out.write(" <p>\n"); |
| _writeComponentSummary(out, component); |
| out.write(" </p>\n"); |
| out.write(" </section>\n"); |
| |
| if (component.hasEvents(true)) |
| { |
| out.write(" <section name=\"Events\">\n"); |
| out.write(" <p>\n"); |
| _writeComponentEvents(out, component); |
| out.write(" </p>\n"); |
| out.write(" </section>\n"); |
| } |
| |
| if (component.hasFacets(true)) |
| { |
| out.write(" <section name=\"Supported Facets\">\n"); |
| out.write(" <p>\n"); |
| _writeComponentFacets(out, component); |
| out.write(" </p>\n"); |
| out.write(" </section>\n"); |
| } |
| |
| out.write(" <section name=\"Attributes\">\n"); |
| _writeComponentAttributes(out, component); |
| out.write(" </section>\n"); |
| |
| out.write(" </body>\n"); |
| out.write("</document>\n"); |
| } |
| finally |
| { |
| out.close(); |
| } |
| |
| return pageName; |
| } |
| |
| private String _generateConverterDoc(ConverterBean converter) throws IOException |
| { |
| if (converter.getTagName() == null) |
| { |
| return null; |
| } |
| |
| String pageName = _toPageName(converter.getTagName()); |
| |
| File targetDir = new File(outputDirectory.getParentFile(), |
| _platformAgnosticPath("generated-site/xdoc/" + |
| _DOC_SUBDIRECTORY)); |
| targetDir.mkdirs(); |
| File targetFile = new File(targetDir, pageName + ".xml"); |
| |
| Writer out = new OutputStreamWriter(new FileOutputStream(targetFile), |
| "UTF-8"); |
| try |
| { |
| out.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); |
| out.write("<document>\n"); |
| out.write(" <properties>\n"); |
| out.write(" <title><" + _getQualifiedName(converter.getTagName()) + "></title>\n"); |
| out.write(" </properties>\n"); |
| out.write(" <body>\n"); |
| |
| out.write(" <section name=\"Summary\">\n"); |
| out.write(" <p>\n"); |
| _writeConverterSummary(out, converter); |
| out.write(" </p>\n"); |
| out.write(" </section>\n"); |
| |
| out.write(" <section name=\"Attributes\">\n"); |
| _writeConverterAttributes(out, converter); |
| out.write(" </section>\n"); |
| |
| out.write(" </body>\n"); |
| out.write("</document>\n"); |
| } |
| finally |
| { |
| out.close(); |
| } |
| |
| return pageName; |
| } |
| |
| private String _generateValidatorDoc(ValidatorBean validator) throws IOException |
| { |
| if (validator.getTagName() == null) |
| { |
| return null; |
| } |
| |
| String pageName = _toPageName(validator.getTagName()); |
| |
| File targetDir = new File(outputDirectory.getParentFile(), |
| _platformAgnosticPath("generated-site/xdoc/" + |
| _DOC_SUBDIRECTORY)); |
| targetDir.mkdirs(); |
| File targetFile = new File(targetDir, pageName + ".xml"); |
| |
| Writer out = new OutputStreamWriter(new FileOutputStream(targetFile), |
| "UTF-8"); |
| try |
| { |
| out.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); |
| out.write("<document>\n"); |
| out.write(" <properties>\n"); |
| out.write(" <title><" + _getQualifiedName(validator.getTagName()) + "></title>\n"); |
| out.write(" </properties>\n"); |
| out.write(" <body>\n"); |
| |
| out.write(" <section name=\"Summary\">\n"); |
| out.write(" <p>\n"); |
| _writeValidatorSummary(out, validator); |
| out.write(" </p>\n"); |
| out.write(" </section>\n"); |
| |
| out.write(" <section name=\"Attributes\">\n"); |
| _writeValidatorAttributes(out, validator); |
| out.write(" </section>\n"); |
| |
| out.write(" </body>\n"); |
| out.write("</document>\n"); |
| } |
| finally |
| { |
| out.close(); |
| } |
| |
| return pageName; |
| } |
| |
| |
| private void _writeComponentSummary(Writer out, ComponentBean bean) throws IOException |
| { |
| out.write(" <b>Tag name:</b> <" + |
| _getQualifiedName(bean.getTagName()) + ">\n"); |
| out.write(" <br/>\n"); |
| |
| |
| out.write(" <b>UIComponent class:</b> "); |
| String javadocURL = _platformAgnosticPath("../apidocs/" + |
| bean.getComponentClass().replace('.', '/') + ".html"); |
| out.write("<a href=\"" + javadocURL + "\">"); |
| |
| out.write(bean.getComponentClass()); |
| out.write("</a>"); |
| out.write("\n"); |
| out.write(" <br/>\n"); |
| out.write(" <b>Component type:</b> " + bean.getComponentType() + "\n"); |
| out.write(" <br/>\n"); |
| |
| if (_isNamingContainer(bean)) |
| { |
| out.write(" <p><b>Naming container:</b> Yes. When referring to children of this " + |
| "component (\"partialTriggers\", <code>findComponent()</code>, etc.), " + |
| "you must prefix the child's ID with this component's ID and a colon (':').</p>"); |
| } |
| |
| String fmtd = _formatPropList(bean.getUnsupportedAgents(), |
| "Unsupported agents", |
| _NON_DOCUMENTED_AGENTS); |
| if (fmtd != null) |
| { |
| out.write(" " + fmtd); |
| } |
| |
| String doc = bean.getLongDescription(); |
| if (doc == null) |
| { |
| doc = bean.getDescription(); |
| } |
| |
| out.write(_preToSource(doc)); |
| out.write("\n"); |
| } |
| |
| |
| private boolean _isNamingContainer(ComponentBean bean) |
| { |
| if (bean.isNamingContainer()) |
| { |
| return true; |
| } |
| |
| ComponentBean parent = bean.resolveSupertype(); |
| if (parent == null) |
| { |
| return false; |
| } |
| return _isNamingContainer(parent); |
| } |
| |
| private void _writeValidatorSummary(Writer out, ValidatorBean bean) throws IOException |
| { |
| out.write(" <b>Tag name:</b> <" + |
| _getQualifiedName(bean.getTagName()) + ">\n"); |
| out.write(" <br/>\n"); |
| |
| out.write(" <br/>\n"); |
| out.write(" <b> type:</b> " + bean.getValidatorId() + "\n"); |
| out.write(" <br/>\n"); |
| |
| String doc = bean.getLongDescription(); |
| if (doc == null) |
| { |
| doc = bean.getDescription(); |
| } |
| |
| out.write(_preToSource(doc)); |
| out.write("\n"); |
| } |
| |
| |
| private void _writeConverterSummary(Writer out, ConverterBean bean) throws IOException |
| { |
| out.write(" <b>Tag name:</b> <" + |
| _getQualifiedName(bean.getTagName()) + ">\n"); |
| out.write(" <br/>\n"); |
| |
| out.write(" <br/>\n"); |
| out.write(" <b> type:</b> " + bean.getConverterId() + "\n"); |
| out.write(" <br/>\n"); |
| |
| String doc = bean.getLongDescription(); |
| if (doc == null) |
| { |
| doc = bean.getDescription(); |
| } |
| |
| out.write(_preToSource(doc)); |
| out.write("\n"); |
| } |
| |
| static private final String _preToSource(String in) |
| { |
| in = in.replaceAll("<pre>", "<source><![CDATA["); |
| in = in.replaceAll("</pre>", "]]></source>"); |
| in = in.replaceAll("<html:", "<"); |
| in = in.replaceAll("</html:", "</"); |
| |
| return in; |
| } |
| |
| static private final String _platformAgnosticPath(String path) |
| { |
| return path.replace('/', File.separatorChar); |
| } |
| |
| |
| private class GroupComparator implements Comparator |
| { |
| public int compare(Object o1, Object o2) |
| { |
| return _getGroupIndex(o1) - _getGroupIndex(o2); |
| } |
| |
| public boolean equals(Object o) |
| { |
| return (o instanceof GroupComparator); |
| } |
| |
| public int hashCode() |
| { |
| // Yes, this is a very strange hashCode implementation. But the |
| // equals method is also very strange; any two GroupComparator |
| // objects are always equal. Therefore by the java rules, any |
| // two GroupComparator objects must always have the same hashCode. |
| return 17; // an arbitrary constant. |
| } |
| |
| private int _getGroupIndex(Object o) |
| { |
| String s = (o == null) ? null : o.toString(); |
| |
| |
| |
| |
| if ("message".equalsIgnoreCase(s)) |
| { |
| return 0; |
| } |
| |
| if ("core".equalsIgnoreCase(s)) |
| { |
| return 1; |
| } |
| if ("events".equalsIgnoreCase(s)) |
| { |
| return 2; |
| } |
| |
| if (s != null) |
| { |
| getLog().warn("UNKNOWN ATTRIBUTE GROUP: " + s); |
| } |
| |
| return 3; |
| } |
| } |
| |
| |
| private void _writeComponentAttributes(Writer out, ComponentBean bean) throws IOException |
| { |
| // Sort the names |
| TreeSet attributes = new TreeSet(); |
| Iterator attrs = bean.properties(true); |
| while (attrs.hasNext()) |
| { |
| PropertyBean property = (PropertyBean) attrs.next(); |
| if (!property.isTagAttributeExcluded()) |
| { |
| attributes.add(property.getPropertyName()); |
| } |
| } |
| |
| // Now get a list of PropertyBeans |
| List list = new ArrayList(); |
| Iterator iter = attributes.iterator(); |
| while (iter.hasNext()) |
| { |
| String attrName = (String) iter.next(); |
| list.add(bean.findProperty(attrName, true)); |
| } |
| |
| |
| TreeSet groups = new TreeSet(new GroupComparator()); |
| /* No current support for grouping |
| // Make sure "null" is the representative for unknown groups |
| Iterator iter = attributes.iterator(); |
| while (iter.hasNext()) |
| { |
| String group = ((AttributeDoc) iter.next()).group; |
| if (group != null) |
| { |
| groups.add(group); |
| } |
| } |
| */ |
| |
| _writeComponentAttributes(out, |
| list.iterator(), |
| bean.getComponentClass(), |
| groups.isEmpty() ? null : "Ungrouped"); |
| |
| Iterator groupIter = groups.iterator(); |
| while (groupIter.hasNext()) |
| { |
| _writeComponentAttributes(out, |
| list.iterator(), |
| bean.getComponentClass(), |
| (String) groupIter.next()); |
| } |
| |
| } |
| |
| |
| private void _writeConverterAttributes(Writer out, ConverterBean bean) throws IOException |
| { |
| // Sort the names |
| TreeSet attributes = new TreeSet(); |
| Iterator attrs = bean.properties(); |
| while (attrs.hasNext()) |
| { |
| PropertyBean property = (PropertyBean) attrs.next(); |
| if (!property.isTagAttributeExcluded()) |
| { |
| attributes.add(property.getPropertyName()); |
| } |
| } |
| |
| // Now get a list of PropertyBeans |
| List list = new ArrayList(); |
| Iterator iter = attributes.iterator(); |
| while (iter.hasNext()) |
| { |
| String attrName = (String) iter.next(); |
| list.add(bean.findProperty(attrName)); |
| } |
| |
| _writeComponentAttributes(out, |
| list.iterator(), |
| bean.getConverterClass(), |
| null); |
| } |
| |
| |
| |
| private void _writeValidatorAttributes(Writer out, ValidatorBean bean) throws IOException |
| { |
| // Sort the names |
| TreeSet attributes = new TreeSet(); |
| Iterator attrs = bean.properties(); |
| while (attrs.hasNext()) |
| { |
| PropertyBean property = (PropertyBean) attrs.next(); |
| if (!property.isTagAttributeExcluded()) |
| { |
| attributes.add(property.getPropertyName()); |
| } |
| } |
| |
| // Now get a list of PropertyBeans |
| List list = new ArrayList(); |
| Iterator iter = attributes.iterator(); |
| while (iter.hasNext()) |
| { |
| String attrName = (String) iter.next(); |
| list.add(bean.findProperty(attrName)); |
| } |
| |
| _writeComponentAttributes(out, |
| list.iterator(), |
| bean.getValidatorClass(), |
| null); |
| } |
| |
| |
| |
| private void _writeComponentAttributes(Writer out, |
| Iterator attributes, |
| String className, |
| String group) throws IOException |
| { |
| boolean writtenAnyAttributes = false; |
| |
| while (attributes.hasNext()) |
| { |
| PropertyBean attr = (PropertyBean) attributes.next(); |
| |
| /* |
| if ((group == null) || "Ungrouped".equals(group)) |
| { |
| if (doc.group != null) |
| continue; |
| } |
| else |
| { |
| if (!group.equalsIgnoreCase(doc.group)) |
| continue; |
| } |
| */ |
| |
| if (!writtenAnyAttributes) |
| { |
| writtenAnyAttributes = true; |
| if (group != null) |
| { |
| String sectionName; |
| if ("events".equalsIgnoreCase(group)) |
| { |
| sectionName = "Javascript attributes"; |
| } |
| else |
| { |
| sectionName = group + " attributes"; |
| } |
| |
| out.write("<subsection name=\"" + sectionName + "\">\n"); |
| } |
| |
| out.write("<table>\n"); |
| out.write("<tr>\n"); |
| out.write("<th>Name</th>\n"); |
| out.write("<th>Type</th>\n"); |
| out.write("<th>Supports EL?</th>\n"); |
| if (!_attrDocSpansColumns) |
| { |
| out.write("<th>Description</th>\n"); |
| } |
| out.write("</tr>\n"); |
| } |
| |
| String propertyName = attr.getPropertyName(); |
| // Quick fix of problems with actionExpression vs. action |
| // actionExpression is the MethodExpression on the component, |
| // but on the tag it's 'action' |
| if ("actionExpression".equals(propertyName)) |
| { |
| propertyName = "action"; |
| } |
| |
| out.write("<tr>\n"); |
| out.write("<td>" + propertyName + "</td>"); |
| String type = _getDisplayType(className, |
| propertyName, |
| attr.getPropertyClass()); |
| |
| out.write("<td>" + type + "</td>"); |
| |
| String elSupported; |
| // MethodBindings, "binding", and some other attributes |
| // require EL support |
| if (attr.isMethodBinding() || |
| attr.isMethodExpression() || |
| "binding".equals(propertyName)) |
| { |
| // "action" doesn't require EL; all else do. |
| elSupported = "action".equals(propertyName) ? "Yes" : "Only EL"; |
| } |
| else |
| { |
| elSupported = attr.isLiteralOnly() ? "No" : "Yes"; |
| } |
| |
| out.write("<td>" + elSupported + "</td>"); |
| |
| if (attr.getDescription() != null) |
| { |
| String valStr = _formatPropList(attr.getPropertyValues(), |
| "Valid Values"); |
| String unsupAgentsStr = |
| _formatPropList(attr.getUnsupportedAgents(), |
| "Not supported on the following agents", |
| _NON_DOCUMENTED_AGENTS); |
| String unsupRkStr = |
| _formatPropList(attr.getUnsupportedRenderKits(), |
| "Not supported on the following renderkits"); |
| |
| if (_attrDocSpansColumns) |
| { |
| out.write("</tr>\n"); |
| out.write("<tr>\n"); |
| out.write("<td colspan=\"3\">\n"); |
| } |
| else |
| { |
| out.write("<td>\n"); |
| } |
| |
| // out.write(EscapeUtils.escapeElementValue(doc.doc)); |
| if (valStr != null) |
| { |
| out.write(valStr); |
| out.write("<br/>"); |
| } |
| out.write(attr.getDescription()); |
| if (unsupAgentsStr != null) |
| { |
| out.write("<br/>"); |
| out.write(unsupAgentsStr); |
| } |
| if (unsupRkStr != null) |
| { |
| out.write("<br/>"); |
| out.write(unsupRkStr); |
| } |
| //out.write(EscapeUtils.escapeAmpersands(doc.doc)); |
| out.write("</td>\n"); |
| } |
| |
| out.write("</tr>\n"); |
| } |
| |
| if (writtenAnyAttributes) |
| { |
| out.write("</table>\n"); |
| if (group != null) |
| { |
| out.write("</subsection>\n"); |
| } |
| } |
| } |
| |
| static private String _getDisplayType(String className, String name, String type) |
| { |
| if (type.startsWith("java.lang.")) |
| { |
| return type.substring("java.lang.".length()); |
| } |
| else if ("binding".equals(name)) |
| { |
| StringTokenizer tokens = new StringTokenizer(className, ".", true); |
| String out = ""; |
| while (tokens.hasMoreTokens()) |
| { |
| String token = tokens.nextToken(); |
| out = out + token; |
| // Give ourselves an opportunity for a line break after "component."; |
| if (out.endsWith("component.")) |
| { |
| out = out + "<wbr/>"; |
| } |
| } |
| |
| return out; |
| } |
| |
| return type; |
| } |
| |
| private void _writeComponentEvents(Writer out, ComponentBean bean) throws IOException |
| { |
| out.write("<table>\n"); |
| out.write("<tr>\n"); |
| out.write("<th>Type</th>\n"); |
| out.write("<th>Phases</th>\n"); |
| out.write("<th>Description</th>\n"); |
| out.write("</tr>\n"); |
| |
| Iterator iter = bean.events(true); |
| while (iter.hasNext()) |
| { |
| EventRefBean eventRef = (EventRefBean) iter.next(); |
| EventBean event = eventRef.resolveEventType(); |
| |
| out.write("<tr>\n"); |
| out.write("<td>" + event.getEventClass() + "</td>"); |
| out.write("<td nowrap=\"nowrap\">"); |
| String[] phases = eventRef.getEventDeliveryPhases(); |
| for (int i = 0; i < phases.length; i++) |
| { |
| if (i > 0) |
| { |
| out.write(",<br/>"); |
| } |
| out.write((String) phases[i]); |
| } |
| |
| out.write("</td>"); |
| out.write("<td>" + event.getDescription() + "</td>"); |
| out.write("</tr>\n"); |
| } |
| |
| out.write("</table>\n"); |
| } |
| |
| |
| |
| |
| |
| private void _writeComponentFacets(Writer out, ComponentBean bean) throws IOException |
| { |
| // Sort the facets |
| TreeSet facetNames = new TreeSet(); |
| Iterator iter = bean.facets(true); |
| while (iter.hasNext()) |
| { |
| facetNames.add(((FacetBean) iter.next()).getFacetName()); |
| } |
| |
| out.write("<table>\n"); |
| out.write("<tr>\n"); |
| out.write("<th>Name</th>\n"); |
| out.write("<th>Description</th>\n"); |
| out.write("</tr>\n"); |
| |
| Iterator nameIter = facetNames.iterator(); |
| while (nameIter.hasNext()) |
| { |
| String name = (String) nameIter.next(); |
| FacetBean facet = bean.findFacet(name, true); |
| out.write("<tr>\n"); |
| out.write("<td>" + facet.getFacetName() + "</td>"); |
| out.write("<td>"); |
| out.write(facet.getDescription()); |
| out.write("</td>\n"); |
| out.write("</tr>\n"); |
| } |
| |
| out.write("</table>\n"); |
| } |
| |
| |
| |
| |
| protected MavenProject getProject() |
| { |
| return project; |
| } |
| |
| protected String getOutputDirectory() |
| { |
| return outputDirectory.getAbsolutePath(); |
| } |
| |
| protected SiteRenderer getSiteRenderer() |
| { |
| return siteRenderer; |
| } |
| |
| public String getName( Locale locale ) |
| { |
| return "JSF Tag Documentation"; |
| } |
| |
| public String getDescription( Locale locale ) |
| { |
| return "Documentation for JSF Tags"; |
| } |
| |
| public String getOutputName() |
| { |
| return "tagdoc"; |
| } |
| |
| protected void processIndex( |
| MavenProject project, |
| String resourcePath) throws MavenReportException |
| { |
| _facesConfig = new FacesConfigBean(); |
| |
| URL[] index = readIndex(project); |
| for (int i=0; i < index.length; i++) |
| { |
| processIndexEntry(index[i]); |
| } |
| } |
| |
| protected void processIndexEntry( |
| URL entry) throws MavenReportException |
| { |
| try |
| { |
| new FacesConfigParser().merge(_facesConfig, entry); |
| } |
| catch (MojoExecutionException e) |
| { |
| throw new MavenReportException("Couldn't parse faces config",e); |
| } |
| } |
| |
| protected FacesConfigBean getFacesConfig() |
| { |
| return _facesConfig; |
| } |
| |
| |
| protected List getMasterConfigs( |
| MavenProject project) throws MavenReportException |
| { |
| String resourcePath = "META-INF/maven-faces-plugin/faces-config.xml"; |
| return getCompileDependencyResources(project, resourcePath); |
| } |
| |
| protected List getCompileDependencyResources( |
| MavenProject project, |
| String resourcePath) throws MavenReportException |
| { |
| try |
| { |
| ClassLoader cl = createCompileClassLoader(project); |
| Enumeration e = cl.getResources(resourcePath); |
| List urls = new ArrayList(); |
| while (e.hasMoreElements()) |
| { |
| URL url = (URL)e.nextElement(); |
| urls.add(url); |
| } |
| return Collections.unmodifiableList(urls); |
| } |
| catch (IOException e) |
| { |
| throw new MavenReportException("Unable to get resources for path " + |
| "\"" + resourcePath + "\"", e); |
| } |
| |
| } |
| |
| protected URL[] readIndex( |
| MavenProject project) throws MavenReportException |
| { |
| try |
| { |
| // 1. read master faces-config.xml resources |
| List masters = getMasterConfigs(project); |
| if (masters.isEmpty()) |
| { |
| getLog().warn("Master faces-config.xml not found"); |
| return new URL[0]; |
| } |
| else |
| { |
| List entries = new LinkedList(); |
| |
| SAXParserFactory spf = SAXParserFactory.newInstance(); |
| spf.setNamespaceAware(true); |
| // requires JAXP 1.3, in JavaSE 5.0 |
| // spf.setXIncludeAware(false); |
| |
| for (Iterator i=masters.iterator(); i.hasNext();) |
| { |
| URL url = (URL)i.next(); |
| Digester digester = new Digester(spf.newSAXParser()); |
| digester.setNamespaceAware(true); |
| |
| // XInclude |
| digester.setRuleNamespaceURI(XIncludeFilter.XINCLUDE_NAMESPACE); |
| digester.addCallMethod("faces-config/include", "add", 1); |
| digester.addFactoryCreate("faces-config/include", |
| URLCreationFactory.class); |
| digester.addCallParam("faces-config/include", 0, 0); |
| |
| digester.push(url); |
| digester.push(entries); |
| digester.parse(url.openStream()); |
| } |
| |
| return (URL[])entries.toArray(new URL[0]); |
| } |
| } |
| catch (ParserConfigurationException e) |
| { |
| throw new MavenReportException("Failed to parse master config", e); |
| } |
| catch (SAXException e) |
| { |
| throw new MavenReportException("Failed to parse master config", e); |
| } |
| catch (IOException e) |
| { |
| throw new MavenReportException("Failed to parse master config", e); |
| } |
| } |
| |
| private ClassLoader createCompileClassLoader( |
| MavenProject project) throws MavenReportException |
| { |
| Thread current = Thread.currentThread(); |
| ClassLoader cl = current.getContextClassLoader(); |
| |
| try |
| { |
| List classpathElements = project.getCompileClasspathElements(); |
| if (!classpathElements.isEmpty()) |
| { |
| String[] entries = (String[])classpathElements.toArray(new String[0]); |
| URL[] urls = new URL[entries.length]; |
| for (int i=0; i < urls.length; i++) |
| { |
| urls[i] = new File(entries[i]).toURL(); |
| } |
| cl = new URLClassLoader(urls, cl); |
| } |
| } |
| catch (DependencyResolutionRequiredException e) |
| { |
| throw new MavenReportException("Error calculating scope classpath", e); |
| } |
| catch (MalformedURLException e) |
| { |
| throw new MavenReportException("Error calculating scope classpath", e); |
| } |
| |
| return cl; |
| } |
| |
| static public class URLCreationFactory extends AbstractObjectCreationFactory |
| { |
| public Object createObject( |
| Attributes attributes) throws MalformedURLException |
| { |
| String href = attributes.getValue("href"); |
| if (href == null) |
| { |
| throw new IllegalStateException("Missing href attribute"); |
| } |
| |
| URL master = (URL)digester.getRoot(); |
| return new URL(master, href); |
| } |
| } |
| |
| static protected class SkipFilter extends ComponentFilter |
| { |
| protected boolean accept( |
| ComponentBean component) |
| { |
| String componentType = component.getComponentType(); |
| |
| // always skip API and base class generation |
| return (!componentType.startsWith("javax") && |
| !componentType.endsWith("Base")); |
| } |
| } |
| |
| |
| |
| private class ComponentNamespaceFilter extends ComponentFilter |
| { |
| public ComponentNamespaceFilter() |
| { |
| } |
| |
| protected boolean accept( |
| ComponentBean component) |
| { |
| if (component.getTagName() == null) |
| { |
| return false; |
| } |
| |
| return taglibs.containsValue(component.getTagName().getNamespaceURI()); |
| } |
| } |
| |
| private class ValidatorNamespaceFilter extends ValidatorFilter |
| { |
| public ValidatorNamespaceFilter() |
| { |
| } |
| |
| protected boolean accept( |
| ValidatorBean component) |
| { |
| if (component.getTagName() == null) |
| { |
| return false; |
| } |
| |
| return taglibs.containsValue(component.getTagName().getNamespaceURI()); |
| } |
| } |
| |
| private class ConverterNamespaceFilter extends ConverterFilter |
| { |
| public ConverterNamespaceFilter() |
| { |
| } |
| |
| protected boolean accept( |
| ConverterBean component) |
| { |
| if (component.getTagName() == null) |
| { |
| return false; |
| } |
| |
| return taglibs.containsValue(component.getTagName().getNamespaceURI()); |
| } |
| } |
| |
| |
| |
| static final protected class TagAttributeFilter extends PropertyFilter |
| { |
| protected boolean accept( |
| PropertyBean property) |
| { |
| return (!property.isTagAttributeExcluded()); |
| } |
| } |
| |
| static final protected class ComponentTagFilter extends ComponentFilter |
| { |
| protected boolean accept( |
| ComponentBean component) |
| { |
| return (component.getTagName() != null); |
| } |
| } |
| |
| static final protected class ConverterTagFilter extends ConverterFilter |
| { |
| protected boolean accept( |
| ConverterBean converter) |
| { |
| return (converter.getTagClass() != null); |
| } |
| } |
| |
| static final protected class ValidatorTagFilter extends ValidatorFilter |
| { |
| protected boolean accept( |
| ValidatorBean validator) |
| { |
| return (validator.getTagClass() != null); |
| } |
| } |
| |
| |
| private FacesConfigBean _facesConfig; |
| |
| |
| |
| // todo: make this configurable? |
| private boolean _attrDocSpansColumns = false; |
| |
| /** |
| * Specifies the directory where the report will be generated |
| * |
| * @parameter default-value="${project.reporting.outputDirectory}" |
| * @required |
| */ |
| private File outputDirectory; |
| |
| |
| /** |
| * Directory where the original site is present. |
| * (TRIED using ${baseDir}/src/site; that inserted a 'null' into |
| * the string for some reason. TRIED using ${siteDirectory}, |
| * which was undefined. TRIED ${project.directory}src/site; which also |
| * inserted a null. ${project.build.directory}/../src/site seems to work, |
| * though it assumes that ${project.build.directory} is |
| * ${project.directory}/target. |
| * |
| * @parameter default-value="${project.build.directory}/../src/site/" |
| * @required |
| */ |
| private File siteDirectory; |
| |
| /** |
| * @parameter expression="${project}" |
| * @required |
| * @readonly |
| */ |
| private MavenProject project; |
| |
| /** |
| * @parameter |
| * @required |
| */ |
| private Map taglibs; |
| |
| /** |
| * @parameter expression="META-INF/maven-faces-plugin/faces-config.xml" |
| * @required |
| * @readonly |
| */ |
| private String resourcePath; |
| |
| |
| /** |
| * @component |
| * @required |
| * @readonly |
| */ |
| private SiteRenderer siteRenderer; |
| |
| static private final String _DOC_SUBDIRECTORY = "tagdoc"; |
| static private final String[] _NON_DOCUMENTED_AGENTS = {"phone", "voice"}; |
| |
| } |