EMMA Coverage Report (generated Thu Nov 26 15:54:18 CST 2009)
[all classes][org.eclipse.pde.api.tools.internal.builder]

COVERAGE SUMMARY FOR SOURCE FILE [TagValidator.java]

nameclass, %method, %block, %line, %
TagValidator.java100% (1/1)100% (11/11)99%  (722/731)97%  (180/186)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TagValidator100% (1/1)100% (11/11)99%  (722/731)97%  (180/186)
getParentModifiers (ASTNode): int 100% (1/1)89%  (16/18)83%  (5/6)
getParentKind (ASTNode): int 100% (1/1)93%  (28/30)89%  (8/9)
processTagProblem (String, TagElement, int, int, int, String): void 100% (1/1)96%  (87/91)88%  (21/24)
processTags (String, List, IApiJavadocTag [], int, String): void 100% (1/1)99%  (99/100)95%  (19/20)
<static initializer> 100% (1/1)100% (4/4)100% (2/2)
TagValidator (ICompilationUnit): void 100% (1/1)100% (12/12)100% (5/5)
getTagProblems (): IApiProblem [] 100% (1/1)100% (15/15)100% (3/3)
getTypeName (ASTNode): String 100% (1/1)100% (7/7)100% (1/1)
getTypeName (ASTNode, StringBuffer): String 100% (1/1)100% (61/61)100% (14/14)
validateTags (ASTNode, List): void 100% (1/1)100% (379/379)100% (97/97)
visit (Javadoc): boolean 100% (1/1)100% (14/14)100% (5/5)

1/*******************************************************************************
2 * Copyright (c) 2008, 2009 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *     IBM Corporation - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.pde.api.tools.internal.builder;
12 
13import java.util.ArrayList;
14import java.util.HashSet;
15import java.util.Iterator;
16import java.util.List;
17import java.util.Set;
18 
19import org.eclipse.core.runtime.CoreException;
20import org.eclipse.jdt.core.Flags;
21import org.eclipse.jdt.core.ICompilationUnit;
22import org.eclipse.jdt.core.JavaModelException;
23import org.eclipse.jdt.core.dom.ASTNode;
24import org.eclipse.jdt.core.dom.ASTVisitor;
25import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
26import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
27import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
28import org.eclipse.jdt.core.dom.CompilationUnit;
29import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
30import org.eclipse.jdt.core.dom.EnumDeclaration;
31import org.eclipse.jdt.core.dom.FieldDeclaration;
32import org.eclipse.jdt.core.dom.Javadoc;
33import org.eclipse.jdt.core.dom.MethodDeclaration;
34import org.eclipse.jdt.core.dom.PackageDeclaration;
35import org.eclipse.jdt.core.dom.TagElement;
36import org.eclipse.jdt.core.dom.TypeDeclaration;
37import org.eclipse.jface.text.BadLocationException;
38import org.eclipse.jface.text.IDocument;
39import org.eclipse.pde.api.tools.internal.JavadocTagManager;
40import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory;
41import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
42import org.eclipse.pde.api.tools.internal.provisional.IApiJavadocTag;
43import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants;
44import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor;
45import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem;
46import org.eclipse.pde.api.tools.internal.util.Util;
47 
48/**
49 * Visit Javadoc comments of types and member to find API javadoc tags that are being misused
50 * 
51 * @since 1.0.0
52 */
53public class TagValidator extends ASTVisitor {
54 
55        /**
56         * backing collection of tag problems, if any
57         */
58        private ArrayList fTagProblems = null;
59        
60        private ICompilationUnit fCompilationUnit = null;
61        
62        private static final IApiJavadocTag[] NO_TAGS = new IApiJavadocTag[0];
63        
64        /**
65         * Constructor
66         * @param parent
67         */
68        public TagValidator(ICompilationUnit parent) {
69                fCompilationUnit = parent;
70        }
71        
72        /* (non-Javadoc)
73         * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Javadoc)
74         */
75        public boolean visit(Javadoc node) {
76                ASTNode parent = node.getParent();
77                if(parent != null) {
78                        List tags = node.tags();
79                        validateTags(parent, tags);
80                }
81                return false;
82        }
83 
84        /**
85         * Validates the set of tags for the given parent node and the given listing of {@link TagElement}s
86         * @param node
87         * @param tags
88         */
89        private void validateTags(ASTNode node, List tags) {
90                if(tags.size() == 0) {
91                        return;
92                }
93                JavadocTagManager jtm = ApiPlugin.getJavadocTagManager();
94                switch(node.getNodeType()) {
95                        case ASTNode.TYPE_DECLARATION: {
96                                TypeDeclaration type = (TypeDeclaration) node;
97                                IApiJavadocTag[] validtags = jtm.getTagsForType(type.isInterface() ? IApiJavadocTag.TYPE_INTERFACE : IApiJavadocTag.TYPE_CLASS, IApiJavadocTag.MEMBER_NONE);
98                                HashSet invalidtags = new HashSet(validtags.length);
99                                String context = BuilderMessages.TagValidator_an_interface;
100                                if(!type.isInterface()) {
101                                        context = BuilderMessages.TagValidator_a_class;
102                                        if(Flags.isAbstract(type.getModifiers())) {
103                                                context = BuilderMessages.TagValidator_an_abstract_class;
104                                                invalidtags.add("@noinstantiate"); //$NON-NLS-1$
105                                        }
106                                        if(Flags.isFinal(type.getModifiers())) {
107                                                context = BuilderMessages.TagValidator_a_final_class;
108                                                invalidtags.add("@noextend"); //$NON-NLS-1$
109                                        }
110                                }
111                                if(invalidtags.size() > 0) {
112                                        ArrayList vtags = new ArrayList(validtags.length);
113                                        for(int i = 0; i < validtags.length; i++) {
114                                                if(invalidtags.contains(validtags[i].getTagName())) {
115                                                        continue;
116                                                }
117                                                vtags.add(validtags[i]);
118                                        }
119                                        validtags = (IApiJavadocTag[]) vtags.toArray(new IApiJavadocTag[vtags.size()]);
120                                }
121                                processTags(getTypeName(type), tags, validtags, IElementDescriptor.TYPE, context);
122                                break;
123                        }
124                        case ASTNode.ENUM_DECLARATION: {
125                                EnumDeclaration enumm = (EnumDeclaration) node;
126                                IApiJavadocTag[] validtags = jtm.getTagsForType(IApiJavadocTag.TYPE_ENUM, IApiJavadocTag.MEMBER_NONE);
127                                processTags(getTypeName(enumm), tags, validtags, IElementDescriptor.TYPE, BuilderMessages.TagValidator_an_enum);
128                                break;
129                        }
130                        case ASTNode.ENUM_CONSTANT_DECLARATION: {
131                                EnumConstantDeclaration decl = (EnumConstantDeclaration) node;
132                                processTags(getTypeName(decl), tags, new IApiJavadocTag[0], IElementDescriptor.FIELD, BuilderMessages.TagValidator_an_enum_constant);
133                                break;
134                        }
135                        case ASTNode.ANNOTATION_TYPE_DECLARATION: {
136                                AnnotationTypeDeclaration annot = (AnnotationTypeDeclaration) node;
137                                IApiJavadocTag[] validtags = jtm.getTagsForType(IApiJavadocTag.TYPE_ANNOTATION, IApiJavadocTag.MEMBER_NONE);
138                                processTags(getTypeName(annot), tags, validtags, IElementDescriptor.TYPE, BuilderMessages.TagValidator_an_annotation);
139                                break;
140                        }
141                        case ASTNode.METHOD_DECLARATION: {
142                                MethodDeclaration method = (MethodDeclaration) node;
143                                int pkind = getParentKind(node);
144                                String context = null;
145                                int mods = method.getModifiers();
146                                boolean isprivate = Flags.isPrivate(mods);
147                                boolean isconstructor = method.isConstructor();
148                                boolean isfinal = Flags.isFinal(mods);
149                                boolean isstatic = Flags.isStatic(mods);
150                                boolean pfinal = false;
151                                switch(pkind) {
152                                        case IApiJavadocTag.TYPE_ENUM: {
153                                                context = isprivate ? BuilderMessages.TagValidator_private_enum_method : BuilderMessages.TagValidator_an_enum_method;
154                                                break;
155                                        }
156                                        case IApiJavadocTag.TYPE_INTERFACE: {
157                                                context = BuilderMessages.TagValidator_an_interface_method;
158                                                break;
159                                        }
160                                        default: {
161                                                pfinal = Flags.isFinal(getParentModifiers(method));
162                                                if(isprivate) {
163                                                        context = isconstructor ? BuilderMessages.TagValidator_private_constructor : BuilderMessages.TagValidator_private_method;
164                                                }
165                                                else if(isstatic && isfinal) {
166                                                        context = BuilderMessages.TagValidator_a_static_final_method;
167                                                }
168                                                else if (isfinal) {
169                                                        context = BuilderMessages.TagValidator_a_final_method;
170                                                }
171                                                else if(isstatic) {
172                                                        context = BuilderMessages.TagValidator_a_static_method;
173                                                }
174                                                else if(pfinal) {
175                                                        context = BuilderMessages.TagValidator_a_method_in_a_final_class;
176                                                }
177                                                else {
178                                                        context = isconstructor ? BuilderMessages.TagValidator_a_constructor : BuilderMessages.TagValidator_a_method;
179                                                }
180                                                break;
181                                        }
182                                }
183                                IApiJavadocTag[] validtags = NO_TAGS;
184                                if(!isprivate) {
185                                        validtags = jtm.getTagsForType(pkind, isconstructor ? IApiJavadocTag.MEMBER_CONSTRUCTOR : IApiJavadocTag.MEMBER_METHOD);
186                                }
187                                if(isfinal || isstatic || pfinal) {
188                                        ArrayList ttags = new ArrayList(validtags.length);
189                                        for(int i = 0; i < validtags.length; i++) {
190                                                if(!validtags[i].getTagName().equals("@nooverride")) { //$NON-NLS-1$
191                                                        ttags.add(validtags[i]);
192                                                }
193                                        }
194                                        validtags = (IApiJavadocTag[]) ttags.toArray(new IApiJavadocTag[ttags.size()]);
195                                }
196                                processTags(getTypeName(method), tags, validtags, IElementDescriptor.METHOD, context);
197                                break;
198                        }
199                        case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: {
200                                AnnotationTypeMemberDeclaration decl = (AnnotationTypeMemberDeclaration) node;
201                                IApiJavadocTag[] validtags = jtm.getTagsForType(IApiJavadocTag.TYPE_ANNOTATION, IApiJavadocTag.MEMBER_METHOD);
202                                processTags(getTypeName(decl), tags, validtags, IElementDescriptor.METHOD, BuilderMessages.TagValidator_an_annotation_method);
203                                break;
204                        }
205                        case ASTNode.FIELD_DECLARATION: {
206                                FieldDeclaration field = (FieldDeclaration) node;
207                                int pkind = getParentKind(node);
208                                String context = null;
209                                boolean isfinal = Flags.isFinal(field.getModifiers());
210                                boolean isprivate = Flags.isPrivate(field.getModifiers());
211                                switch(pkind) {
212                                        case IApiJavadocTag.TYPE_ANNOTATION: {
213                                                context = BuilderMessages.TagValidator_annotation_field;
214                                                if(isfinal) {
215                                                        context = BuilderMessages.TagValidator_a_final_annotation_field;
216                                                }
217                                                break;
218                                        }
219                                        case IApiJavadocTag.TYPE_ENUM: {
220                                                context = isprivate ? BuilderMessages.TagValidator_private_enum_field : BuilderMessages.TagValidator_enum_field;
221                                                break;
222                                        }
223                                        default: {
224                                                if(isprivate) {
225                                                        context = BuilderMessages.TagValidator_private_field;
226                                                }
227                                                else {
228                                                        context = isfinal ? BuilderMessages.TagValidator_a_final_field : BuilderMessages.TagValidator_a_field;
229                                                }
230                                                break;
231                                        }
232                                }
233                                IApiJavadocTag[] validtags = NO_TAGS;
234                                if(!isprivate && !isfinal) {
235                                        validtags = jtm.getTagsForType(pkind, IApiJavadocTag.MEMBER_FIELD);
236                                }
237                                processTags(getTypeName(field), tags, validtags, IElementDescriptor.FIELD, context);
238                                break;
239                        }
240                }
241        }
242        
243        /**
244         * Returns the modifiers from the smallest enclosing type containing the given node
245         * @param node
246         * @return the modifiers for the smallest enclosing type or 0
247         */
248        private int getParentModifiers(ASTNode node) {
249                if(node == null) {
250                        return 0;
251                }
252                if(node instanceof AbstractTypeDeclaration) {
253                        AbstractTypeDeclaration type = (AbstractTypeDeclaration) node;
254                        return type.getModifiers();
255                }
256                return getParentModifiers(node.getParent());
257        }
258        
259        /**
260         * Returns the fully qualified name of the enclosing type for the given node
261         * @param node
262         * @return the fully qualified name of the enclosing type
263         */
264        private String getTypeName(ASTNode node) {
265                return getTypeName(node, new StringBuffer());
266        }
267        
268        /**
269         * Constructs the qualified name of the enclosing parent type
270         * @param node the node to get the parent name for
271         * @param buffer the buffer to write the name into
272         * @return the fully qualified name of the parent 
273         */
274        private String getTypeName(ASTNode node, StringBuffer buffer) {
275                switch(node.getNodeType()) {
276                        case ASTNode.COMPILATION_UNIT : {
277                                CompilationUnit unit = (CompilationUnit) node;
278                                PackageDeclaration packageDeclaration = unit.getPackage();
279                                if (packageDeclaration != null) {
280                                        buffer.insert(0, '.');
281                                        buffer.insert(0, packageDeclaration.getName().getFullyQualifiedName());
282                                }
283                                return String.valueOf(buffer);
284                        }
285                        default : {
286                                if (node instanceof AbstractTypeDeclaration) {
287                                        AbstractTypeDeclaration typeDeclaration = (AbstractTypeDeclaration) node;
288                                        if (typeDeclaration.isPackageMemberTypeDeclaration()) {
289                                                buffer.insert(0, typeDeclaration.getName().getIdentifier());
290                                        }
291                                        else {
292                                                buffer.insert(0, typeDeclaration.getName().getFullyQualifiedName());
293                                                buffer.insert(0, '$');
294                                        }
295                                }
296                        }
297                }
298                return getTypeName(node.getParent(), buffer);
299        }
300 
301        /**
302         * Processes the listing of valid tags against the listing of existing tags on the node, and
303         * creates errors if disallowed tags are found.
304         * @param tags
305         * @param validtags
306         * @param element
307         * @param context
308         */
309        private void processTags(String typeName, List tags, IApiJavadocTag[] validtags, int element, String context) {
310                IApiJavadocTag[] alltags = ApiPlugin.getJavadocTagManager().getAllTags();
311                Set tagnames = ApiPlugin.getJavadocTagManager().getAllTagNames();
312                HashSet invalidtags = new HashSet(alltags.length);
313                for(int i = 0; i < alltags.length; i++) {
314                        invalidtags.add(alltags[i].getTagName());
315                }
316                for(int i = 0; i < validtags.length; i++) {
317                        invalidtags.remove(validtags[i]);
318                }
319                if(invalidtags.size() == 0) {
320                        return;
321                }
322                TagElement tag = null;
323                HashSet tagz = new HashSet(tags.size());
324                String tagname = null;
325                for(Iterator iter = tags.iterator(); iter.hasNext();) {
326                        tag = (TagElement) iter.next();
327                        tagname = tag.getTagName();
328                        if(invalidtags.contains(tag.getTagName())) {
329                                processTagProblem(typeName, tag, element, IApiProblem.UNSUPPORTED_TAG_USE, IApiMarkerConstants.UNSUPPORTED_TAG_MARKER_ID, context);
330                        }
331                        if(tagnames.contains(tag.getTagName()) && !tagz.add(tagname)) {
332                                processTagProblem(typeName, tag, element, IApiProblem.DUPLICATE_TAG_USE, IApiMarkerConstants.DUPLICATE_TAG_MARKER_ID, null);
333                        }
334                }
335        }
336        
337        /**
338         * Creates a new {@link IApiProblem} for the given tag and adds it to the cache
339         * @param tag
340         * @param element
341         * @param context
342         */
343        private void processTagProblem(String typeName, TagElement tag, int element, int kind, int markerid, String context) {
344                if(fTagProblems == null) {
345                        fTagProblems = new ArrayList(10);
346                }
347                int charstart = tag.getStartPosition();
348                int charend = charstart + tag.getTagName().length();
349                int linenumber = -1;
350                try {
351                        // unit cannot be null
352                        IDocument document = Util.getDocument(fCompilationUnit);
353                        linenumber = document.getLineOfOffset(charstart);
354                } 
355                catch (BadLocationException e) {} 
356                catch (CoreException e) {}
357                try {
358                        IApiProblem problem = ApiProblemFactory.newApiProblem(fCompilationUnit.getCorrespondingResource().getProjectRelativePath().toPortableString(),
359                                        typeName,
360                                        new String[] {tag.getTagName(), context},
361                                        new String[] {IApiMarkerConstants.API_MARKER_ATTR_ID, IApiMarkerConstants.MARKER_ATTR_HANDLE_ID}, 
362                                        new Object[] {new Integer(markerid), fCompilationUnit.getHandleIdentifier()}, 
363                                        linenumber,
364                                        charstart,
365                                        charend,
366                                        IApiProblem.CATEGORY_USAGE,
367                                        element,
368                                        kind,
369                                        IApiProblem.NO_FLAGS);
370                        
371                        fTagProblems.add(problem);
372                } 
373                catch (JavaModelException e) {}
374        }
375        
376        /**
377         * Returns the {@link IApiJavadocTag} kind of the parent {@link ASTNode} for the given 
378         * node or -1 if the parent is not found or not a {@link TypeDeclaration}
379         * @param node
380         * @return the {@link IApiJavadocTag} kind of the parent or -1
381         */
382        private int getParentKind(ASTNode node) {
383                if(node == null) {
384                        return -1;
385                }
386                if(node instanceof TypeDeclaration) {
387                        return ((TypeDeclaration)node).isInterface() ? IApiJavadocTag.TYPE_INTERFACE : IApiJavadocTag.TYPE_CLASS;
388                }
389                else if(node instanceof AnnotationTypeDeclaration) {
390                        return IApiJavadocTag.TYPE_ANNOTATION;
391                }
392                else if(node instanceof EnumDeclaration) {
393                        return IApiJavadocTag.TYPE_ENUM;
394                }
395                return getParentKind(node.getParent());
396        }
397        
398        /**
399         * Returns the complete listing of API tag problems found during the scan or 
400         * an empty array, never <code>null</code>
401         * @return the complete listing of API tag problems found
402         */
403        public IApiProblem[] getTagProblems() {
404                if(fTagProblems == null) {
405                        return new IApiProblem[0];
406                }
407                return (IApiProblem[]) fTagProblems.toArray(new IApiProblem[fTagProblems.size()]);
408        }
409}

[all classes][org.eclipse.pde.api.tools.internal.builder]
EMMA 2.0.5312 EclEmma Fix 1 (C) Vladimir Roubtsov