Package org.infinispan.tools.doclet.config

Source Code of org.infinispan.tools.doclet.config.AbstractConfigHtmlGenerator

/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.tools.doclet.config;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.infinispan.config.ConfigurationDoc;
import org.infinispan.config.ConfigurationDocRef;
import org.infinispan.config.ConfigurationDocs;
import org.infinispan.tools.doclet.html.HtmlGenerator;
import org.infinispan.tools.schema.TreeNode;
import org.infinispan.tools.schema.XSOMSchemaTreeWalker;
import org.infinispan.util.ReflectionUtil;

import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.RootDoc;
import com.sun.xml.xsom.XSAttributeDecl;
import com.sun.xml.xsom.XSFacet;
import com.sun.xml.xsom.XSRestrictionSimpleType;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.parser.XSOMParser;

@SuppressWarnings("restriction")
public abstract class AbstractConfigHtmlGenerator extends HtmlGenerator {

   protected static final String CONFIG_REF = "configRef";

   protected static final String CONFIG_REF_NAME_ATT = "name";
   protected static final String CONFIG_REF_PARENT_NAME_ATT = "parentName";
   protected static final String CONFIG_REF_DESC_ATT = "desc";

   private static final int LEVEL_MULT = 3;
   private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("infinispan.tools.configdoc.debug", "false"));

   protected RootDoc rootDoc;
   protected StringBuilder sb;

   public AbstractConfigHtmlGenerator(String encoding, String title, String bottom, String footer,
            String header, String metaDescription, List<String> metaKeywords) {
      super(encoding, title, bottom, footer, header, metaDescription, metaKeywords);
      sb = new StringBuilder();
   }

   protected abstract List<Class<?>> getConfigBeans() throws Exception;

   /**
    * Returns name of the schema file.
    *
    * <p>
    * Note that schema file should be placed on a classpath.
    *
    * @return name of the schema file located on the classpath.
    */
   protected abstract String getSchemaFile();

   /**
    * Name of the root element in the schema
    *
    * @return name of the root element in the schema
    */
   protected abstract String getRootElementName();

   /**
    * Invoked prior to creation of XML tree table of contents for configuration elements in schema
    *
    * @param sw
    * @param tw
    */
   protected void preXMLTableOfContentsCreate(XSOMSchemaTreeWalker sw, XMLTreeOutputWalker tw) {

   }

   /**
    * Invoked after creation of XML tree table of contents for configuration elements in schema
    *
    * @param sw
    * @param tw
    */
   protected void postXMLTableOfContentsCreate(XSOMSchemaTreeWalker w, XMLTreeOutputWalker tw) {

   }

   /**
    * Callback invoked prior to visiting the specified node n.
    *
    * @param n
    * @return true if the TreeNode n should be skipped for configuration reference creation, false
    *         otherwise
    */
   protected boolean preVisitNode(TreeNode n) {
      return true;
   }

   /**
    * Callback invoked after visiting the specified node n.
    *
    * @param n
    * @return true if no more elements should be included in configuration reference creation, false
    *         otherwise
    */
   protected boolean postVisitNode(TreeNode n) {
      return false;
   }

   protected String getTitle() {
      return "<h2>Configuration reference</h2><br/>";
   }

   public RootDoc getRootDoc() {
      return rootDoc;
   }

   public void setRootDoc(RootDoc rootDoc) {
      this.rootDoc = rootDoc;
   }

   public InputStream lookupFile(String filename) {
      InputStream is = filename == null || filename.length() == 0 ? null
               : getAsInputStreamFromClassLoader(filename);
      if (is == null) {
         try {
            is = new FileInputStream(filename);
         } catch (FileNotFoundException e) {
            return null;
         }
      }
      return is;
   }

   protected InputStream getAsInputStreamFromClassLoader(String filename) {
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      InputStream is = cl == null ? null : cl.getResourceAsStream(filename);
      if (is == null) {
         // check system class loader
         is = getClass().getClassLoader().getResourceAsStream(filename);
      }
      return is;
   }

   protected StringBuilder getStringBuilder() {
      return sb;
   }

   protected String generateContents() {
      sb.append(getTitle());

      List<Class<?>> configBeans;
      try {
         configBeans = getConfigBeans();

         if (configBeans == null || configBeans.isEmpty())
            throw new Exception(
                     "Configuration bean classes are not specified. Make sure that "
                              + "getConfigBeans() method returns a list of classes. Documentation creation aborted");

         XMLTreeOutputWalker tw = new XMLTreeOutputWalker(sb);
         String schemaFile = getSchemaFile();

         if (schemaFile == null)
            throw new Exception("Schema file name not specified. Documentation creation aborted");

         InputStream file = lookupFile(schemaFile);

         if (file == null)
            throw new Exception("Schema file " + schemaFile
                     + " not found on classpath. Documentation creation aborted");

         XSOMParser reader = new XSOMParser();
         reader.parse(file);
         XSSchemaSet xss = reader.getResult();
         XSOMSchemaTreeWalker w = new XSOMSchemaTreeWalker(xss.getSchema(1), getRootElementName());
         TreeNode root = w.getRoot();
         associateBeansWithTreeNodes(configBeans, root);

         preXMLTableOfContentsCreate(w, tw);

         sb.append("<div class=\"" + "source" + "\"><pre>");
         // print XMLTableOfContents into StringBuilder
         tw.preOrderTraverse(root);
         sb.append("</pre></div>");

         postXMLTableOfContentsCreate(w, tw);

         for (TreeNode n : root) {

            boolean skip = preVisitNode(n);
            // do not generate element for skipped element
            if (skip)
               continue;

            sb.append("<div class=\"section\">\n");

            debug("Generating " + n + " bean is " + n.getBeanClass());

            // Name, description, parent and child elements for node
            generateHeaderForConfigurationElement(sb, tw, n);

            // now attributes
            if (!n.getAttributes().isEmpty()) {
               generateAttributeTableRows(sb, n);
            }

            boolean breakLoop = postVisitNode(n);
            sb.append("</div>\n");
            if (breakLoop)
               break;
         }

      } catch (Exception e) {
         System.out.println("Exception while generating configuration reference " + e);
         e.printStackTrace();
      }
      return sb.toString();
   }

   private void associateBeansWithTreeNodes(List<Class<?>> configBeans, TreeNode root) {
      for (TreeNode n : root) {
         if (n.getBeanClass() == null) {
            for (Class<?> clazz : configBeans) {
               if (clazz.isAnnotationPresent(ConfigurationDoc.class)) {
                  associate(clazz.getAnnotation(ConfigurationDoc.class), n, clazz);
               } else if (clazz.isAnnotationPresent(ConfigurationDocs.class)) {
                  ConfigurationDoc[] docs = clazz.getAnnotation(ConfigurationDocs.class).value();
                  for (ConfigurationDoc cd : docs) {
                     associate(cd, n, clazz);
                  }
               }
            }
         }
      }
   }

   private void associate(ConfigurationDoc cd, TreeNode n, Class<?> clazz) {
      if (cd != null) {
         String thisNode = cd.name();
         String parentNode = cd.parentName();
         if (n.getName().equalsIgnoreCase(thisNode)) {
            if (parentNode.equalsIgnoreCase(n.getParent().getName())) {
               debug("Parent associated " + clazz + " with node " + n.getName() + ", parent " + parentNode);
               n.setBeanClass(clazz);
            } else if (parentNode.length() == 0) {
               debug("Normal associated " + clazz + " with node " + n.getName());
               n.setBeanClass(clazz);
            }
         }
      }
   }

   private void generateAttributeTableRows(StringBuilder sb, TreeNode n) {

      sb.append("<table class=\"bodyTable\"> ");
      sb.append("<tr class=\"a\"><th>Attribute</th><th>Type</th><th>Default</th><th>Description</th></tr>\n");

      Class<?> bean = n.getBeanClass();
      Object object = null;
      try {
         Constructor<?>[] constructors = bean.getDeclaredConstructors();
         for (Constructor<?> c : constructors) {
            if (c.getParameterTypes().length == 0) {
               c.setAccessible(true);
               object = c.newInstance();
            }
         }
      } catch (Exception e) {
         System.out.println("Did not construct object " + bean);
      }

      Set<XSAttributeDecl> attributes = n.getAttributes();
      for (XSAttributeDecl a : attributes) {
         sb.append("<tr class=\"b\">");

         // name, type...
         sb.append("<td>").append("<code>" + a.getName() + "</code>").append("</td>\n");
         sb.append("<td>").append("<code>" + a.getType().getName() + "</code>");

         boolean isRestricted = false;
         XSRestrictionSimpleType restriction = a.getType().asRestriction();
         Collection<? extends XSFacet> declaredFacets = restriction.getDeclaredFacets();
         for (XSFacet facet : declaredFacets) {
            if (facet.getName().equalsIgnoreCase("enumeration")) {
               isRestricted = true;
               break;
            }
         }

         debug("attribute = " + a.getName() + "(restricted = " + isRestricted + ")", 1);

         // restriction on type...
         if (isRestricted) {
            sb.append("* (");
            for (XSFacet facet : declaredFacets) {
               sb.append(facet.getValue().toString() + '|');
            }
            sb.deleteCharAt(sb.length() - 1);
            sb.append(")</td>\n");
         } else {
            sb.append("</td>\n");
         }

         Field field = findField(bean, a.getName());
         if (field == null) {
            throw new RuntimeException("Null field for " + bean.getName() + " attribute "
                     + a.getName());
         }

         // if default value specified in annotation use it
         if (a.getDefaultValue() != null) {
            debug("annotation-defined default = " + a.getDefaultValue(), 2);
            sb.append("<td>").append(a.getDefaultValue().toString()).append("</td>\n");
         }

         // otherwise use reflected field and read default value
         else {
            Object defaultValue = null;
            try {
               defaultValue = ReflectionUtil.getValue(object, field.getName());
               if (defaultValue != null) {
                  sb.append("<td>").append(defaultValue.toString()).append("</td>\n");
                  debug("field-defined default = " + defaultValue, 2);
               } else {
                  debug("field-defined default is null!", 2);
                  sb.append("<td>").append("null").append("</td>\n");
               }
            } catch (Exception e) {
               debug("Caught exception, bean is " + bean.getName() + ", looking for field "
                        + a.getName() + ", field " + field, 2);
               e.printStackTrace();
               sb.append("<td>").append("N/A").append("</td>\n");
            }
         }

         // and finally description
         String desc = findAttributeDescription(field);
         if (desc != null) {
            sb.append("<td>").append(desc).append("\n");
            String htmlFile = field.getDeclaringClass().getName().replace(".", "/").replace("$",".").concat(".html");
            sb.append(" (<a href=\"" + htmlFile.concat("#").concat(field.getName()) + "\">"+ "Javadoc</a>)");
            sb.append("</td>\n");
         }
         sb.append("</tr>\n");
      }
      sb.append("</table>\n");
   }

   private void debug(String s, int level) {
      if (DEBUG) {
         StringBuilder sb = new StringBuilder();
         for (int i = 0; i < level * LEVEL_MULT; i++)
            sb.append(" ");
         sb.append("> ").append(s);
         System.out.println(sb.toString());
      }
   }

   private void debug(String s) {
      debug(s, 0);
   }

   private boolean validString(String s) {
      return s != null && s.length() > 0;
   }

   protected void generateHeaderForConfigurationElement(StringBuilder sb, XMLTreeOutputWalker tw,
            TreeNode n) {
      sb.append("<a name=\"").append("ce_" + n.getParent().getName() + "_" + n.getName() + "\">" + "</a>");
      sb.append("<h3><a name=\"" + n.getName() + "\"></a>" + n.getName() + "</h3>");
      sb.append("\n<p>");
      Class<?> beanClass = n.getBeanClass();
      Map<String, String> desc = findDescription(beanClass);
      if (!desc.isEmpty()) {
         for (Entry<String, String> e : desc.entrySet()) {
            if (n.getName().equals(e.getKey())) {
               sb.append(e.getValue());
            }
         }
      }

      sb.append("<BR/><BR />");

      if (n.getParent().getParent() != null) {
         sb.append("The parent element is " + "<a href=\"").append("#ce_"
                  + n.getParent().getParent().getName() + "_" + n.getParent().getName()
                           + "\">" + "&lt;" + n.getParent().getName() + "&gt;" + "</a>.  ");
      }

      if (!n.getChildren().isEmpty()) {
         int childCount = n.getChildren().size();
         int count = 1;
         if (childCount == 1)
            sb.append("The only child element is ");
         else
            sb.append("Child elements are ");
         for (TreeNode tn : n.getChildren()) {
            sb.append("<a href=\"").append("#ce_" + tn.getParent().getName() +
                     "_" + tn.getName() + "\">" + "&lt;" + tn.getName() + "&gt;" + "</a>");
            if (count < childCount) {
               sb.append(", ");
            } else {
               sb.append(".");
            }
            count++;
         }
         sb.append("\n");
      }
      sb.append("</p>");
   }

   protected String findAttributeDescription(Field field) {
      String desc = null;
      if (field.isAnnotationPresent(ConfigurationDoc.class)) {
         desc = field.getAnnotation(ConfigurationDoc.class).desc();
      } else if (field.isAnnotationPresent(ConfigurationDocRef.class)) {
         ConfigurationDocRef docRef = field.getAnnotation(ConfigurationDocRef.class);
         Doc fieldDocRec = findDocElement(docRef.bean(), docRef.targetElement());
         desc = fieldDocRec.commentText();
      }
      return desc;
   }

   protected Field findField(Class<?> clazz, String name) {
      Field f = null;
      boolean found = false;
      Class<?> current = clazz;
      while (current != null) {
         try {
            f = current.getDeclaredField(name);
            return f;
         } catch (NoSuchFieldException e) {
            current = current.getSuperclass();
         }
      }

      List<Field> anFields = ReflectionUtil.getAnnotatedFields(clazz, ConfigurationDoc.class);
      for (Field field : anFields) {
         if (field.getAnnotation(ConfigurationDoc.class).name().equals(name)) {
            f = field;
            found = true;
            break;
         }
      }

      // or with ConfigurationDocReference....
      if (!found) {
         anFields = ReflectionUtil.getAnnotatedFields(clazz, ConfigurationDocRef.class);
         for (Field field : anFields) {
            if (field.getAnnotation(ConfigurationDocRef.class).name().equals(name)) {
               f = field;
               break;
            }
         }
      }
      return f;
   }

   protected Map<String, String> findDescription(AnnotatedElement e) {
      Map<String, String> m = new HashMap<String, String>();
      ConfigurationDoc cd = e.getAnnotation(ConfigurationDoc.class);
      if (cd != null) {
         extractConfigurationDocComments(e, m, cd);
      } else if (e.isAnnotationPresent(ConfigurationDocs.class)) {
         ConfigurationDoc[] configurationDocs = e.getAnnotation(ConfigurationDocs.class).value();
         for (ConfigurationDoc cd2 : configurationDocs) {
            extractConfigurationDocComments(e, m, cd2);
         }
      }
      return m;
   }

   protected void extractConfigurationDocComments(AnnotatedElement e, Map<String, String> m,
            ConfigurationDoc cd) {
      if (cd != null) {
         if (validString(cd.desc())) {
            m.put(cd.name(), cd.desc());
         } else {
            if (e instanceof Class<?>) {
               Class<?> clazz = (Class<?>) e;
               ClassDoc classDoc = rootDoc.classNamed(clazz.getName());
               m.put(cd.name(), classDoc.commentText());
            }
         }
      }
   }

   protected Doc findDocElement(Class<?> c, String elementName) {
      while (true) {
         ClassDoc classDoc = rootDoc.classNamed(c.getName());
         for (MethodDoc md : classDoc.methods()) {
            if (md.name().equalsIgnoreCase(elementName)) {
               return md;
            }
         }
         for (FieldDoc fd : classDoc.fields()) {
            if (fd.name().equalsIgnoreCase(elementName)) {
               return fd;
            }
         }
         if (c.getSuperclass() != null) {
            c = c.getSuperclass();
            continue;
         }
         return null;
      }
   }
}
TOP

Related Classes of org.infinispan.tools.doclet.config.AbstractConfigHtmlGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.