Package de.kruis.padoclet

Source Code of de.kruis.padoclet.PublishedApiDoclet$ClassDocHandler

/*
*  PublishedApiDoclet - a filter proxy for any javadoc doclet
*  Copyright (C) 2007, 2010  Anselm Kruis <a.kruis@science-computing.de>
*
*  This library 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 library 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 library; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

/*
* This file is based on a public domain source found at
* http://www.sixlegs.com/blog/java/exclude-javadoc-tag.html
*
* Many thanks to Chris Nokleberg for this piece of code.
*/

package de.kruis.padoclet;

import java.lang.reflect.Array;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.sun.javadoc.AnnotationDesc;
import com.sun.javadoc.AnnotationTypeDoc;
import com.sun.javadoc.AnnotationTypeElementDoc;
import com.sun.javadoc.AnnotationValue;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.ConstructorDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.DocErrorReporter;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.LanguageVersion;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.ProgramElementDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.SourcePosition;
import com.sun.javadoc.Tag;
import com.sun.javadoc.Type;
import com.sun.tools.javadoc.Main;

import de.kruis.padoclet.util.AbstractOption;
import de.kruis.padoclet.util.HalfDynamicProxy;

/**
* This class is a java 1.5 doclet, that is used as a filter between the javadoc
* framework and another doclet, that produces some output.
*
* The PublishedApiDoclet limits the packages, classes, fields, methods seen by
* the second doclet based on exclude and include tags.
*
* Technically this class is an application of the
* {@link de.kruis.padoclet.FilterDocletBase} class.
*
* @author kruis
*/
public class PublishedApiDoclet extends FilterDocletBase {
   
    /**
     * the name of the <i>exclude</i> tag. Default is <code>pad.exclude</code>.
     */
    private String excludeTag;
    /**
     * the exclude filter regular expression.
     */
    private Pattern excludeFilter;
    /**
     * the name of the <i>include</i> tag. Default is <code>pad.include</code>.
     */
    private String includeTag;
    /**
     * the include filter regular expression.
     */
    private Pattern includeFilter;
    /**
     * the name of the <i>forceInclude</i> tag. Default is <code>pad.forceInclude</code>.
     */
    private String forceIncludeTag;
    /**
     * the forceInclude filter regular expression.
     */
    private Pattern forceIncludeFilter;
    /**
     * the name of the <i>excludeChilds</i> tag. Default is <code>pad.excludeChilds</code>.
     */
    private String excludeChildsTag;
    /**
     * the excludeChilds filter regular expression.
     */
    private Pattern excludeChildsFilter;
    /**
     * the default mode. If <code>true</code>, everything is excluded by default.
     * Default is <code>false</code> / include.
     */
    private boolean defaultIsExclude;
    /**
     * the priority of the default setting. A positive value. Default is 1.
     */
    private int defaultPriority;
    /**
     * If <code>true</code>, retrieve all items from the javadoc core. Otherwise
     * retrieve only the included subset.
     */
    private boolean disableJavadocFilter;
    /**
     * If <code>true</code>, don't call {@link Doc#isIncluded()}.
     */
    private boolean ignoreJavadocIsIncluded;
   
    /**
     * If <code>true</code>, do not filter enum constants
     */
    private boolean dontFilterEnumConstants;
    /**
     * If <code>true</code>, do not filter annotation elements
     */
    private boolean dontFilterAnnotationElements;
    /**
     * Create a new instance
     */
    private PublishedApiDoclet() {
    }
  
    /**
   * @return Returns the defaultIsExclude.
   */
  public final boolean isDefaultIsExclude() {
    return defaultIsExclude;
  }

  /**
   * @param defaultIsExclude The defaultIsExclude to set.
   */
  public final void setDefaultIsExclude(boolean defaultIsExclude) {
    this.defaultIsExclude = defaultIsExclude;
  }

  /**
   * @return Returns the defaultPriority.
   */
  public final int getDefaultPriority() {
    return defaultPriority;
  }

  /**
   * @param defaultPriority The defaultPriority to set.
   */
  public final void setDefaultPriority(int defaultPriority) {
    this.defaultPriority = defaultPriority;
  }

  /**
   * @return Returns the disableJavadocFilter.
   */
  public final boolean isDisableJavadocFilter() {
    return disableJavadocFilter;
  }

  /**
   * @param disableJavadocFilter The disableJavadocFilter to set.
   */
  public final void setDisableJavadocFilter(boolean disableJavadocFilter) {
    this.disableJavadocFilter = disableJavadocFilter;
  }

  /**
   * @return Returns the excludeChildsFilter.
   */
  public final String getExcludeChildsFilter() {
    return excludeChildsFilter.pattern();
  }
  public final Pattern getExcludeChildsFilterPat() {
    return excludeChildsFilter;
  }
 
 
  /**
   * @param excludeChildsFilter The excludeChildsFilter to set.
   */
  public final void setExcludeChildsFilter(String excludeChildsFilter) {
    this.excludeChildsFilter = Pattern.compile(excludeChildsFilter);
  }

  /**
   * @return Returns the excludeChildsTag.
   */
  public final String getExcludeChildsTag() {
    return excludeChildsTag;
  }

  /**
   * @param excludeChildsTag The excludeChildsTag to set.
   */
  public final void setExcludeChildsTag(String excludeChildsTag) {
    this.excludeChildsTag = excludeChildsTag;
  }

  /**
   * @return Returns the excludeFilter.
   */
  public final String getExcludeFilter() {
    return excludeFilter.pattern();
  }
  public final Pattern getExcludeFilterPat() {
    return excludeFilter;
  }
 
  /**
   * @param excludeFilter The excludeFilter to set.
   */
  public final void setExcludeFilter(String excludeFilter) {
    this.excludeFilter = Pattern.compile(excludeFilter);
  }

  /**
   * @return Returns the forceIncludeFilter.
   */
  public final String getForceIncludeFilter() {
    return forceIncludeFilter.pattern();
  }
  public final Pattern getForceIncludeFilterPat() {
    return forceIncludeFilter;
  }

  /**
   * @param forceIncludeFilter The forceIncludeFilter to set.
   */
  public final void setForceIncludeFilter(String forceIncludeFilter) {
    this.forceIncludeFilter = Pattern.compile(forceIncludeFilter);
  }

  /**
   * @return Returns the forceIncludeTag.
   */
  public final String getForceIncludeTag() {
    return forceIncludeTag;
  }

  /**
   * @param forceIncludeTag The forceIncludeTag to set.
   */
  public final void setForceIncludeTag(String forceIncludeTag) {
    this.forceIncludeTag = forceIncludeTag;
  }

  /**
   * @return Returns the ignoreJavadocIsIncluded.
   */
  public final boolean isIgnoreJavadocIsIncluded() {
    return ignoreJavadocIsIncluded;
  }

  /**
   * @param ignoreJavadocIsIncluded The ignoreJavadocIsIncluded to set.
   */
  public final void setIgnoreJavadocIsIncluded(boolean ignoreJavadocIsIncluded) {
    this.ignoreJavadocIsIncluded = ignoreJavadocIsIncluded;
  }

  /**
   * @return Returns the includeFilter.
   */
  public final String getIncludeFilter() {
    return includeFilter.pattern();
  }
  public final Pattern getIncludeFilterPat() {
    return includeFilter;
  }

  /**
   * @param includeFilter The includeFilter to set.
   */
  public final void setIncludeFilter(String includeFilter) {
    this.includeFilter = Pattern.compile(includeFilter);
  }

  /**
     * @return Returns the excludeTag.
     */
    public final String getExcludeTag() {
        return excludeTag;
    }
    /**
     * @param excludeTag The excludeTag to set.
     */
    public final void setExcludeTag(String excludeTag) {
        this.excludeTag = excludeTag;
    }
    /**
     * @return Returns the includeTag.
     */
    public final String getIncludeTag() {
        return includeTag;
    }
    /**
     * @param includeTag The includeTag to set.
     */
    public final void setIncludeTag(String includeTag) {
        this.includeTag = includeTag;
    }
  /**
   * @return the dontFilterAnnotationElements
   */
  public final boolean isDontFilterAnnotationElements() {
    return dontFilterAnnotationElements;
  }

  /**
   * @param dontFilterAnnotationElements the dontFilterAnnotationElements to set
   */
  public final void setDontFilterAnnotationElements(
      boolean dontFilterAnnotationElements) {
    this.dontFilterAnnotationElements = dontFilterAnnotationElements;
  }

  /**
   * @return the dontFilterEnumConstants
   */
  public final boolean isDontFilterEnumConstants() {
    return dontFilterEnumConstants;
  }

  /**
   * @param dontFilterEnumConstants the dontFilterEnumConstants to set
   */
  public final void setDontFilterEnumConstants(boolean dontFilterEnumConstants) {
    this.dontFilterEnumConstants = dontFilterEnumConstants;
  }

   
  // register the options. The option names must match the setable properties of
    // the class
     static {
        Option.register(new Option("FilterDefault",".*",false,"Default regular expression for all filter options."+Option.LI
            +"PublishedApiDoclet tags are ignored, unless the regular expression matches the text"+Option.LI
            +"after the tag (that may be the empty string). The default regular expression \".*\" matches"+Option.LI
            +"everything including the empty string."+Option.LI
            +"  Every matched portion of the tag gets a priority number and the maximum of all these numbers"+Option.LI
            +"is taken as the priority for this tag and language element. If a matched portion of the text"+Option.LI
            +"contains a decimal number (e.g. \"foo123\") then the number is taken as the priority of the"+Option.LI
            +"match. Otherwise the match priority is 1."+Option.LI
            +"  The comparision of include- and exclude-priorities determinates, if the language element"+Option.LI
            +"will be documented."));
        Option.register(new Option("ExcludeTag","pad.exclude",true,"The name(s) of the exclude tag."+Option.LI
            +"Use this option to redefine the tag. The empty string disables the tag."+Option.LI
            +"Multiple names may be separated by white space or comma."));
        Option.register(new Option("ExcludeFilter","<value of "+Option.namePrefix+"FilterDefault>",false,"The regular expression used to filter excludeTags."));
        Option.register(new Option("IncludeTag","pad.include",true,"The name(s) of the include tag."+Option.LI
            +"Use this option to redefine the tag. The empty string disables the tag."+Option.LI
            +"Multiple names may be separated by white space or comma."));
        Option.register(new Option("IncludeFilter","<value of "+Option.namePrefix+"FilterDefault>",false,"The regular expression used to filter includeTags."));
        Option.register(new Option("ForceIncludeTag","pad.forceInclude",true,"The name(s) of the forceInclude tag. This tag is used,"+Option.LI
            +"to include lenguage elements, that were excluded by the doclet itself (e.g. a private method)."+Option.LI
            +"This tag has no priority."+Option.LI
            +"  Use this option to redefine the tag. The empty string disables the tag."+Option.LI
            +"Multiple names may be separated by white space or comma."));
        Option.register(new Option("ForceIncludeFilter","<value of "+Option.namePrefix+"FilterDefault>",false,"The regular expression used to filter forceIncludeTags."));
        Option.register(new Option("ExcludeChildsTag","pad.excludeChilds",true,"The name(s) of the excludeChilds tag. This tag is similar to the"+Option.LI
            +"@pad.exclude tag, but it influences the exclude priority for childs of the taged element only."+Option.LI
            +"The effective child exclude priority is the maximum of the @pad.exclude and the"+Option.LI
            +"@pad.excludeChilds priorities."+Option.LI
            +"  Use this option to redefine the tag. The empty string disables the tag."+Option.LI
            +"Multiple names may be separated by white space or comma."));
        Option.register(new Option("ExcludeChildsFilter","<value of "+Option.namePrefix+"FilterDefault>",false,"The regular expression used to filter excludeChildsTags."));
        Option.register(new Option("DefaultIsExclude","Exclude all items except explicitly included ones. This option is usefull,"+Option.LI
            +"if you want to include only a hand selected subset of your API."));
        Option.register(new Option("DefaultPriority","1",false,"The priority of the default behaviour."));
        Option.register(new Option("DisableJavadocFilter","Get unfiltered item collections from javadoc. You may need to"+Option.LI
            +"use this option, if you use the @pad.forceInclude tag."));
        Option.register(new Option("IgnoreJavadocIsIncluded","Do not call the javadoc isIncluded method."));
        Option.register(new Option("DontFilterEnumConstants","Do not filter enum constants. (Enum constants are always included.)"));
        Option.register(new Option("DontFilterAnnotationElements","Do not filter annotation elements. (Annotations elements are always included.)"));
         // make sure RefCheckDoclet is loaded and static initializers were execuded
         new RefCheckDoclet();
         AbstractOption option = RefCheckDoclet.Option.get(RefCheckDoclet.OPTION_WARN_ON);
         Option.register(new Option(option.name, "", option.isTag, option.description));
      }
    
     /**
      * Implements the doclet languageVersion method.
      *
      * This version of PA doclet only supports {@link LanguageVersion#JAVA_1_5}.
      *
      * @return the supported language version
      */
     public static LanguageVersion languageVersion() {
       LanguageVersion lv = languageVersionHelper();
       if (lv != LanguageVersion.JAVA_1_5) {
         throw new RuntimeException("This version of "+PublishedApiDoclet.class.getName()+
             " only supports language version "+LanguageVersion.JAVA_1_5+" but the "+
             "delegate doclet uses language version "+lv);
       }
       return lv;
     }

    /**
     * Implements the doclet validOptions method
     *
     * @param options the options
     * @param reporter used to emit messages
     * @return <code>true</code>, is everything is ok, <code>false</code> otherwise.
     * @throws java.io.IOException
     * @see com.sun.javadoc.Doclet#validOptions(java.lang.String[][], com.sun.javadoc.DocErrorReporter)
     */
    public static boolean validOptions(String[][] options,
            DocErrorReporter reporter) throws java.io.IOException {
      boolean showHelp = false;
     
      // init the options, because we need the DefaultPriority option
      Option.initOptions(options);     
      try{
        Integer.parseInt(Option.get("DefaultPriority").value);
      } catch (NumberFormatException e) {
      reporter.printError("Option "+Option.namePrefix+"DefaultPriority"+" requires an integer argument" );
      showHelp = true;
      }
      // delegate most of the work to the helper method
        return validOptionsHelper(options,reporter,showHelp,PublishedApiDoclet.class.getName());
    }

    /**
     * The doclet optionLength method.
     *
     * @param option the name of the option
     * @return the length or 0, if the option is unknown
     * @see com.sun.javadoc.Doclet#optionLength(java.lang.String)
     */
    public static int optionLength(String option) {
      // delegate the work to the helper method
        return optionLengthHelper(option,PublishedApiDoclet.class.getName());
    }

    /**
     * The doclet start method.
     *
     * @param root the RootDoc object
     * @return <code>true</code>, if everything is ok, otherwise <code>false</code>.
     * @throws java.io.IOException
     * @see com.sun.javadoc.Doclet#start(com.sun.javadoc.RootDoc)
     */
    public static boolean start(RootDoc root) throws java.io.IOException {
        // process our options: set the default for the filter options
        Option.initOptions(root.options()); // first pass: we need FilterDefault
        Option.get("ExcludeFilter").value =
          Option.get("ExcludeChildsFilter").value =
            Option.get("IncludeFilter").value =
              Option.get("ForceIncludeFilter").value
              = Option.get("FilterDefault").value;
       
        // create the filter doclet instance
        FilterDocletBase fd = new PublishedApiDoclet();
        // delegate the work to the helper method
        return startHelper(root,fd);
    }

  /* (non-Javadoc)
   * @see de.kruis.padoclet.FilterDocletBase#preDelegateStartHook(com.sun.javadoc.RootDoc)
   */
  protected void preDelegateStartHook(RootDoc filteredRootDoc) {
    // the following lines are mor or less a copy of
    // RefCheckDoclet#start()
    if (Option.get(RefCheckDoclet.OPTION_WARN_ON).value.length() > 0) {
      RefCheckDoclet rcd = new RefCheckDoclet();
      try {
        Option.initJavaBeanProperties(rcd);
      } catch (Throwable e) {
        e.printStackTrace();
        this.getErrorReporter().printError(e.toString());
        return;
      }
      rcd.setErrorReporter(this.getErrorReporter());
      rcd.check(filteredRootDoc);
    }
  }

    /**
     * A main method.
     *
     * This method simply calls the doclet main method.
     *
     * @param args the command line arguments
     * @see Main#execute(java.lang.String, java.lang.String, java.lang.String[])
     */
    public static void main(String[] args) {
        String name = PublishedApiDoclet.class.getName();
        Main.execute(name, name, args);
    }

    // initialize the proxy class table
  static {
    HalfDynamicProxy.setProxyClassTable(
        new Class[][] {
        // 1st entry: handler class, 2nd and up: interfaces required to match
        // the interfaces are part of the com.sun.javadoc
        new Class[] { RootDocHandler.class,RootDoc.class},
        new Class[] { AnnotationTypeDocHandler.class, AnnotationTypeDoc.class},
        new Class[] { ClassDocHandler.class, ClassDoc.class},
        new Class[] { PackageDocHandler.class, PackageDoc.class},
        new Class[] { MethodDocHandler.class, MethodDoc.class},
        new Class[] { DocHandler.class, Doc.class},
        new Class[] { HandlerBase.class, AnnotationValue.class },
        new Class[] { AnnotationDescHandler.class, AnnotationDesc.class },
        new Class[] { HandlerBase.class, AnnotationDesc.ElementValuePair.class },
        new Class[] { ComparableHandler.class, Tag.class, Comparable.class},
        new Class[] { HandlerBase.class, Tag.class},
        new Class[] { HandlerBase.class, Type.class},
        new Class[] { HandlerBase.class, Parameter.class},
    }
    );
  }
 
 
  /**
   * Proxy methods and state common to all {@link Doc} instances.
   *
   * @author kruis
   */
  public static class DocHandler extends ComparableHandler {


    /**
     * The value of this field is only valid, if {@link #isIncludedValid}
     * is <code>true</code>.
     * Then this field determinates, if the Doc instance is included.
     */
    private boolean isIncluded;
   
    /**
     * if <code>true</code>, the inclusion state is known and
     * {@link #isIncluded} is valid
     */
    private boolean isIncludedValid = false;
   
    /**
     * Flag, that is used to avoid infinite recursion. May be obsolete.
     */
    private boolean isCheckStarted = false;
   
    /**
     * Cached inclusion priority for this node.
     * Only valid, if {@link #inclusionPriorityValid} is <code>true</code>.
     */
    private int inclusionPriority = 0;
    /**
     * Cached inclusion priority for childs of this node.
     * Only valid, if {@link #inclusionPriorityValid} is <code>true</code>.
     */
    private int childInclusionPriority = 0;
    /**
     * Indicates, if {@link #inclusionPriority} and {@link #childInclusionPriority} is
     * valid.
     */
    private boolean inclusionPriorityValid = false;
   
    /**
     * Create a new instance.
     */
    public DocHandler() {}
   
    /**
     * Get the value of the boolean <i>included</i> property of the Doc
     * object, that is represented by this dynamic proxy instance.
     *
     * This method is overridden by PublishedApiDoclet. It is the
     * switch used to turn documentation on or off.
     *
     * @return return <code>true</code>, if this node (package, class, method, field)
     * shall be included in the documentation.
     *
     * @see com.sun.javadoc.Doc#isIncluded()
     */
    public boolean isIncluded() {
      // if the cached result is valid, there is no need to recompute the
      //  result of this method.
      if (isIncludedValid)
        // the result is already known. Nothing to do.
        return isIncluded;
     
      // we probably do not need this check, but during development it proved
      // to be useful.
      if (isCheckStarted)
        throw new IllegalStateException("unexpected recursion detected");
     
      // start of try{ }finally
      try {
        // anti recursion flag.
        this.isCheckStarted = true;
       
        // get the state object and the target of this proxy
        PublishedApiDoclet pad = (PublishedApiDoclet) getHDPStateUserObject();
        Doc doc = (Doc)this.getInvocationTarget();
       
        // determinate, if the target is to be included in the
        // documentation: Include it, if either
        //
        this.isIncluded = pad.isIgnoreJavadocIsIncluded() // the global flag says so
          || doc.isIncluded()  // the javadoc framework says so
          || // the "forceInclude"-tag of the target says so
             tagPriority(doc,pad.getForceIncludeTag(),pad.getForceIncludeFilterPat()) > 0;

          // if we already know, that the target is not to be included,
          // we are ready.
        if (! this.isIncluded)
          return false;

        // check the current target node and its parent nodes.
        this.isIncluded = this.calcInclusionPriority(false) >= 0 ;
       
        // if the default is "exclude", check, if the
        // inclusion of just this target node is required,
        // because one of the child elements is included
        if (! this.isIncluded) {
          boolean inclusionRequired = false;
          if (doc instanceof PackageDoc) {
            // nested nodes are classes, therefore check, if the
            // package contains any classes to be documented
            PackageDoc pd = (PackageDoc) dynamicProxyInstance();
            inclusionRequired = pd.allClasses().length > 0;
          } else if (doc instanceof ClassDoc) {
            // nested nodes are constructors, methods or fields.
            ClassDoc cd = (ClassDoc) dynamicProxyInstance();
            inclusionRequired =
              cd.constructors().length > 0 ||
              cd.methods().length > 0 ||
              cd.fields().length > 0 ||
              cd.enumConstants().length > 0;
            if (! inclusionRequired && cd instanceof AnnotationTypeDoc) {
              AnnotationTypeDoc atd = (AnnotationTypeDoc) cd;
              inclusionRequired = atd.elements().length > 0;
            }
          }
          if (inclusionRequired) {
            debug("detected required inclusion of: "+doc.toString());
            this.isIncluded = true;
          }
        }         
        return this.isIncluded;
      } finally {
        // mark the value of this.isIncluded as valid.
        this.isIncludedValid = true;
      }     
    }

    /**
     * A constant used by {@link #tagPriority(Doc, String, Pattern)}.
     */
    private static final Pattern digits = Pattern.compile(".*?(\\d+)");
    /**
     * Get the priority from a tag.
     *
     * If the doc-node does not contain a matching tag, the result is
     * 0. Otherwise the result is at least 1. If one or more matched portions of the
     * tag end with a decimal number the maximum of these numbers is returned.
     *
     * @param doc the document node (package, class, method, field, ...)
     * @param tag the name of the tag
     * @param filter a filter pattern. Only tags, that match this pattern are
     * looked at.
     * @return the priority asiciated with the tag: a non negative integer. 0 is the
     * minimum priority.
     */
    private static int tagPriority(Doc doc, String tag, Pattern filter) {
      if (tag == null || tag.length() == 0)
        return 0;
      StringTokenizer tokenizer = new StringTokenizer(tag,Option.TAG_DELIMITER);
      int prio = 0;
      Tag[] tags;
      while(tokenizer.hasMoreTokens()) {
        tags = doc.tags(tokenizer.nextToken());
        int p;
        for(int i=0;i<tags.length;i++) {
          String text = tags[i].text();
          if (text == null)
            text=""; // I'm paranoid
          Matcher matcher = filter.matcher(text);
          while(matcher.find()) {
            // the tag matches, now get its priority
            p = 1;
            // now try to get the priority from the matched
            matcher = digits.matcher(matcher.group());
            if (matcher.lookingAt()) {
              // the tag has an encoded priority
              p = Integer.parseInt(matcher.group(1));
            }
            // we need the maximum value
            if (prio < p)
              prio = p;
          }
        }
      }
      return prio;
    }
   
    /**
     * Check, if this item shall be included.
     *
     * This method checks this item and its parents, but not the childs.
     *
     * @param iscallFromChild if <code>true</code> return the inclusion priority
     * for a child item, otherwise return the inclusion priority for this item.
     * @return an include (return value is positive) or exclude
     * (return value is negative) priority.
     */
    private int calcInclusionPriority(boolean iscallFromChild) {
      if (this.inclusionPriorityValid)
        return iscallFromChild ? this.childInclusionPriority : this.inclusionPriority;
      try {
        Doc doc = (Doc) dynamicProxyInstance();
        PublishedApiDoclet pad = (PublishedApiDoclet) getHDPStateUserObject();
        int includePriority = tagPriority(doc,pad.getIncludeTag(),pad.getIncludeFilterPat());
        int excludePriority = tagPriority(doc,pad.getExcludeTag(),pad.getExcludeFilterPat());
        int excludeChildsPriority = tagPriority(doc,pad.getExcludeChildsTag(),pad.getExcludeChildsFilterPat());
        if (excludePriority > excludeChildsPriority) {
          // the exclude childs priority must be at least as high
          // as the exclude priority, because we do not want childs
          // to be included, and the local node included
          excludeChildsPriority = excludePriority;
        }
       
        int parentPriority = (pad.isDefaultIsExclude()?-1:1)*
            pad.getDefaultPriority();
        // ---- ask the parent, if possible ----
         
        Doc doc2 = null; // holds the parent
        // get the parent
        if (doc instanceof ProgramElementDoc) {
          ProgramElementDoc member = (ProgramElementDoc) doc;
          doc2 = member.containingClass();
          if (doc2 == null) {
            doc2 = member.containingPackage();
          }
        }
        // is parent valid?
        if (doc2 instanceof Proxy &&
            Proxy.getInvocationHandler(doc2) instanceof DocHandler) {
          // doc2 is the parent
          parentPriority = ((DocHandler)Proxy.getInvocationHandler(doc2)).calcInclusionPriority(true);
        }
       
        // now do the calculation for the local inclusion prio
        this.inclusionPriority = includePriority;
        if (excludePriority > includePriority) {
          this.inclusionPriority = -excludePriority;
        }
        if (Math.abs(parentPriority) > Math.abs(this.inclusionPriority)) {
          this.inclusionPriority = parentPriority;
        }
        // now do the calculation for the child inclusion prio
        this.childInclusionPriority = includePriority;
        if (excludeChildsPriority > includePriority) {
          this.childInclusionPriority = -excludeChildsPriority;
        }
        if (Math.abs(parentPriority) > Math.abs(this.childInclusionPriority)) {
          this.childInclusionPriority = parentPriority;
        }
       
        return iscallFromChild ?
            this.childInclusionPriority : this.inclusionPriority;
      } finally {
        this.inclusionPriorityValid = true;
      }
    }
       
    /**
     * Filter the content of a com.sun.javadoc.Doc-array, in order to remove entries not to be documented.
     *
     * This method removes the entries from an array of Doc-items,
     * that were not to be documented.
     *
     * @param array the array of doc items.
     * @param expect the class of the concrete array type, i.e. <code>com.sun.javadoc.PackageDoc[].class</code>,
     * if array contains PackageDoc items.
     * @param doFilter if <code>false</code>, simply return a HD-proxy for array.
     * @return a Doc[], containing HD-Proxies for the possibly filtered entries
     * of the given array.
     */
    protected Doc[] filterDocArray(Doc[] array, Class<? extends Doc[]> expect, boolean doFilter) {
      if (!doFilter) {
        return (Doc[]) getHDPProxy(array, expect);
      }
      Class<?> componentType = expect.getComponentType();
      List<Doc> list = new ArrayList<Doc>(array.length);
      for (int i = 0; i < array.length; i++) {
        Doc entry = (Doc) getHDPProxy(array[i], componentType);
        if (entry != null && ! entry.isIncluded()){
          //debug("Array Excluding: "+entry.getClass().getName()+ " " + entry);
          continue;
        }
        list.add(entry);
      }
      return (Doc[]) list.toArray((Object[]) Array.newInstance(componentType,list.size()));
    }

    /**
     * Determinate, if a com.sun.javadoc.Doc array shall be filtered.
     *
     * @param filter if <code>false</code>, do not filter.
     * @return returns <code>false</code>, if either the filter parameter is false or if
     * the filter is globally disabled.
     */
    protected boolean isFilter(boolean filter) {
      if (! filter)
        return false;
      PublishedApiDoclet pad = (PublishedApiDoclet) getHDPStateUserObject();
      return ! pad.isDisableJavadocFilter();
    }

    /* (non-Javadoc)
     * @see de.kruis.padoclet.FilterDocletBase.HandlerBase#debug(java.lang.String)
     */
    protected void debug(String message) {
      SourcePosition position = ((Doc)(this.getInvocationTarget())).position();
      FilterDocletBase pad = (FilterDocletBase) getHDPStateUserObject();
      pad.getErrorReporter().printNotice(position,message);
    }

  }
 
  /**
   * Proxy methods for the {@link MethodDoc} instance.
   *
   * @author kruis
   */
  public static class MethodDocHandler extends DocHandler {
    /**
     * Create a new instance.
     */
    public MethodDocHandler() {}
   
    /*
     * @see MethodDoc#overrides(MethodDoc)
     */
    public boolean overrides(MethodDoc meth) {
      return ((MethodDoc)target).overrides((MethodDoc) unwrap(meth));
    }
  }

  /**
   * Proxy methods for the {@link RootDoc} instance.
   *
   * @author kruis
   */
  public static class RootDocHandler extends DocHandler {
    /**
     * Create a new instance.
     */
    public RootDocHandler() {}
   
    /* (non-Javadoc)
     * @see com.sun.javadoc.RootDoc#classes()
     */
    public ClassDoc[] classes() {
      return (ClassDoc[]) filterDocArray(((RootDoc)target).classes(),ClassDoc[].class,true);
    }

    /* (non-Javadoc)
     * @see com.sun.javadoc.RootDoc#options()
     */
    public String[][] options() {
      return filterOptions(((RootDoc)target).options());
    }
   
    /* (non-Javadoc)
     * @see com.sun.javadoc.RootDoc#specifiedClasses()
     */
    public ClassDoc[] specifiedClasses() {
      PublishedApiDoclet ds = (PublishedApiDoclet) getHDPStateUserObject();
      return (ClassDoc[]) filterDocArray(((RootDoc)target).specifiedClasses() ,ClassDoc[].class,
          ! ds.isDisableJavadocFilter());
    }

    /* (non-Javadoc)
     * @see com.sun.javadoc.RootDoc#specifiedClasses()
     */
    public PackageDoc[] specifiedPackages() {
      PublishedApiDoclet ds = (PublishedApiDoclet) getHDPStateUserObject();
      return (PackageDoc[]) filterDocArray(((RootDoc)target).specifiedPackages() ,PackageDoc[].class,
          ! ds.isDisableJavadocFilter());
    }

  }

  /**
   * Proxy methods for {@link ClassDoc} instances.
   *
   * @author kruis
   */
  public static class ClassDocHandler extends DocHandler {
    /**
     * Create a new instance.
     */
    public ClassDocHandler() {}
   
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#constructors()
     */
    public ConstructorDoc[] constructors() {
      return constructors(true);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#constructors(boolean)
     */
    public ConstructorDoc[] constructors(boolean filter) {
      return (ConstructorDoc[]) filterDocArray(((ClassDoc)target).constructors(isFilter(filter)) ,ConstructorDoc[] .class, filter);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#fields()
     */
    public FieldDoc[] fields() {
      return fields(true);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#fields(boolean)
     */
    public FieldDoc[] fields(boolean filter) {
      return (FieldDoc[]) filterDocArray(((ClassDoc)target).fields(isFilter(filter)) ,FieldDoc[] .class, filter);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#innerClasses()
     */
    public ClassDoc[] innerClasses() {
      return innerClasses(true);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#innerClasses(boolean)
     */
    public ClassDoc[] innerClasses(boolean filter) {
      return (ClassDoc[]) filterDocArray(((ClassDoc)target).innerClasses(isFilter(filter)) ,ClassDoc[] .class, filter);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#methods()
     */
    public MethodDoc[] methods() {
      return methods(true);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#methods(boolean)
     */
    public MethodDoc[] methods(boolean filter) {
      return (MethodDoc[]) filterDocArray(((ClassDoc)target).methods(isFilter(filter)) ,MethodDoc[] .class, filter);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.ClassDoc#subclassOf(com.sun.javadoc.ClassDoc)
     */
    public boolean subclassOf(ClassDoc arg0) {
      return ((ClassDoc)target).subclassOf((ClassDoc) unwrap(arg0));
    }
   
    /*
     * @see ClassDoc#enumConstants()
     */
    public FieldDoc[] enumConstants() {
      PublishedApiDoclet pad = (PublishedApiDoclet) getHDPStateUserObject();
      return (FieldDoc[]) filterDocArray(((ClassDoc)target).enumConstants() ,FieldDoc[] .class, ! pad.isDontFilterEnumConstants());
    }
  } 
 
  /**
   * Proxy methods for {@link ClassDoc} instances.
   *
   * @author kruis
   */
  public static class AnnotationTypeDocHandler extends ClassDocHandler {
    /**
     * Create a new instance.
     */
    public AnnotationTypeDocHandler() {}
   
    /*
     * @see AnnotationTypeDoc#elements()
     */
    public AnnotationTypeElementDoc[] elements() {
      PublishedApiDoclet pad = (PublishedApiDoclet) getHDPStateUserObject();
      return (AnnotationTypeElementDoc[]) filterDocArray(
          ((AnnotationTypeDoc) target).elements(), AnnotationTypeElementDoc[].class,
          !pad.isDontFilterAnnotationElements());
    }
  }
 
  /**
   * Proxy methods for {@link AnnotationDesc} instances.
   *
   * @author kruis
   */
  public static class AnnotationDescHandler extends HandlerBase {
    /**
     * Create a new instance.
     */
    public AnnotationDescHandler() {}
   
    /*
     * @see AnnotationDesc#elementValues()
     */
    public AnnotationDesc.ElementValuePair[] elementValues() {
      PublishedApiDoclet pad = (PublishedApiDoclet) getHDPStateUserObject();
      AnnotationDesc.ElementValuePair[] array =
        ((AnnotationDesc)target).elementValues();
     
      if (pad.isDontFilterAnnotationElements()) {
        return (AnnotationDesc.ElementValuePair[]) getHDPProxy(array, AnnotationDesc.ElementValuePair[].class);
      }
      List<AnnotationDesc.ElementValuePair> list = new ArrayList<AnnotationDesc.ElementValuePair>(array.length);
      for (int i = 0; i < array.length; i++) {
        AnnotationDesc.ElementValuePair pair = (AnnotationDesc.ElementValuePair) getHDPProxy(array[i], null);
        Doc entry = pair.element();
        if (entry != null && ! entry.isIncluded()){
          //debug("Array Excluding: "+entry.getClass().getName()+ " " + entry);
          continue;
        }
        list.add(pair);
      }
      return list.toArray((AnnotationDesc.ElementValuePair[]) Array.newInstance(AnnotationDesc.ElementValuePair.class,list.size()));
    }
  }
   
  /**
   * Proxy methods for {@link PackageDoc} instances.
   *
   * @author kruis
   */
  public static class PackageDocHandler extends DocHandler {
    /**
     * Create a new instance.
     */
    public PackageDocHandler() {}
   
    /* (non-Javadoc)
     * @see com.sun.javadoc.PackageDoc#allClasses()
     */
    public ClassDoc[] allClasses() {
      return allClasses(true);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.PackageDoc#allClasses(boolean)
     */
    public ClassDoc[] allClasses(boolean filter) {
      return (ClassDoc[]) filterDocArray(((PackageDoc)target).allClasses(isFilter(filter)) ,ClassDoc[] .class, filter);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.PackageDoc#errors()
     */
    public ClassDoc[] errors() {
      return (ClassDoc[]) filterDocArray(((PackageDoc)target).errors() ,ClassDoc[] .class, true);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.PackageDoc#exceptions()
     */
    public ClassDoc[] exceptions() {
      return (ClassDoc[]) filterDocArray(((PackageDoc)target).exceptions() ,ClassDoc[] .class, true);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.PackageDoc#interfaces()
     */
    public ClassDoc[] interfaces() {
      return (ClassDoc[]) filterDocArray(((PackageDoc)target).interfaces() ,ClassDoc[] .class, true);
    }
    /* (non-Javadoc)
     * @see com.sun.javadoc.PackageDoc#ordinaryClasses()
     */
    public ClassDoc[] ordinaryClasses() {
      return (ClassDoc[]) filterDocArray(((PackageDoc)target).ordinaryClasses() ,ClassDoc[] .class, true);
    }
           
    public ClassDoc[] enums() {
      return (ClassDoc[]) filterDocArray(((PackageDoc)target).enums(), ClassDoc[].class, true);
    }
   
    public AnnotationTypeDoc[] annotationTypes() {
      return (AnnotationTypeDoc[]) filterDocArray(((PackageDoc)target).annotationTypes(), AnnotationTypeDoc[].class, true);
    }
   
  }

}
TOP

Related Classes of de.kruis.padoclet.PublishedApiDoclet$ClassDocHandler

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.