001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.checks.javadoc;
020
021import com.google.common.collect.Lists;
022import com.puppycrawl.tools.checkstyle.api.JavadocTagInfo;
023import com.puppycrawl.tools.checkstyle.api.TextBlock;
024import com.puppycrawl.tools.checkstyle.api.Utils;
025import java.util.List;
026import java.util.regex.Matcher;
027import java.util.regex.Pattern;
028
029/**
030 * Contains utility methods for working with Javadoc.
031 * @author Lyle Hanson
032 */
033public final class JavadocUtils
034{
035    ///CLOVER:OFF
036    /** prevent instantiation */
037    private JavadocUtils()
038    {
039    }
040    ///CLOVER:ON
041
042    /**
043     * Gets validTags from a given piece of Javadoc.
044     * @param aCmt the Javadoc comment to process.
045     * @param aTagType the type of validTags we're interested in
046     * @return all standalone validTags from the given javadoc.
047     */
048    public static JavadocTags getJavadocTags(TextBlock aCmt,
049                                             JavadocTagType aTagType)
050    {
051        final String[] text = aCmt.getText();
052        final List<JavadocTag> tags = Lists.newArrayList();
053        final List<InvalidJavadocTag> invalidTags = Lists.newArrayList();
054        Pattern blockTagPattern =
055            Utils.getPattern("/\\*{2,}\\s*@(\\p{Alpha}+)\\s");
056        for (int i = 0; i < text.length; i++) {
057            final String s = text[i];
058            final Matcher blockTagMatcher = blockTagPattern.matcher(s);
059            if ((aTagType.equals(JavadocTagType.ALL) || aTagType
060                    .equals(JavadocTagType.BLOCK)) && blockTagMatcher.find())
061            {
062                final String tagName = blockTagMatcher.group(1);
063                String content = s.substring(blockTagMatcher.end(1));
064                if (content.endsWith("*/")) {
065                    content = content.substring(0, content.length() - 2);
066                }
067                final int line = aCmt.getStartLineNo() + i;
068                int col = blockTagMatcher.start(1) - 1;
069                if (i == 0) {
070                    col += aCmt.getStartColNo();
071                }
072                if (JavadocTagInfo.isValidName(tagName)) {
073                    tags.add(
074                        new JavadocTag(line, col, tagName, content.trim()));
075                }
076                else {
077                    invalidTags.add(new InvalidJavadocTag(line, col, tagName));
078                }
079            }
080            // No block tag, so look for inline validTags
081            else if (aTagType.equals(JavadocTagType.ALL)
082                    || aTagType.equals(JavadocTagType.INLINE))
083            {
084                // Match JavaDoc text after comment characters
085                final Pattern commentPattern =
086                    Utils.getPattern("^\\s*(?:/\\*{2,}|\\*+)\\s*(.*)");
087                final Matcher commentMatcher = commentPattern.matcher(s);
088                final String commentContents;
089                final int commentOffset; // offset including comment characters
090                if (!commentMatcher.find()) {
091                    commentContents = s; // No leading asterisks, still valid
092                    commentOffset = 0;
093                }
094                else {
095                    commentContents = commentMatcher.group(1);
096                    commentOffset = commentMatcher.start(1) - 1;
097                }
098                final Pattern tagPattern =
099                    Utils.getPattern(".*?\\{@(\\p{Alpha}+)\\s+(.*?)\\}");
100                final Matcher tagMatcher = tagPattern.matcher(commentContents);
101                while (tagMatcher.find()) {
102                    if (tagMatcher.groupCount() == 2) {
103                        final String tagName = tagMatcher.group(1);
104                        final String tagValue = tagMatcher.group(2).trim();
105                        final int line = aCmt.getStartLineNo() + i;
106                        int col = commentOffset + (tagMatcher.start(1) - 1);
107                        if (i == 0) {
108                            col += aCmt.getStartColNo();
109                        }
110                        if (JavadocTagInfo.isValidName(tagName)) {
111                            tags.add(new JavadocTag(line, col, tagName,
112                                    tagValue));
113                        }
114                        else {
115                            invalidTags.add(new InvalidJavadocTag(line, col,
116                                    tagName));
117                        }
118                    }
119                    // else Error: Unexpected match count for inline JavaDoc
120                    // tag!
121                }
122            }
123            blockTagPattern =
124                Utils.getPattern("^\\s*\\**\\s*@(\\p{Alpha}+)\\s");
125        }
126        return new JavadocTags(tags, invalidTags);
127    }
128
129    /**
130     * The type of Javadoc tag we want returned.
131     */
132    public enum JavadocTagType
133    {
134        /** block type. */
135        BLOCK,
136        /** inline type. */
137        INLINE,
138        /** all validTags. */
139        ALL;
140    }
141}