001: /**
002: * Spoon - http://spoon.gforge.inria.fr/
003: * Copyright (C) 2006 INRIA Futurs <renaud.pawlak@inria.fr>
004: *
005: * This software is governed by the CeCILL-C License under French law and
006: * abiding by the rules of distribution of free software. You can use,
007: * modify and/or redistribute the software under the terms of the
008: * CeCILL-C
009: * license as circulated by CEA, CNRS and INRIA at the following URL:
010: * http://www.cecill.info.
011: *
012: * This program is distributed in the hope that it will be useful, but
013: * WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C
015: * License for more details.
016: *
017: * The fact that you are presently reading this means that you have had
018: * knowledge of the CeCILL-C license and that you accept its terms.
019: */package spoon.aval.processing;
020:
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: import spoon.processing.AbstractProcessor;
025: import spoon.processing.Property;
026: import spoon.processing.Severity;
027: import spoon.reflect.Factory;
028: import spoon.reflect.declaration.CtAnnotationType;
029: import spoon.reflect.declaration.CtField;
030: import spoon.reflect.declaration.CtPackage;
031: import spoon.reflect.reference.CtExecutableReference;
032: import spoon.reflect.reference.CtPackageReference;
033: import spoon.reflect.reference.CtReference;
034: import spoon.reflect.reference.CtTypeReference;
035: import spoon.reflect.visitor.CtScanner;
036:
037: /**
038: * This processor visits all the declarations of annotations in the program,
039: * checking that they are processed.
040: * <p>
041: * The completeness checker verifies that for every annotation type defined in
042: * the application there is a class that uses it. This is performed by taking
043: * each declaration, and checking that there is at least a reference to the
044: * annotation type, and a reference to each of the attributes defined in it.
045: *
046: * <p>
047: * The places where the reference is going to be checked are described by a list
048: * of packages, so it is possible to restrict the check to a subset of the
049: * system (for example only to unit tests)
050: *
051: *
052: */
053:
054: public class CompletenessCheckerProcessor extends
055: AbstractProcessor<CtAnnotationType<?>> {
056:
057: private List<CtPackage> framework;
058:
059: private List<CtPackageReference> annotations;
060:
061: @Property
062: String[] checkPackages;
063:
064: @Property
065: String[] skipAnnotationsIn;
066:
067: @Property
068: boolean checkAll = false;
069:
070: /**
071: * Initializes the package list.
072: * <p>
073: * This method is called by spoon
074: */
075: @Override
076: public void init() {
077: super .init();
078: framework = new ArrayList<CtPackage>();
079: annotations = new ArrayList<CtPackageReference>();
080: configurePackages();
081: }
082:
083: /**
084: * Overload this method to change the list of packages in which to chek the
085: * references.
086: */
087: protected void configurePackages() {
088: // framework.addAll(getFactory().Package().getCreatedPackages());
089: // annotations.add(getFactory().Package().createReference("spoon.aval"));
090: // annotations.add(getFactory().Package().createReference("java"));
091:
092: Factory fac = getFactory();
093: if (checkAll) {
094: framework.addAll(getFactory().Package().getAll());
095: } else {
096: for (String check : checkPackages) {
097: framework.add(fac.Package().getOrCreate(check));
098: }
099: }
100:
101: for (String skip : skipAnnotationsIn) {
102: annotations.add(fac.Package().createReference(skip));
103: }
104:
105: }
106:
107: /**
108: * Processes each CtAnnotationType, checking that it is processed.
109: * <p>
110: * This method is called by spoon
111: */
112: public void process(CtAnnotationType<?> element) {
113:
114: if (shouldSkip(element))
115: return;
116:
117: checkTypeUse(element);
118: List<CtField<?>> fields = element.getFields();
119:
120: for (CtField<?> field : fields) {
121: checkAccess(field);
122: }
123:
124: }
125:
126: private boolean shouldSkip(CtAnnotationType<?> element) {
127: CtPackageReference elPackRef = element.getPackage()
128: .getReference();
129: for (CtPackageReference ref : annotations) {
130: if (elPackRef.getSimpleName().startsWith(
131: ref.getSimpleName())) {
132: return true;
133: }
134: }
135: return false;
136: }
137:
138: private void checkAccess(final CtField<?> field) {
139: final boolean[] found = new boolean[1];
140: found[0] = false;
141: for (CtPackage pack : framework) {
142: new CtScanner() {
143: @Override
144: public void scan(CtReference reference) {
145: if (reference instanceof CtExecutableReference) {
146: CtExecutableReference ref = (CtExecutableReference) reference;
147: if (ref.getSimpleName().equals(
148: field.getSimpleName())
149: && ref.getDeclaringType().equals(
150: field.getDeclaringType()
151: .getReference())) {
152: found[0] = true;
153: }
154: }
155: super .scan(reference);
156: }
157: }.scan(pack);
158: }
159:
160: if (!found[0])
161: getFactory().getEnvironment().report(this ,
162: Severity.WARNING, field,
163: "This Field is never processed!");
164:
165: }
166:
167: private void checkTypeUse(final CtAnnotationType<?> element) {
168: final boolean[] found = new boolean[1];
169: found[0] = false;
170: for (CtPackage pack : framework) {
171: new CtScanner() {
172: @Override
173: public void scan(CtReference reference) {
174: if (reference instanceof CtTypeReference<?>) {
175: CtTypeReference<?> ref = (CtTypeReference<?>) reference;
176: if (ref.equals(element.getReference())) {
177: found[0] = true;
178: }
179: }
180: super .scan(reference);
181: }
182: }.scan(pack);
183: }
184:
185: if (!found[0])
186: getFactory().getEnvironment().report(this ,
187: Severity.WARNING, element,
188: "This annotation is never processed!");
189:
190: }
191:
192: }
|