ItemDocAnalyzer.java :  » Code-Analyzer » doctorj » org » incava » doctorj » Java Open Source

Java Open Source » Code Analyzer » doctorj 
doctorj » org » incava » doctorj » ItemDocAnalyzer.java
package org.incava.doctorj;

import java.util.*;
import net.sourceforge.pmd.ast.JavaParserConstants;
import net.sourceforge.pmd.ast.SimpleNode;
import net.sourceforge.pmd.ast.Token;
import org.incava.analysis.Report;
import org.incava.java.SimpleNodeUtil;
import org.incava.javadoc.*;
import org.incava.text.LineMapping;
import org.incava.text.Location;


/**
 * Analyzes Javadoc and code.
 */
public abstract class ItemDocAnalyzer extends DocAnalyzer
{
    public final static String MSG_NO_SUMMARY_SENTENCE = "No summary sentence";

    public final static String MSG_SUMMARY_SENTENCE_DOES_NOT_END_WITH_PERIOD = "Summary sentence does not end with period";

    public final static String MSG_SUMMARY_SENTENCE_TOO_SHORT = "Summary sentence too short";

    public final static String MSG_SEE_WITHOUT_REFERENCE = "@see without reference";

    public final static String MSG_SINCE_WITHOUT_TEXT = "@since without text";

    public final static String MSG_DEPRECATED_WITHOUT_TEXT = "@deprecated without text";
    
    public final static String MSG_TAG_IMPROPER_ORDER = "Tag in improper order";

    protected final static int CHKLVL_SUMMARY_SENTENCE = 1;

    protected final static int CHKLVL_MISORDERED_TAGS = 0;

    protected final static int CHKLVL_VALID_TAGS = 0;

    public static ItemCommentSpellCheck spellChecker = new ItemCommentSpellCheck();

    private SimpleNode _node;
    
    public ItemDocAnalyzer(Report r, SimpleNode node)
    {
        super(r);
        
        _node = node;
    }

    public static void addDictionary(String dictName)
    {
        spellChecker.addDictionary(dictName);
    }

    /**
     * Returns the Javadoc for the given node. By default, this is parses from
     * the whitespace immediately preceding this node. Other nodes might be
     * nested under the node with the preceding Javadoc.
     */
    protected JavadocNode getJavadoc()
    {
        SimpleNode  sn      = getEnclosingNode();
        JavadocNode javadoc = null;
        Token       first   = sn.getFirstToken();
        Token       st      = first.specialToken;
        
        while (javadoc == null && st != null) {
            javadoc = JavadocNode.parse(st.image, st.beginLine, st.beginColumn);
            st = st.specialToken;
        }
        
        return javadoc;
    }

    /**
     * Returns the node that contains the access and modifier tokens.
     */
    protected abstract SimpleNode getEnclosingNode();

    /**
     * Runs the analysis. Should be invoked by either the constructors of
     * concrete, final subclasses, or by the client.
     */
    public void run()
    {
        SimpleNode  encNode = getEnclosingNode();
        JavadocNode javadoc = getJavadoc();
        
        if (javadoc == null) {
            if (isCheckable(encNode, CHKLVL_DOC_EXISTS)) {
                tr.Ace.log("no Javadoc");
                StringBuffer desc = new StringBuffer("Undocumented ");
                if (SimpleNodeUtil.hasLeadingToken(encNode, JavaParserConstants.PUBLIC)) {
                    desc.append("public ");
                }
                else if (SimpleNodeUtil.hasLeadingToken(encNode, JavaParserConstants.PROTECTED)) {
                    desc.append("protected ");
                }
                else if (SimpleNodeUtil.hasLeadingToken(encNode, JavaParserConstants.PRIVATE)) {
                    desc.append("private ");
                }

                if (SimpleNodeUtil.hasLeadingToken(encNode, JavaParserConstants.ABSTRACT)) {
                    desc.append("abstract ");
                }
                
                desc.append(getItemType());

                addUndocumentedViolation(desc.toString());
            }
            else {
                tr.Ace.log("not checkable");
            }
        }
        else {
            checkJavadoc(javadoc);
        }
    }

    /**
     * Adds a violation for this type of item, with the violation pointing to
     * the name for this item.
     */
    protected abstract void addUndocumentedViolation(String desc);

    protected void checkJavadoc(JavadocNode javadoc)
    {
        // check for short summary sentence and spell check the description.

        SimpleNode encNode = getEnclosingNode();

        if (isCheckable(encNode, CHKLVL_SUMMARY_SENTENCE)) {
            JavadocElement desc = javadoc.getDescription();
            if (desc == null) {
                addViolation(MSG_NO_SUMMARY_SENTENCE, javadoc.getStartLine(), javadoc.getStartColumn(), javadoc.getEndLine(), javadoc.getEndColumn());
            }
            else {
                String descStr = desc.text;
                // tr.Ace.log("desc: '" + descStr + "'");
                int    dotPos  = descStr.indexOf('.');
                int    len     = descStr.length();
                
                // tr.Ace.log("dotPos: " + dotPos);

                // skip sequences like '127.0.0.1', i.e., whitespace must follow the dot:
                while (dotPos != -1 && dotPos + 1 < len && !Character.isWhitespace(descStr.charAt(dotPos + 1))) {
                    dotPos = descStr.indexOf('.', dotPos + 1);
                }

                if (dotPos == -1) {
                    LineMapping lines = new LineMapping(descStr, desc.start.line, desc.start.column);
                    Location    end   = lines.getLocation(descStr.length() - 1);
                    addViolation(MSG_SUMMARY_SENTENCE_DOES_NOT_END_WITH_PERIOD, desc.start, end);
                }
                else {
                    String summarySentence = descStr.substring(0, dotPos + 1);
                    tr.Ace.log("summary: '" + summarySentence + "'");
                    int    nSpaces         = 0;
                    int    spacePos        = -1;
                    while ((spacePos = summarySentence.indexOf(' ', spacePos + 1)) != -1) {
                        ++nSpaces;
                    }
                    if (nSpaces < 3) {
                        LineMapping lines = new LineMapping(descStr, desc.start.line, desc.start.column);
                        Location    end   = lines.getLocation(dotPos);
                        addViolation(MSG_SUMMARY_SENTENCE_TOO_SHORT, desc.start, end);
                    }
                }

                spellChecker.check(this, desc);
            }
        }
        else {
            tr.Ace.log("NOT checking summary sentence");
        }

        if (isCheckable(encNode, CHKLVL_MISORDERED_TAGS)) {
            tr.Ace.log("checking for misordered tags");
            int previousOrderIndex = -1;
            JavadocTaggedNode[] taggedComments = javadoc.getTaggedComments();
            for (int ti = 0; ti < taggedComments.length; ++ti) {
                JavadocTag tag   = taggedComments[ti].getTag();
                int        index = JavadocTags.getIndex(tag.text);
                tr.Ace.log("index of '" + tag.text + "': " + index);
                if (index < previousOrderIndex) {
                    addViolation(MSG_TAG_IMPROPER_ORDER, tag.start, tag.end);
                    break;
                }
                previousOrderIndex = index;
            }
        }
        else {
            tr.Ace.log("skipping check for misordered tags");
        }

        // check for proper tags for this type of item
        if (isCheckable(encNode, CHKLVL_VALID_TAGS)) {
            tr.Ace.log("checking for valid tags");
            List                validTags      = getValidTags();
            JavadocTaggedNode[] taggedComments = javadoc.getTaggedComments();
            for (int ti = 0; ti < taggedComments.length; ++ti) {
                JavadocTag tag = taggedComments[ti].getTag();
                if (!validTags.contains(tag.text)) {
                    addViolation("Tag not valid for " + getItemType(), tag.start, tag.end);
                }
            }
        }        
        else {
            tr.Ace.log("skipping check for valid tags");
        }

        // check each tag for this item
        if (isCheckable(encNode, CHKLVL_TAG_CONTENT)) {
            JavadocTaggedNode[] taggedComments = javadoc.getTaggedComments();
            for (int ti = 0; ti < taggedComments.length; ++ti) {
                JavadocTag tag = taggedComments[ti].getTag();
                tr.Ace.log("checking tag: " + tag);
                if (tag.text.equals(JavadocTags.SEE)) {
                    checkForTagDescription(taggedComments[ti], MSG_SEE_WITHOUT_REFERENCE);
                }
                else if (tag.text.equals(JavadocTags.SINCE)) {
                    checkForTagDescription(taggedComments[ti], MSG_SINCE_WITHOUT_TEXT);
                }
                else if (tag.text.equals(JavadocTags.DEPRECATED)) {
                    checkForTagDescription(taggedComments[ti], MSG_DEPRECATED_WITHOUT_TEXT);
                }
            }
        }
        else {
            tr.Ace.log("skipping check for tag content");
        }
    }

    protected void checkForTagDescription(JavadocTaggedNode taggedNode, String msg)
    {
        JavadocElement desc = taggedNode.getDescription();
        if (desc == null) {
            JavadocTag tag = taggedNode.getTag();
            addViolation(msg, tag.start, tag.end);
        }
    }

    protected SimpleNode getNode()
    {
        return _node;
    }

    /**
     * Returns the type of item this analyzer is operating on.
     */
    protected abstract String getItemType();

    /**
     * Returns the valid tags, as strings, for this type of item.
     */
    protected abstract List getValidTags();

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.