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, modify
007: * and/or redistribute the software under the terms of the CeCILL-C license as
008: * circulated by CEA, CNRS and INRIA at http://www.cecill.info.
009: *
010: * This program is distributed in the hope that it will be useful, but WITHOUT
011: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
013: *
014: * The fact that you are presently reading this means that you have had
015: * knowledge of the CeCILL-C license and that you accept its terms.
016: */
017:
018: package spoon.processing;
019:
020: import java.lang.annotation.Annotation;
021: import java.lang.reflect.Method;
022: import java.util.ArrayList;
023: import java.util.Map;
024: import java.util.Set;
025: import java.util.TreeMap;
026: import java.util.TreeSet;
027:
028: import spoon.reflect.declaration.CtAnnotation;
029: import spoon.reflect.declaration.CtElement;
030:
031: /**
032: * This class defines an abstract annotation processor to be subclassed by the
033: * user for defining new annotation processors.
034: */
035: public abstract class AbstractAnnotationProcessor<A extends Annotation, E extends CtElement>
036: extends AbstractProcessor<E> implements
037: AnnotationProcessor<A, E> {
038:
039: Map<String, Class<? extends A>> consumedAnnotationTypes = new TreeMap<String, Class<? extends A>>();
040:
041: Map<String, Class<? extends A>> processedAnnotationTypes = new TreeMap<String, Class<? extends A>>();
042:
043: /**
044: * Empty constructor only for all processors (invoked by Spoon).
045: */
046: @SuppressWarnings("unchecked")
047: public AbstractAnnotationProcessor() {
048: super ();
049: clearProcessedElementType();
050:
051: for (Method m : getClass().getMethods()) {
052: if (m.getName().equals("process")
053: && (m.getParameterTypes().length == 2)) {
054: Class c = m.getParameterTypes()[0];
055: if (inferConsumedAnnotationType()
056: && (Annotation.class != c)) {
057: addConsumedAnnotationType(c);
058: }
059: c = m.getParameterTypes()[1];
060: if (CtElement.class != c) {
061: addProcessedElementType(c);
062: }
063: }
064: }
065: if (inferConsumedAnnotationType()
066: && processedAnnotationTypes.isEmpty()) {
067: addProcessedAnnotationType((Class<? extends A>) Annotation.class);
068: }
069: if (processedElementTypes.isEmpty()) {
070: addProcessedElementType(CtElement.class);
071: }
072: }
073:
074: /**
075: * Adds a consumed annotation type (to be used in subclasses constructors).
076: * A consumed annotation type is also part of the processed annotation
077: * types.
078: */
079: final protected void addConsumedAnnotationType(
080: Class<? extends A> annotationType) {
081: addProcessedAnnotationType(annotationType);
082: consumedAnnotationTypes.put(annotationType.getName(),
083: annotationType);
084: }
085:
086: /**
087: * Adds a processed annotation type (to be used in subclasses constructors).
088: */
089: final protected void addProcessedAnnotationType(
090: Class<? extends A> annotationType) {
091: processedAnnotationTypes.put(annotationType.getName(),
092: annotationType);
093: }
094:
095: /**
096: * Removes a processed annotation type.
097: */
098: final protected void removeProcessedAnnotationType(
099: Class<? extends A> annotationType) {
100: processedAnnotationTypes.remove(annotationType.getName());
101: }
102:
103: /**
104: * Clears the processed annotation types.
105: */
106: final protected void clearProcessedAnnotationTypes() {
107: processedAnnotationTypes.clear();
108: }
109:
110: /**
111: * Clears the consumed annotation types.
112: */
113: final protected void clearConsumedAnnotationTypes() {
114: consumedAnnotationTypes.clear();
115: }
116:
117: /**
118: * Removes a processed annotation type.
119: */
120: final protected void removeConsumedAnnotationType(
121: Class<? extends A> annotationType) {
122: consumedAnnotationTypes.remove(annotationType.getName());
123: }
124:
125: final public Set<Class<? extends A>> getConsumedAnnotationTypes() {
126: return new TreeSet<Class<? extends A>>(consumedAnnotationTypes
127: .values());
128: }
129:
130: final public Set<Class<? extends A>> getProcessedAnnotationTypes() {
131: return new TreeSet<Class<? extends A>>(processedAnnotationTypes
132: .values());
133: }
134:
135: public boolean inferConsumedAnnotationType() {
136: return true;
137: }
138:
139: /**
140: * Returns true if the element is annotated with an annotation whose type is
141: * processed.
142: */
143: @Override
144: final public boolean isToBeProcessed(E element) {
145: if ((element != null) && (element.getAnnotations() != null)) {
146: for (CtAnnotation<? extends Annotation> a : element
147: .getAnnotations()) {
148: if (shoudBeProcessed(a)) {
149: return true;
150: }
151: }
152: }
153: return false;
154: }
155:
156: @SuppressWarnings("unchecked")
157: final public void process(E element) {
158: for (CtAnnotation<? extends Annotation> annotation : new ArrayList<CtAnnotation>(
159: element.getAnnotations())) {
160: if (shoudBeProcessed(annotation)) {
161: try {
162: process((A) annotation.getActualAnnotation(),
163: element);
164: } catch (Exception e) {
165: e.printStackTrace();
166: }
167: if (shoudBeConsumed(annotation)) {
168: element.getAnnotations().remove(annotation);
169: }
170: }
171: }
172: }
173:
174: private boolean shoudBeConsumed(
175: CtAnnotation<? extends Annotation> annotation) {
176: if (consumedAnnotationTypes.containsKey(annotation
177: .getAnnotationType().getQualifiedName())) {
178: return true;
179: }
180: return false;
181: }
182:
183: private boolean shoudBeProcessed(
184: CtAnnotation<? extends Annotation> annotation) {
185: if (processedAnnotationTypes.containsKey(annotation
186: .getAnnotationType().getQualifiedName())) {
187: return true;
188: }
189: return false;
190: }
191:
192: }
|