001: /*
002: * Spoon - http://spoon.gforge.inria.fr/
003: * Copyright (C) 2006 INRIA Futurs
004: * <renaud.pawlak@inria.fr,Didier.Donsez@ieee.org>
005: *
006: * This software is governed by the CeCILL-C License under French law and
007: * abiding by the rules of distribution of free software. You can use, modify
008: * and/or redistribute the software under the terms of the CeCILL-C license as
009: * circulated by CEA, CNRS and INRIA at http://www.cecill.info.
010: *
011: * This program is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
014: *
015: * The fact that you are presently reading this means that you have had
016: * knowledge of the CeCILL-C license and that you accept its terms.
017: */
018:
019: package spoon.processing;
020:
021: import java.io.IOException;
022: import java.lang.annotation.Annotation;
023: import java.util.ArrayList;
024: import java.util.List;
025:
026: import javax.xml.parsers.DocumentBuilder;
027: import javax.xml.parsers.DocumentBuilderFactory;
028: import javax.xml.parsers.ParserConfigurationException;
029:
030: import org.w3c.dom.Document;
031: import org.w3c.dom.Element;
032: import org.w3c.dom.NodeList;
033: import org.xml.sax.SAXException;
034: import org.xml.sax.SAXParseException;
035:
036: import spoon.reflect.declaration.CtClass;
037: import spoon.reflect.declaration.CtElement;
038: import spoon.reflect.declaration.CtExecutable;
039: import spoon.reflect.declaration.CtField;
040: import spoon.reflect.declaration.CtSimpleType;
041: import spoon.reflect.declaration.CtType;
042: import spoon.reflect.reference.CtTypeReference;
043:
044: /**
045: * A processor to add/replace/override/remove annotations described in an XML
046: * file. This version is based on java.util.regexp, but it can be subclassed to
047: * implement other types of matchings. Note that the used XML file is defined by
048: * the property {@link #xmlPath}, which can be defined in a property file or a
049: * Spoonlet file (spoon.xml) depending on your environment.
050: *
051: * @author Didier Donsez
052: * @author Renaud Pawlak
053: */
054: public class XMLAnnotationProcessor extends AbstractManualProcessor {
055:
056: /**
057: * This property contains the path of the XML file that describes the
058: * annotations of the processed program. By default, the file is the
059: * annotations.xml file contained in the current directory.
060: */
061: @Property
062: public String xmlPath = "annotations.xml";
063:
064: private Document document;
065:
066: /**
067: * Creates a new XMLAnnotationProcessor.
068: */
069: public XMLAnnotationProcessor() {
070: }
071:
072: final public void init() {
073: // Initiate DocumentBuilderFactory
074: DocumentBuilderFactory factory = DocumentBuilderFactory
075: .newInstance();
076:
077: // To get a validating parser
078: factory.setValidating(false); // since no DTD/XML Schema is defined
079: // To get one that understands namespaces
080: factory.setNamespaceAware(true);
081:
082: try {
083: // Get DocumentBuilder
084: DocumentBuilder builder = factory.newDocumentBuilder();
085: // Parse and load into memory the Document
086: document = builder.parse(getClass().getClassLoader()
087: .getResourceAsStream(xmlPath));
088: } catch (SAXParseException spe) {
089: // Error generated by the parser
090: getEnvironment().report(
091: this ,
092: Severity.ERROR,
093: "XML parsing error line " + spe.getLineNumber()
094: + ", uri " + spe.getSystemId());
095: // Use the contained exception, if any
096: Exception x = spe;
097: if (spe.getException() != null)
098: x = spe.getException();
099: x.printStackTrace();
100: } catch (SAXException sxe) {
101: // Error generated during parsing
102: Exception x = sxe;
103: if (sxe.getException() != null)
104: x = sxe.getException();
105: x.printStackTrace();
106: } catch (ParserConfigurationException pce) {
107: // Parser with specified options can't be built
108: pce.printStackTrace();
109: } catch (IOException ioe) {
110: // I/O error
111: ioe.printStackTrace();
112: }
113: }
114:
115: /**
116: * Override this method to create other types of matchings than the defaut
117: * one (java.util.regexp).
118: *
119: * @param type
120: * the canditate type
121: * @param typeExpression
122: * and expression to match the type against.
123: * @return true if the type matches the expression
124: * @see CtElement#getSignature()
125: */
126: protected boolean isTypeMatching(CtSimpleType<?> type,
127: String typeExpression) {
128: return java.util.regex.Pattern.matches(typeExpression, type
129: .getQualifiedName());
130: }
131:
132: /**
133: * Override this method to create other types of matchings than the defaut
134: * one (java.util.regexp).
135: *
136: * @param executable
137: * the canditate executable
138: * @param executableExpression
139: * and expression to match the executable against.
140: * @return true if the executable matches the expression
141: * @see CtElement#getSignature()
142: */
143: protected boolean isExecutableMatching(CtExecutable<?> executable,
144: String executableExpression) {
145: String signature = executable.getSignature();
146: return java.util.regex.Pattern.matches(executableExpression,
147: signature);
148: }
149:
150: /**
151: * Override this method to create other types of matchings than the defaut
152: * one (java.util.regexp).
153: *
154: * @param field
155: * the canditate field
156: * @param fieldExpression
157: * and expression to match the field against.
158: * @return true if the field matches the expression
159: * @see CtElement#getSignature()
160: */
161: protected boolean isFieldMatching(CtField<?> field,
162: String fieldExpression) {
163: String signature = field.getSignature();
164: return java.util.regex.Pattern.matches(fieldExpression,
165: signature);
166: }
167:
168: final public void process() {
169: // Get the XML root node
170: Element root = document.getDocumentElement();
171:
172: NodeList nodeList = root.getElementsByTagName("class");
173: int len = nodeList.getLength();
174: for (int i = 0; i < len; i++) {
175: Element clazz = (Element) nodeList.item(i);
176: String nameExpr = clazz.getAttribute("expr");
177: for (CtSimpleType<?> t : getFactory().Type().getAll(true)) {
178: // CtSimpleType<?> t = getType(name);
179: if (!isTypeMatching(t, nameExpr))
180: continue;
181: try {
182: annotateElement(t, clazz);
183: } catch (Exception e) {
184: e.printStackTrace();
185: }
186:
187: NodeList nodeList3 = clazz
188: .getElementsByTagName("field");
189: for (int j = 0; j < nodeList3.getLength(); j++) {
190: Element fieldElt = (Element) nodeList3.item(j);
191: if (fieldElt.getParentNode() != clazz)
192: continue;
193: String fieldExpr = fieldElt.getAttribute("expr");
194:
195: for (CtField<?> field : t.getFields()) {
196: if (!isFieldMatching(field, fieldExpr))
197: continue;
198: try {
199: annotateElement(field, fieldElt);
200: } catch (Exception e) {
201: e.printStackTrace();
202: }
203: }
204: }
205:
206: if (!(t instanceof CtType))
207: continue;
208:
209: List<CtExecutable<?>> executables = new ArrayList<CtExecutable<?>>();
210: executables.addAll(((CtType<?>) t).getMethods());
211: if (t instanceof CtClass) {
212: executables.addAll(((CtClass<?>) t)
213: .getConstructors());
214: }
215:
216: NodeList nodeList2 = clazz
217: .getElementsByTagName("executable");
218: for (int j = 0; j < nodeList2.getLength(); j++) {
219: Element executableElt = (Element) nodeList2.item(j);
220: if (executableElt.getParentNode() != clazz)
221: continue;
222: String executableExpr = executableElt
223: .getAttribute("expr");
224:
225: for (CtExecutable<?> executable : executables) {
226:
227: if (!isExecutableMatching(executable,
228: executableExpr))
229: continue;
230: try {
231: annotateElement(executable, executableElt);
232: } catch (Exception e) {
233: e.printStackTrace();
234: }
235:
236: // NodeList paramList = method
237: // .getElementsByTagName("parameter");
238: // for (int k = 0; k < paramList.getLength(); k++) {
239: // Element param = (Element) paramList.item(k);
240: // if (param.getParentNode() != method)
241: // continue;
242: // String paramName = param.getAttribute("name");
243: // CtParameter<?> parameter = getParameter(exec,
244: // paramName);
245: // if (parameter != null) {
246: // try {
247: // annotateElement(parameter, param);
248: // } catch (Exception e) {
249: // e.printStackTrace();
250: // }
251: // }
252: // }
253:
254: }
255: }
256:
257: }
258: }
259: }
260:
261: // private CtSimpleType<?> getType(String name) {
262: // CtTypeReference<?> ref = getFactory().Type().createReference(name);
263: // CtSimpleType<?> t = ref.getDeclaration();
264: // return t;
265: // }
266: //
267: // private CtExecutable<?> getExecutable(String signature) {
268: // CtExecutableReference<?> ref = getFactory().Executable()
269: // .createReference(signature);
270: // CtExecutable<?> t = ref.getDeclaration();
271: // return t;
272: // }
273: //
274: // private CtField<?> getField(String signature) {
275: // CtFieldReference<?> ref = getFactory().Field().createReference(
276: // signature);
277: // CtField<?> t = ref.getDeclaration();
278: // return t;
279: // }
280: //
281: // private CtParameter<?> getParameter(CtExecutable<?> exec, String name) {
282: // for (CtParameter<?> p : exec.getParameters()) {
283: // if (p.getSimpleName().equals(name)) {
284: // return p;
285: // }
286: // }
287: // return null;
288: // }
289:
290: @SuppressWarnings("unchecked")
291: private void annotateElement(CtElement javaElt, Element xmlNode)
292: throws Exception {
293: NodeList annNodeList = xmlNode
294: .getElementsByTagName("annotation");
295: for (int i = 0; i < annNodeList.getLength(); i++) {
296: Element annotationNode = (Element) annNodeList.item(i);
297: if (annotationNode.getParentNode() != xmlNode)
298: continue;
299: String name = annotationNode.getAttribute("name");
300: CtTypeReference<? extends Annotation> aref = getFactory()
301: .Annotation().createReference(name);
302: getFactory().Annotation().annotate(javaElt, aref);
303: NodeList fieldNodeList = annotationNode
304: .getElementsByTagName("element");
305: for (int f = 0; f < fieldNodeList.getLength(); f++) {
306: Element fieldNode = (Element) fieldNodeList.item(f);
307: String fieldName = fieldNode.getAttribute("name");
308: String fieldValue = fieldNode.getAttribute("value");
309: Class type = aref.getActualClass().getMethod(fieldName)
310: .getReturnType();
311: Object v = getFactory().convert(type, fieldValue);
312: getFactory().Annotation().annotate(javaElt, aref,
313: fieldName, v);
314: }
315: }
316: }
317:
318: }
|