alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.javacore.parser;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.File;

/** JavaDoc Parser.
 *
 * @author Vladimir Hudec
 */
public class JavaDocParser {
    
    public static final String TEXT_TAG_NAME = "@@@@@"; // NOI18N
    
    protected String rawText;
    protected JavaDocTag[] tags;
        
    /**
     * Native line separator.
     */
    private static final String lineSeparator;
    private static final int lineSeparatorLength;
    
    static {
        String sep = System.getProperty("line.separator"); // NOI18N
        if (sep == null || sep.equals("\n")) { // NOI18N
            lineSeparator = null;
            lineSeparatorLength = 0;
        } else {
            lineSeparator = sep;
            lineSeparatorLength = sep.length();
        }
    }
    
    
    /** Constructs the JavaDoc object held in memory. Parses the tags from rawText.
     */
    
    public JavaDocParser(String rawText) {
        this.rawText =  rawText;
        this.tags = null;
    }
    
    /** Constructs the JavaDoc object held in memory. Used for rawText construction.
     */
    
    public JavaDocParser(JavaDocTag[] tags) {
        this.rawText =  null;
        this.tags = tags;
    }
    
    public JavaDocParser(String text, JavaDocTag[] tags) {
        this.rawText =  null;
        if (text == null) {
            this.tags = tags;
        }
        else {
            int newSize = (tags != null) ? tags.length+1 : 1;
            JavaDocTag[] newTags = new JavaDocTag[newSize];
            newTags[0] = new JavaDocTag(TEXT_TAG_NAME, text);
            if (tags != null)
                System.arraycopy(tags, 0, newTags, 1, tags.length);
            this.tags = newTags;
        }
    }
    
    /** Get the entire text of the comment.
     * @return the whole text
     */
    public String getRawText() {
        return getRawText(false);
    }
    public String getRawText(boolean reorder) {
        if (rawText != null && !reorder)
            return rawText;
        if (rawText != null)
            tags = getTags();
        if (reorder)
            Arrays.sort(tags);            
        String javadocContent = buildComment(tags);
        //return surroundWithJavaDocStars(javadocContent);
        return javadocContent;
    }
    
    /** Set the raw text of the comment.
     * @param s the whole text to set
     */
    //public void setRawText(String s) {
    //    rawText = s;
    //}
    
    /** Clears the javadoc from the source.
     */
    public void clearJavaDoc() {
        rawText = null;
    }
    
    /** Test if this javadoc is empty.
     * @return true if it is not generated to the source.
     */
    public boolean isEmpty() {
        return rawText == null;
    }
    
    /** Gets all tags from comment.
     */
    public JavaDocTag[] getTags() {
        if (tags != null)
            return tags;
            
        if ( rawText == null )
            return null;
        
        List tagList = new ArrayList();
        //System.out.println("getRawText(): "+getRawText());
        int[] textIndexes = removeJavaDocStars(getRawText());
        parseComment(tagList, getRawText(), textIndexes);
        
        JavaDocTag[] tagArray = new JavaDocTag[tagList.size()];
        tagList.toArray(tagArray);
        return tagArray;
    }
    
    /** Gets all tags of given name
     */
    public JavaDocTag[] getTags(String name) {
        JavaDocTag[] allTags = getTags();
        ArrayList resultList = new ArrayList( allTags.length );
        
        for( int i = 0; i < allTags.length; i++ ) {
            if (allTags[i].getName().equals(name))
                resultList.add(allTags[i]);
        }
        
        JavaDocTag result[] = new JavaDocTag[resultList.size()];
        resultList.toArray(result);
        return result;
    }
    
    // PRIVATE & UTILITY METHODS ----------------------------------------------------------
    
    /**
     * Parses the rawText and generates list of tags;
     */
    
    private void parseComment(List tagList, String textWithStars, int[] textIndexes) {
        final int IN_TEXT = 1;
        final int TAG_GAP = 2;
        final int TAG_NAME = 3;
        final int TAG_TEXT = 4;
        
        int state = IN_TEXT;
        
        boolean newLine = true;
        
        int tagStart = 0;
        int tagEnd = 0;
        int textStart = 0;
        int textEnd = 0;
        int lastNonWhite = -1;
        String rawText = rebuildText(textWithStars, textIndexes);
        //System.out.println("parseComment(): rawText=>"+rawText+"<");
        //System.out.println("parseComment(): lineSeparator=>"+lineSeparator+"<");
        int len = rawText.length();
        
        for (int i = 0; i < len; ++i) {
            
            char ch = rawText.charAt(i);
            boolean isWhite = Character.isWhitespace(ch);
            
            switch (state) {
                case IN_TEXT:
                    if (newLine && ch == '@') {
                        parseCommentComponent(tagList, rawText, textIndexes, -1, -1, 0, textEnd);
                        tagStart = i;
                        state = TAG_NAME;
                    }
                    break;
                case TAG_NAME:
                    if (isWhite) {
                        tagEnd = i;
                        state = TAG_GAP;
                    }
                    break;
                case TAG_GAP:
                    if (isWhite) {
                        break;
                    }
                    textStart = i;
                    state = TAG_TEXT;
                    // Fall through (in case of @tagname\n@anothertagname)
                case TAG_TEXT:
                    if (newLine && ch == '@') {
                        parseCommentComponent(tagList, rawText, textIndexes, tagStart, tagEnd, textStart, lastNonWhite + 1);
                        tagStart = i;
                        state = TAG_NAME;
                    }
                    break;
            }
            
            // more idiot-proof check for newline terminator:
            if (lineSeparator != null && i + lineSeparatorLength <= len && rawText.regionMatches(i, lineSeparator, 0, lineSeparatorLength)) {
                newLine = true;
                if (state == IN_TEXT) {
                    textEnd = i;
                }
                // advance the scan pointer after the separator string.
                i += lineSeparatorLength - 1;
            } else if (ch == '\n') {
                newLine = true;
                if (state == IN_TEXT) {
                    textEnd = i;
                }
            } else if (!isWhite) {
                lastNonWhite = i;
                newLine = false;
            }
        }
        
        // Finish what's currently being processed
        switch (state)  {
            case TAG_NAME:
                tagEnd = len;
                /* fall thru */
            case TAG_GAP:
                textStart = len;
                /* fall thru */
            case TAG_TEXT:
            case IN_TEXT:
                parseCommentComponent(tagList, rawText, textIndexes, tagStart, tagEnd, textStart, lastNonWhite + 1);
                break;
        };
        
    }
    
    /**
     * Parses the tag.
     * Saves away the last parsed item.
     */
    private void parseCommentComponent(List tagList, String text, int[] textIndexes, int tagStart, int tagEnd, int textStart, int textEnd) {
        //System.out.println("parseCommentComponent: "+tagStart+","+tagEnd+","+textStart+","+textEnd);
        //System.out.println("parseCommentComponent: "+textIndex(textIndexes,tagStart)+","+textIndex(textIndexes,tagEnd)+","+textIndex(textIndexes,textStart)+","+textIndex(textIndexes,textEnd));
        String tagName = (tagStart < 0 || tagEnd < 0 || tagStart > tagEnd || (tagStart == 0 && tagEnd == 0)) ? TEXT_TAG_NAME : text.substring(tagStart, tagEnd);
        String tx = (textStart < 0 || textEnd < 0 || textStart > textEnd) ? "" : text.substring(textStart, textEnd);
        
        if (TEXT_TAG_NAME.equals(tagName)) {
            JavaDocTag tag = new JavaDocTag(TEXT_TAG_NAME, tx, -1, -1, (textIndexes!=null)?textIndexes[textStart]:textStart, (textIndexes!=null)?textIndexes[textEnd]:textEnd);
            tagList.add(tag);
        }
        else {
            JavaDocTag tag = new JavaDocTag(tagName, tx, (textIndexes!=null)?textIndexes[tagStart]:tagStart, (textIndexes!=null)?textIndexes[tagEnd]:tagEnd, 
                                            (textIndexes!=null)?textIndexes[textStart]:textStart, (textIndexes!=null)?textIndexes[textEnd]:textEnd);
            tagList.add(tag);
        }
    }
    
    public int[] removeJavaDocStars(String rawText) {
        final int SPACE_BEFORE=1;
        final int ASTERIX=2;
        final int SPACE_AFTER=3;
        final int TEXT=4;
        final int LINE_BREAK=5;
        
        if (rawText == null || rawText.length() < 4)
            return null;
        
        int len = rawText.length();
        int text[] = new int[len];
        int i, j = 0;
        int state = ASTERIX;
        
        int start = rawText.indexOf("/*");
        int end = rawText.lastIndexOf("*/"); // NOI18N
        if (start == -1 || end == -1 || (start+2) > end)
            return null;
        int linebreak = -1;
        
        for (i=start+2; i= 0)
                linebreak = -1;
            
            switch (state) {
                case SPACE_BEFORE:
                    if (ch=='*')
                        state = ASTERIX;
                    else if (ch=='\n' || ch=='\r')
                        state = LINE_BREAK;
                    else if (ch!=' ' && ch!='\t')
                        state = TEXT;
                    break;
                case SPACE_AFTER:
                    if (ch=='\n' || ch=='\r')
                        state = LINE_BREAK;
                    else if (ch!=' ' && ch!='\t')
                        state = TEXT;
                    break;
                case ASTERIX:
                    if (ch=='\n' || ch=='\r')
                        state = LINE_BREAK;
                    else if (ch==' ' || ch=='\t')
                        state = SPACE_AFTER;
                    else if (ch != '*')
                        state = TEXT;
                    break;
                case TEXT:
                    if (ch=='\n' || ch=='\r')
                        state = LINE_BREAK;
                    break;
                case LINE_BREAK:
                    if (ch=='*')
                        state = ASTERIX;
                    else if (ch==' ' || ch=='\t')
                        state = SPACE_BEFORE;
                    else if (ch!='\n' && ch!='\r')
                        state = TEXT;
                    break;
            }
            
            if (state==LINE_BREAK || state==SPACE_AFTER) {
                text[j++]=i;
            }
            else if (state==TEXT) {
                if (linebreak != -1) {
                    for (int k = linebreak+1; k <= i; k++) {
                        text[j++]=k;
                    }
                    linebreak = -1;
                }
                else {
                    text[j++]=i;
                }
            }
        }
        
        int result[];
        if (j < len) {
            result = new int[j];
            System.arraycopy(text, 0, result, 0, j);
        }
        else
            result = text;
        return result;
    }
    
    private String rebuildText(String text, int[] textIndexes) {
        String rawText = text;
        if (textIndexes != null) {
            char[] rawTextChars = new char[textIndexes.length];
            for (int i = 0; i < textIndexes.length; i++)
                rawTextChars[i] = text.charAt(textIndexes[i]);
            rawText = new String(rawTextChars, 0, rawTextChars.length);
        }
        return rawText;
    }
    
    private String buildComment(JavaDocTag[] tags) {
        if (tags == null)
            return null;
        if (tags.length == 0)
            return "";
            
        String separator = (lineSeparator != null) ? lineSeparator : "\n"; // NOI18N
        String separator2 = separator+separator;
        
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < tags.length; i++) {
            if (TEXT_TAG_NAME.equals(tags[i].getName())) {
                String value = tags[i].getValue();
                sb.append(value);
                
                if (!value.endsWith(separator2)) {
                    if (!value.endsWith(separator)) {
                        sb.append(separator);
                    }
                    sb.append(separator);
                }
            }
            else {
                String value = tags[i].getValue();
                sb.append(tags[i].getName()).append(" ").append(value); // NOI18N
                if (!value.endsWith(separator)) {
                    sb.append(separator);
                }
            }
        }
        return sb.toString();
    }

    /**
     * Creates javaDoc comment from provided parameter. 
     * 
     * @param  javadocContent contents
     * @return javadoc comment created from provided string
     */
    public static String surroundWithJavaDocStars(String javadocContent) {
        if (javadocContent == null)
            return null;
        
        String separator = "\n"; // NOI18N

        StringBuffer sb = new StringBuffer("/**\n *"); // NOI18N
        String[] tokens = javadocContent.split(separator);
        if (tokens.length > 1) {
            for (int i = 0; i < tokens.length; i++) {
                sb.append(' ').append(tokens[i]);
                sb.append(separator).append(" *"); // NOI18N
            }
            sb.append('/'); // NOI18N
        }
        else {
            sb.append(' ').append(tokens[0]);
            sb.append(separator).append(" */"); // NOI18N
        }
        return sb.toString();
    }
    
    
    public static class JavaDocTag implements Comparable {
        public static final HashMap nameValues = new HashMap(); 
        static {
            nameValues.put(TEXT_TAG_NAME, new Integer(1));
            nameValues.put("@author", new Integer(2)); // NOI18N
            nameValues.put("@version", new Integer(3)); // NOI18N
            nameValues.put("@param", new Integer(4)); // NOI18N
            nameValues.put("@return", new Integer(5)); // NOI18N
            nameValues.put("@exception", new Integer(6)); // NOI18N
            nameValues.put("@see", new Integer(7)); // NOI18N
            nameValues.put("@since", new Integer(8)); // NOI18N
            nameValues.put("@serial", new Integer(9)); // NOI18N
            nameValues.put("@deprecated", new Integer(10)); // NOI18N
        }
        
        String name;
        String text;
        
        /** Holds value of property startName. */
        private int startName;
        
        /** Holds value of property endName. */
        private int endName;
        
        /** Holds value of property startText. */
        private int startText;
        
        /** Holds value of property endText. */
        private int endText;
        
        
        public JavaDocTag(String name, String text) {
            this.name = name;
            this.text = text;
            startName = endName = startText = endText = -1;
        }
        
        JavaDocTag(String name, String text, int startName, int endName, int startText, int endText) {
            this.name = name;
            this.text = text;
            this.startName = startName;
            this.endName = endName;
            this.startText = startText;
            this.endText = endText;
        }
        
        public String toString() {
            return name+" "+text; // NOI18N
        }
        
        public String toInfo() {
            return startName+","+endName+"{"+name+"} "+startText+","+endText+"{"+text+"}"; // NOI18N
        }
        
        /**
         * Return the name of this tag.
         */
        public String getName() {
            return name;
        }
        
        /**
         * Return the kind of this tag.
         */
        public String getKind() {
            return name;
        }
        
        public boolean isText() {
            return TEXT_TAG_NAME.equals(getName());
        }
        
        /**
         * Return the text of this tag, that is, portion beyond tag name.
         */
        public String getValue() {
            return text;
        }
        
        /** Getter for property startName.
         * @return Value of property startName.
         *
         */
        public int getStartName() {
            return this.startName;
        }
        
        /** Getter for property endName.
         * @return Value of property endName.
         *
         */
        public int getEndName() {
            return this.endName;
        }
        
        /** Getter for property startText.
         * @return Value of property startText.
         *
         */
        public int getStartText() {
            return this.startText;
        }
        
        /** Getter for property endText.
         * @return Value of property endText.
         *
         */
        public int getEndText() {
            return this.endText;
        }
        
        private int getNameValue(String name) {
            Integer value = (Integer) nameValues.get(name.intern());
            if (value != null)
                return value.intValue();
            return 0;
        }
        
        public int compareTo(Object o) {
            if (o == null || !(o instanceof JavaDocTag))
              return -1;
            String name1 = getName();
            String name2 = ((JavaDocTag)o).getName();
            int value1 = getNameValue(name1);
            int value2 = getNameValue(name2);
            
            if (value1 > 0 && value2 > 0) {
                return value1-value2;
            }
            else if (value1 >0) {
                return -100;
            }
            else if (value2 >0) {
                return 100;
            }
            return name1.compareTo(name2);
        }
    }
    
    public static String readFile(File file) throws Exception {
        
        String fileContent = null;
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        //File file = new File(fileName);
        
        try {
            int count = bis.available();            
            byte[] b = new byte[count];
            int total_number_read = 0;
            
            while ( total_number_read < count ) {
                int number_read = bis.read(b, 0, count);
                if ( number_read == -1 )
                    break;
                total_number_read += number_read;
            }
            fileContent = new String(b, 0, total_number_read, "UTF-8"); // NOI18N
        }
        finally {
            bis.close();
        }
        return fileContent;
    }
    
    public static void main(String args[]) {
        if (args.length < 1)
            System.exit(0);
        
        String fileName = args[0];
        String fileContent = null;
        try {
            fileContent = readFile(new File(fileName));
        }
        catch (Exception ex) {
            System.out.println("JavaDocParser: Error reading file '"+fileName+"': "+ex.getMessage()); // NOI18N
        }
        
        JavaDocParser parser = new JavaDocParser(fileContent);
        JavaDocParser.JavaDocTag[] tags = parser.getTags();
        if (tags == null) {
            System.out.println("JavaDocParser: result is null"); // NOI18N
        }
        else if (tags.length == 0) {
            System.out.println("JavaDocParser: result is empty"); // NOI18N
        }
        else {
            for (int i = 0; i < tags.length; i++) {
                JavaDocParser.JavaDocTag tag = tags[i];
                System.out.println("JavaDocParser: Tag #"+(i+1)+": "+tag.toInfo()); // NOI18N
                
                if (tag.getStartName() >= 0 && tag.getEndName() >= 0 &&
                tag.getStartName() <= tag.getEndName()) {
                    String tagName = fileContent.substring(tag.getStartName(), tag.getEndName());
                    if (!tag.getName().equals(tagName)) {
                        System.out.println("Error in tag name: '"+tagName+"' - '"+tag.getName()+"'"); // NOI18N
                    }
                }
                else if (!tag.isText()) {
                    System.out.println("Error in tag name: '"+JavaDocParser.TEXT_TAG_NAME+"' - '"+tag.getName()+"'"); // NOI18N
                }
                
                //                if (tag.getStartText() >= 0 && tag.getEndText() >= 0 &&
                //                    tag.getStartText() <= tag.getEndText()) {
                //                    String tagText = fileContent.substring(tag.getStartText(), tag.getEndText());
                //                    if (!tag.getText().equals(tagText)) {
                //                        System.out.println("Error in tag text: '"+tagText+"' - '"+tag.getText()+"'");
                //                    }
                //                }
                //                else {
                //                    System.out.println("Error in tag text: 'null' - '"+tag.getName()+"'");
                //                }
            }
        }
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.