TRINIDAD-1679 Tag doc to list relationship between tags
Thanks to Maria Kaval for the patch
diff --git a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/ComponentBean.java b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/ComponentBean.java
index 80042fb..c67a6ed 100644
--- a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/ComponentBean.java
+++ b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/ComponentBean.java
@@ -20,9 +20,12 @@
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
@@ -716,6 +719,147 @@
}
/**
+ * Parses the string of satisfied contracts into a String array
+ * using space as the separator between values.
+ * In the component metadata file, the satisfied contracts are noted
+ * with satisfied-contracts markup. As an example, af:popup
+ * (oracle.adf.view.rich.component.rich.RichPopup) supports
+ * oracle-adf-richmenu-holder, oracle-adf-richdialog-holder,
+ * oracle-adf-richnotewindow-holder, and oracle-adf-richpanelwindow-holder
+ * contracts. The satisfied contracts of a given component are matched
+ * with the required ancestor contracts of other components to determine
+ * if a component hierarchy is legally assembled.
+ *
+ * @param satisfiedContracts a space delimited string of satisifed contracts
+ */
+ public void parseSatisfiedContracts(
+ String satisfiedContracts)
+ {
+ setSatisfiedContracts(satisfiedContracts.split(" "));
+ }
+
+ /**
+ * Sets the possible values for this property.
+ * In the component metadata file, the satisfied contracts are noted
+ * with satisfied-contracts markup. As an example, af:popup
+ * (oracle.adf.view.rich.component.rich.RichPopup) supports
+ * oracle-adf-richmenu-holder, oracle-adf-richdialog-holder,
+ * oracle-adf-richnotewindow-holder, and oracle-adf-richpanelwindow-holder
+ * contracts. The satisfied contracts of a given component are matched
+ * with the required ancestor contracts of other components to determine
+ * if a component hierarchy is legally assembled.
+ *
+ * @param satisfiedContracts a string array of the satisfied contracts
+ */
+ public void setSatisfiedContracts(
+ String[] satisfiedContracts)
+ {
+ _satisfiedContracts = Arrays.asList(satisfiedContracts);
+ }
+
+ /**
+ * Returns an iterator of the satisfied contracts for this component.
+ * In the component metadata file, the satisfied contracts are noted
+ * with satisfied-contracts markup. As an example, af:popup
+ * (oracle.adf.view.rich.component.rich.RichPopup) supports
+ * oracle-adf-richmenu-holder, oracle-adf-richdialog-holder,
+ * oracle-adf-richnotewindow-holder, and oracle-adf-richpanelwindow-holder
+ * contracts. The satisfied contracts of a given component are matched
+ * with the required ancestor contracts of other components to determine
+ * if a component hierarchy is legally assembled.
+ *
+ * @return a java.util.Iterator of Strings, where each string is the name of a
+ * satisfied contract
+ */
+ public Iterator<String> satisfiedContracts()
+ {
+ return _satisfiedContracts.iterator();
+ }
+
+ /**
+ * Returns true if this component has any satisfied contracts.
+ *
+ * @return true if this component has any satisfied contracts,
+ * false otherwise
+ */
+ public boolean hasSatisfiedContracts()
+ {
+ return (!_satisfiedContracts.isEmpty());
+ }
+
+ /**
+ * Parses the string of required ancestor contracts into a String array
+ * using space as the separator between values.
+ * In the component metadata file, the required ancestors are noted
+ * with required-ancestor-contracts markup. This indicates that an
+ * ancestor (e.g. parent or grandparent) tag must be have satisfied-contracts
+ * metadata matching the required-ancestor-contracts metadata of this tag.
+ * As an example, af:dialog
+ * (oracle.adf.view.rich.component.rich.RichDialog) lists
+ * oracle-adf-richdialog-holder as a required ancestor contract, and
+ * af:popup (oracle.adf.view.rich.component.rich.RichPopup) lists
+ * oracle-adf-richdialog-holder as a satisified contract.
+ *
+ * @param requiredAncestorContracts a space delimited string of required ancestor contracts
+ */
+ public void parseRequiredAncestorContracts(
+ String requiredAncestorContracts)
+ {
+ setRequiredAncestorContracts(requiredAncestorContracts.split(" "));
+ }
+
+ /**
+ * Sets the possible values for this property.
+ * In the component metadata file, the required ancestors are noted
+ * with required-ancestor-contracts markup. This indicates that an
+ * ancestor (e.g. parent or grandparent) tag must be have satisfied-contracts
+ * metadata matching the required-ancestor-contracts metadata of this tag.
+ * As an example, af:dialog
+ * (oracle.adf.view.rich.component.rich.RichDialog) lists
+ * oracle-adf-richdialog-holder as a required ancestor contract, and
+ * af:popup (oracle.adf.view.rich.component.rich.RichPopup) lists
+ * oracle-adf-richdialog-holder as a satisified contract.
+ *
+ * @param requiredAncestorContracts a string array of the required ancestor contracts
+ */
+ public void setRequiredAncestorContracts(
+ String[] requiredAncestorContracts)
+ {
+ _requiredAncestorContracts = Arrays.asList(requiredAncestorContracts);
+ }
+
+ /**
+ * Returns the required ancestor contracts for this component.
+ * In the component metadata file, the required ancestors are noted
+ * with required-ancestor-contracts markup. This indicates that an
+ * ancestor (e.g. parent or grandparent) tag must be have satisfied-contracts
+ * metadata matching the required-ancestor-contracts metadata of this tag.
+ * As an example, af:dialog
+ * (oracle.adf.view.rich.component.rich.RichDialog) lists
+ * oracle-adf-richdialog-holder as a required ancestor contract, and
+ * af:popup (oracle.adf.view.rich.component.rich.RichPopup) lists
+ * oracle-adf-richdialog-holder as a satisified contract.
+ *
+ * @return a java.util.Iterator of strings, where each string is the name
+ * of a required ancestor contract
+ */
+ public Iterator<String> requiredAncestorContracts()
+ {
+ return _requiredAncestorContracts.iterator();
+ }
+
+ /**
+ * Returns true if this component has any required ancestor contracts.
+ *
+ * @return true if this component has any required ancestor contracts,
+ * false otherwise
+ */
+ public boolean hasRequiredAncestorContracts()
+ {
+ return (!_requiredAncestorContracts.isEmpty());
+ }
+
+ /**
* Adds a Java Language class modifier to the tag class.
*
* @param modifier the modifier to be added
@@ -1043,6 +1187,8 @@
private int _componentClassModifiers;
private int _tagClassModifiers;
private String[] _unsupportedAgents = new String[0];
+ private List<String> _requiredAncestorContracts = new ArrayList<String>();
+ private List<String> _satisfiedContracts = new ArrayList<String>();
static private final String _TRINIDAD_COMPONENT_BASE =
"org.apache.myfaces.trinidad.component.UIXComponentBase";
diff --git a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/FacesConfigParser.java b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/FacesConfigParser.java
index f06a272..720f5f2 100644
--- a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/FacesConfigParser.java
+++ b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/FacesConfigParser.java
@@ -224,6 +224,21 @@
digester.addBeanPropertySetter("faces-config/component/property/property-extension/property-metadata/required");
digester.addBeanPropertySetter("faces-config/component/property/property-extension/property-metadata/value-expression", "valueExpression");
+ // faces-config/component/facet/facet-extension/facet-metadata/allowed-child-components
+ digester.addCallMethod("faces-config/component/facet/facet-extension/facet-metadata/allowed-child-components",
+ "parseAllowedChildComponents", 1);
+ digester.addCallParam("faces-config/component/facet/facet-extension/facet-metadata/allowed-child-components", 0);
+
+ // faces-config/component/component-extension/component-metadata/required-ancestor-contracts
+ digester.addCallMethod("faces-config/component/component-extension/component-metadata/required-ancestor-contracts",
+ "parseRequiredAncestorContracts", 1);
+ digester.addCallParam("faces-config/component/component-extension/component-metadata/required-ancestor-contracts", 0);
+
+ // faces-config/component/component-extension/component-metadata/satisfied-contracts
+ digester.addCallMethod("faces-config/component/component-extension/component-metadata/satisfied-contracts",
+ "parseSatisfiedContracts", 1);
+ digester.addCallParam("faces-config/component/component-extension/component-metadata/satisfied-contracts", 0);
+
// XInclude rules
digester.setRuleNamespaceURI(XIncludeFilter.XINCLUDE_NAMESPACE);
digester.addFactoryCreate("faces-config/component/include",
diff --git a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/FacetBean.java b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/FacetBean.java
index feb41ed..b01fc48 100644
--- a/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/FacetBean.java
+++ b/maven-faces-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/faces/parse/FacetBean.java
@@ -19,6 +19,7 @@
package org.apache.myfaces.trinidadbuild.plugin.faces.parse;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -143,9 +144,75 @@
return _accessibilityGuidelines.iterator();
}
+ /**
+ * Parses the string of allowed child class names into a String array
+ * using space as the separator between values.
+ * In the component metadata file, the allowed child components
+ * are denoted with allowed-child-components markup. As an example,
+ * oracle.adf.RichMenu is an allowed child component of
+ * af:panelCollection's (oracle.adf.view.rich.component.rich.output.RichPanelCollection)
+ * menu facet. The allowed child components of one component are matched with the
+ * component type attribute of other components to determine which child tags
+ * are allowed in the component hierarchy.
+ *
+ * @param allowedChildComponents a space delimited string of allowed child component class names
+ */
+ public void parseAllowedChildComponents(
+ String allowedChildComponents)
+ {
+ setAllowedChildComponents(allowedChildComponents.split(" "));
+ }
+
+ /**
+ * Sets the possible values for this property.
+ * In the component metadata file, the allowed child components
+ * are denoted with allowed-child-components markup. As an example,
+ * oracle.adf.RichMenu is an allowed child component of
+ * af:panelCollection's (oracle.adf.view.rich.component.rich.output.RichPanelCollection)
+ * menu facet. The allowed child components of one component are matched with the
+ * component type attribute of other components to determine which child tags
+ * are allowed in the component hierarchy.
+ *
+ * @param allowedChildComponents a string array of the allowed child component class names
+ */
+ public void setAllowedChildComponents(
+ String[] allowedChildComponents)
+ {
+ _allowedChildComponents = Arrays.asList(allowedChildComponents);
+ }
+
+ /**
+ * Returns the allowed child components for this facet.
+ * In the component metadata file, the allowed child components
+ * are denoted with allowed-child-components markup. As an example,
+ * oracle.adf.RichMenu is an allowed child component of
+ * af:panelCollection's (oracle.adf.view.rich.component.rich.output.RichPanelCollection)
+ * menu facet. The allowed child components of one component are matched with the
+ * component type attribute of other components to determine which child tags
+ * are allowed in the component hierarchy.
+ *
+ * @return an iterator of allowed child component class name strings
+ */
+ public Iterator<String> allowedChildComponents()
+ {
+ return (_allowedChildComponents.iterator());
+ }
+
+ /**
+ * Returns true if this component has any allowed child components.
+ *
+ * @return true if this component has any allowed child components,
+ * false otherwise
+ */
+ public boolean hasAllowedChildComponents()
+ {
+ return (!_allowedChildComponents.isEmpty());
+ }
+
private String _description;
private String _facetName;
private boolean _required;
private boolean _hidden;
private List<String> _accessibilityGuidelines = new ArrayList<String>();
+ private List<String> _allowedChildComponents = new ArrayList<String>();
}
diff --git a/maven-tagdoc-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/tagdoc/TagdocReport.java b/maven-tagdoc-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/tagdoc/TagdocReport.java
index 1d2b9e5..421349b 100644
--- a/maven-tagdoc-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/tagdoc/TagdocReport.java
+++ b/maven-tagdoc-plugin/src/main/java/org/apache/myfaces/trinidadbuild/plugin/tagdoc/TagdocReport.java
@@ -31,6 +31,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -115,16 +116,77 @@
return;
}
-
- Iterator components = facesConfig.components();
+ // Need to cycle through the components two times, hence need two iterators.
+ // components Iterator will be used when actually writing out the tag doc
+ // compIter Iterator will be used when creating the maps of component relationships
+ Iterator<ComponentBean> components = facesConfig.components();
components = new FilteredIterator(components, new SkipFilter());
components = new FilteredIterator(components, new ComponentTagFilter());
components = new FilteredIterator(components, new ComponentNamespaceFilter());
+ Iterator<ComponentBean> compIter = facesConfig.components();
+ compIter = new FilteredIterator(compIter, new SkipFilter());
+ compIter = new FilteredIterator(compIter, new ComponentTagFilter());
+ compIter = new FilteredIterator(compIter, new ComponentNamespaceFilter());
+
+ // compTypeMap holds a map of compononent types to tag names that implement that component type
+ // The map is built using getComponentType method on the component bean to determine the
+ // component type of a given tag name
+ Map<String, List<QName>> compTypeMap = new HashMap<String, List<QName>> ();
+ // contractMap holds a map of contract name to tag names that satisify that contract.
+ // The map is built using the getSatisfiedContracts method API on the component bean to determine
+ // which contracts are satisfied for a given tagname
+ Map<String, List<QName>> contractMap = new HashMap<String, List<QName>>();
+ while (compIter.hasNext())
+ {
+ ComponentBean compBean = compIter.next();
+ List<QName> tagNames;
+ String compType = compBean.getComponentType();
+ if (compType != null &&
+ compTypeMap.containsKey (compType) &&
+ compBean.getTagName() != null)
+ {
+ // the component type map already contains an entry for this component type
+ tagNames = compTypeMap.get(compType);
+ }
+ else
+ {
+ // the component type map does not contain an entry for this component type
+ // so create a new ArrayList that will be used to store the tag names of
+ // component that have this component type
+ tagNames = new ArrayList<QName>();
+ }
+ tagNames.add(compBean.getTagName());
+ compTypeMap.put (compType, tagNames);
+
+ if (compBean.hasSatisfiedContracts())
+ {
+ Iterator<String> satContractsIter = compBean.satisfiedContracts();
+ while (satContractsIter.hasNext())
+ {
+ String satContract = satContractsIter.next();
+ if (contractMap.containsKey (satContract))
+ {
+ // the contract map already contains an entry for this contract
+ tagNames = contractMap.get(satContract);
+ }
+ else
+ {
+ // the contract map does not contain an entry for this contract, so
+ // create a new ArrayList which will be used to store the tag names of
+ // components that satisfy this contract
+ tagNames = new ArrayList<QName>();
+ }
+ tagNames.add(compBean.getTagName());
+ contractMap.put (satContract, tagNames);
+ }
+ }
+ }
+
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());
@@ -147,7 +209,7 @@
int count = 0;
while (components.hasNext())
{
- String pageName = _generateComponentDoc((ComponentBean)components.next());
+ String pageName = _generateComponentDoc((ComponentBean)components.next(), compTypeMap, contractMap);
if (pageName != null)
{
componentPages.add(pageName);
@@ -246,6 +308,53 @@
return set;
}
+ private String _formatTagList(
+ Iterator<String> strIter,
+ Map <String, List<QName>> pMap,
+ String header)
+ {
+ String formatted = null;
+
+ // Don't know how long this will be, but 300 should be plenty.
+ StringBuffer sb = new StringBuffer(300);
+ sb.append("\n");
+ sb.append("<b>");
+ sb.append(header);
+ sb.append(":</b> ");
+
+ boolean gotOne = false;
+ while (strIter.hasNext())
+ {
+ List<QName> tagNameList = pMap.get(strIter.next());
+ if (tagNameList != null && !tagNameList.isEmpty())
+ {
+ Iterator<QName> tagNameIter = tagNameList.iterator();
+
+ while (tagNameIter.hasNext())
+ {
+ QName tagName = tagNameIter.next();
+
+ String tagdocURL = _platformAgnosticPath("../tagdoc/" +
+ _toPageName(tagName) + ".html");
+ sb.append("<a href=\"" + tagdocURL + "\">");
+ sb.append(_getQualifiedName(tagName));
+ sb.append("</a>");
+ if (gotOne)
+ {
+ sb.append(", ");
+ }
+ gotOne = true;
+ }
+ }
+ if (gotOne)
+ {
+ sb.append("<br/>\n");
+ formatted = sb.toString();
+ }
+ }
+ return formatted;
+ }
+
private String _formatPropList(
String[] pList,
String header)
@@ -372,7 +481,7 @@
return "unknown";
}
- private String _generateComponentDoc(ComponentBean component)
+ private String _generateComponentDoc(ComponentBean component, Map<String, List<QName>> compTypeMap, Map <String, List<QName>> contractMap)
throws Exception
{
if (component.getTagName() == null)
@@ -400,7 +509,7 @@
out.write(" <section name=\"Summary\">\n");
out.write(" <p>\n");
- _writeComponentSummary(out, component);
+ _writeComponentSummary(out, component, contractMap);
out.write(" </p>\n");
out.write(" </section>\n");
@@ -423,7 +532,7 @@
{
out.write(" <section name=\"Supported Facets\">\n");
out.write(" <p>\n");
- _writeComponentFacets(out, component);
+ _writeComponentFacets(out, component, compTypeMap);
out.write(" </p>\n");
out.write(" </section>\n");
}
@@ -546,7 +655,7 @@
}
- private void _writeComponentSummary(Writer out, ComponentBean bean) throws IOException
+ private void _writeComponentSummary(Writer out, ComponentBean bean, Map <String, List<QName>> contractMap) throws IOException
{
out.write(" <b>Tag name:</b> <" +
_getQualifiedName(bean.getTagName()) + ">\n");
@@ -565,6 +674,14 @@
out.write(" <b>Component type:</b> " + bean.getComponentType() + "\n");
out.write(" <br/>\n");
+ if (bean.hasRequiredAncestorContracts())
+ {
+ String formattedAncestors = _formatTagList ( bean.requiredAncestorContracts(),
+ contractMap,
+ "Required Ancestor Tag");
+ out.write (formattedAncestors);
+ }
+
if (_isNamingContainer(bean))
{
out.write(" <p><b>Naming container:</b> Yes. When referring to children of this " +
@@ -1041,14 +1158,14 @@
}
- private void _writeComponentFacets(Writer out, ComponentBean bean) throws IOException
+ private void _writeComponentFacets(Writer out, ComponentBean bean, Map<String, List<QName>> compTypeMap) throws IOException
{
// Sort the facets
TreeSet facetNames = new TreeSet();
- Iterator iter = bean.facets(true);
+ Iterator<FacetBean> iter = bean.facets(true);
while (iter.hasNext())
{
- FacetBean facetBean = (FacetBean)iter.next();
+ FacetBean facetBean = iter.next();
if (!facetBean.isHidden())
{
facetNames.add(facetBean.getFacetName());
@@ -1061,14 +1178,22 @@
out.write("<th>Description</th>\n");
out.write("</tr>\n");
- Iterator nameIter = facetNames.iterator();
+ Iterator<String> nameIter = facetNames.iterator();
while (nameIter.hasNext())
{
- String name = (String) nameIter.next();
+ String name = nameIter.next();
FacetBean facet = bean.findFacet(name, true);
out.write("<tr>\n");
out.write("<td>" + facet.getFacetName() + "</td>");
out.write("<td>");
+
+ if (facet.hasAllowedChildComponents())
+ {
+ String formattedChildComps = _formatTagList (facet.allowedChildComponents(), compTypeMap, "Allowed Child Components");
+ out.write (formattedChildComps);
+ out.write("<br/>");
+ }
+
out.write(facet.getDescription());
out.write("</td>\n");
out.write("</tr>\n");