001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.controls.runtime.generator;
020:
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.Collection;
024:
025: import com.sun.mirror.declaration.AnnotationMirror;
026: import com.sun.mirror.declaration.AnnotationTypeDeclaration;
027: import com.sun.mirror.declaration.MethodDeclaration;
028: import com.sun.mirror.declaration.ParameterDeclaration;
029: import com.sun.mirror.declaration.TypeParameterDeclaration;
030: import com.sun.mirror.declaration.Modifier;
031: import com.sun.mirror.type.AnnotationType;
032: import com.sun.mirror.type.DeclaredType;
033: import com.sun.mirror.type.PrimitiveType;
034: import com.sun.mirror.type.ReferenceType;
035: import com.sun.mirror.type.TypeMirror;
036: import com.sun.mirror.type.WildcardType;
037:
038: import org.apache.beehive.controls.api.packaging.FeatureInfo;
039: import org.apache.beehive.controls.runtime.generator.apt.TwoPhaseAnnotationProcessor;
040:
041: /**
042: * The AptMethod class defines a base set of utility methods for acessing method attributes
043: * based upon an APT method declaration.
044: */
045: public class AptMethod {
046: //
047: // Maps primitive type names to a default value string
048: //
049: private static HashMap<String, String> _defaultReturnValues = new HashMap<String, String>();
050:
051: static final HashMap<PrimitiveType.Kind, String> _primToObject = new HashMap<PrimitiveType.Kind, String>();
052:
053: static {
054: _defaultReturnValues.put("void", "");
055: _defaultReturnValues.put("boolean", "false");
056: _defaultReturnValues.put("char", "'\0'");
057: _defaultReturnValues.put("byte", "0");
058: _defaultReturnValues.put("short", "0");
059: _defaultReturnValues.put("int", "0");
060: _defaultReturnValues.put("long", "0");
061: _defaultReturnValues.put("float", "0.0f");
062: _defaultReturnValues.put("double", "0.0d");
063:
064: _primToObject.put(PrimitiveType.Kind.BOOLEAN, "Boolean");
065: _primToObject.put(PrimitiveType.Kind.BYTE, "Byte");
066: _primToObject.put(PrimitiveType.Kind.CHAR, "Character");
067: _primToObject.put(PrimitiveType.Kind.DOUBLE, "Double");
068: _primToObject.put(PrimitiveType.Kind.FLOAT, "Float");
069: _primToObject.put(PrimitiveType.Kind.INT, "Integer");
070: _primToObject.put(PrimitiveType.Kind.LONG, "Long");
071: _primToObject.put(PrimitiveType.Kind.SHORT, "Short");
072: }
073:
074: /**
075: * Constructs a new AptMethod instance associated with a specific method declaration
076: */
077: public AptMethod(MethodDeclaration methodDecl,
078: TwoPhaseAnnotationProcessor ap) {
079: _methodDecl = methodDecl;
080: _interceptorServiceNames = initInterceptorServiceNames();
081: _ap = ap;
082: }
083:
084: /**
085: * Returns the name of the method
086: */
087: public String getName() {
088: if (_methodDecl == null)
089: return "";
090:
091: return _methodDecl.getSimpleName();
092: }
093:
094: /**
095: * Returns the argument declaration of the method, applying the bindings in the provided
096: * type map to any parameter types
097: */
098: public String getArgDecl(HashMap<String, TypeMirror> bindingMap) {
099: StringBuffer sb = new StringBuffer();
100:
101: if (_methodDecl.getParameters() == null)
102: return "";
103:
104: int i = 0;
105: for (ParameterDeclaration paramDecl : _methodDecl
106: .getParameters()) {
107: TypeMirror paramType = paramDecl.getType();
108: if (paramType == null)
109: return "";
110:
111: if (bindingMap != null
112: && bindingMap.containsKey(paramType.toString()))
113: paramType = bindingMap.get(paramType.toString());
114:
115: if (i != 0)
116: sb.append(", ");
117:
118: sb.append(paramType.toString());
119:
120: sb.append(' ');
121:
122: // BUGBUG: when the MethodDeclaration is derived from Reflection, this seems
123: // to return 'arg0' for all arguments!
124: String argName = paramDecl.getSimpleName();
125: if (argName.equals("arg0"))
126: sb.append("arg" + i);
127: else
128: sb.append(argName);
129:
130: i++;
131: }
132: return sb.toString();
133: }
134:
135: /**
136: * Returns the arguments declarations for the method, with no formal parameter binding applied
137: */
138: public String getArgDecl() {
139: return getArgDecl(null);
140: }
141:
142: /**
143: * Returns the the method argument names, in a comma separated list
144: */
145: public String getArgList(boolean quoteDelimit) {
146: StringBuffer sb = new StringBuffer();
147: int i = 0;
148:
149: if (_methodDecl.getParameters() == null)
150: return "";
151:
152: for (ParameterDeclaration paramDecl : _methodDecl
153: .getParameters()) {
154: if (i != 0)
155: sb.append(", ");
156:
157: // BUGBUG: when the MethodDeclaration is derived from Reflection, this seems
158: // to return 'arg0' for all arguments!
159: String argName = paramDecl.getSimpleName();
160: if (quoteDelimit)
161: sb.append('"');
162: if (argName.equals("arg0"))
163: sb.append("arg" + i);
164: else
165: sb.append(argName);
166: if (quoteDelimit)
167: sb.append('"');
168: i++;
169: }
170: return sb.toString();
171: }
172:
173: /**
174: * Default form of getArgList, that does not quote delimit arguments
175: */
176: public String getArgList() {
177: return getArgList(false);
178: }
179:
180: /**
181: * Returns the the method argument classes, in a comma separated list
182: */
183: public String getArgTypes() {
184: StringBuffer sb = new StringBuffer();
185: int i = 0;
186:
187: if (_methodDecl == null || _methodDecl.getParameters() == null)
188: return "";
189:
190: for (ParameterDeclaration paramDecl : _methodDecl
191: .getParameters()) {
192: if (i++ != 0)
193: sb.append(", ");
194:
195: TypeMirror paramType = paramDecl.getType();
196: if (paramType == null)
197: return "";
198:
199: //
200: // Use the erasure here, because we only want the raw type, not the reference
201: // type
202: //
203: sb.append(_ap.getAnnotationProcessorEnvironment()
204: .getTypeUtils().getErasure(paramType));
205: sb.append(".class");
206: }
207: return sb.toString();
208: }
209:
210: /**
211: * Returns 'true' if the method uses any parameterized types as parameters
212: */
213: public boolean hasParameterizedArguments() {
214: for (ParameterDeclaration paramDecl : _methodDecl
215: .getParameters()) {
216: TypeMirror paramType = paramDecl.getType();
217: if (paramType instanceof ReferenceType
218: || paramType instanceof WildcardType)
219: return true;
220: }
221: return false;
222: }
223:
224: /**
225: * Returns the declaration of any generic formal types associated with the method
226: */
227: public String getFormalTypes() {
228: if (_methodDecl == null || _methodDecl.getReturnType() == null)
229: return "";
230:
231: Collection<TypeParameterDeclaration> formalTypes = _methodDecl
232: .getFormalTypeParameters();
233: if (formalTypes.size() == 0)
234: return "";
235:
236: StringBuffer sb = new StringBuffer("<");
237: boolean isFirst = true;
238: for (TypeParameterDeclaration tpd : formalTypes) {
239: if (isFirst)
240: isFirst = false;
241: else
242: sb.append(", ");
243:
244: sb.append(tpd.toString());
245: }
246: sb.append(">");
247: return sb.toString();
248: }
249:
250: /**
251: * Returns the method return type, applying any formal type parameter bindings defined
252: * by the provided map.
253: */
254: public String getReturnType(HashMap<String, TypeMirror> bindingMap) {
255: if (_methodDecl == null || _methodDecl.getReturnType() == null)
256: return "";
257:
258: String returnType = _methodDecl.getReturnType().toString();
259: if (bindingMap != null && bindingMap.containsKey(returnType))
260: return bindingMap.get(returnType).toString();
261:
262: return returnType;
263: }
264:
265: /**
266: * Returns the method return type with no type bindings applied
267: */
268: public String getReturnType() {
269: return getReturnType(null);
270: }
271:
272: /**
273: * Returns the throws clause of the operation
274: */
275: public String getThrowsClause() {
276: if (_methodDecl == null || _methodDecl.getThrownTypes() == null)
277: return "";
278:
279: Collection<ReferenceType> thrownTypes = _methodDecl
280: .getThrownTypes();
281: if (thrownTypes.size() == 0)
282: return "";
283:
284: StringBuffer sb = new StringBuffer("throws ");
285: int i = 0;
286: for (ReferenceType exceptType : thrownTypes) {
287: if (i++ != 0)
288: sb.append(", ");
289: sb.append(exceptType.toString());
290: }
291: return sb.toString();
292: }
293:
294: /**
295: * Returns an ArrayList of thrown exceptions
296: */
297: public ArrayList<String> getThrowsList() {
298: ArrayList<String> throwsList = new ArrayList<String>();
299:
300: if (_methodDecl == null || _methodDecl.getThrownTypes() == null
301: || _methodDecl.getThrownTypes().size() == 0)
302: return throwsList;
303:
304: Collection<ReferenceType> thrownTypes = _methodDecl
305: .getThrownTypes();
306: for (ReferenceType exceptType : thrownTypes)
307: throwsList.add(exceptType.toString());
308:
309: return throwsList;
310: }
311:
312: /**
313: * Returns a default return value string for the method, based upon bound return type
314: */
315: public String getDefaultReturnValue(
316: HashMap<String, TypeMirror> typeBinding) {
317: String returnType = getReturnType(typeBinding);
318: if (_defaultReturnValues.containsKey(returnType))
319: return _defaultReturnValues.get(returnType);
320: return "null";
321: }
322:
323: /**
324: * Returns a default return value string for the method, with no type binding applied
325: */
326: public String getDefaultReturnValue() {
327: return getDefaultReturnValue(null);
328: }
329:
330: /**
331: * Returns any FeatureInfo associated with the method (or null if none)
332: */
333: public FeatureInfo getFeatureInfo() {
334: if (_methodDecl == null)
335: return null;
336:
337: return _methodDecl.getAnnotation(FeatureInfo.class);
338: }
339:
340: /**
341: * Sets the unique index value for this method. If a particular method is overloaded,
342: * then each associated AptMethod will have a unique index; otherwise, the index is -1.
343: */
344: public void setIndex(int index) {
345: _index = index;
346: }
347:
348: /**
349: * Returns the unique index value for this method.
350: */
351: public int getIndex() {
352: return _index;
353: }
354:
355: /**
356: * Is this a public method?
357: * @return true if public
358: */
359: protected boolean isPublic() {
360: Collection<Modifier> modifiers = _methodDecl.getModifiers();
361: return modifiers.contains(Modifier.PUBLIC);
362: }
363:
364: MethodDeclaration _methodDecl;
365: int _index = -1;
366: TwoPhaseAnnotationProcessor _ap;
367:
368: /**
369: * Returns the names of interceptor service interfaces associated with this operation
370: * @return the names of the interceptor service interfaces associated with this operation
371: */
372: public Collection<String> getInterceptorServiceNames() {
373: return _interceptorServiceNames;
374: }
375:
376: /**
377: * Returns the names of interceptor service interfaces associated with this operation, formatted as a
378: * constant initializer string.
379: * @return the names of the interceptor service interfaces associated with this operation
380: */
381: public String getInterceptorDecl() {
382: Collection<String> names = getInterceptorServiceNames();
383: if (names == null || names.size() == 0)
384: return null;
385:
386: StringBuffer ret = new StringBuffer("{");
387:
388: String[] n = names.toArray(new String[0]);
389: for (int i = 0; i < n.length; ++i) {
390: ret.append('"');
391: ret.append(n[i]);
392: ret.append('"');
393: if (i != n.length - 1)
394: ret.append(", ");
395: }
396: ret.append("}");
397:
398: return ret.toString();
399: }
400:
401: private Collection<String> initInterceptorServiceNames() {
402: ArrayList<String> ret = new ArrayList<String>();
403:
404: if (_methodDecl == null)
405: return ret;
406:
407: // Iterate over annotations on operation, looking for interceptor-based ones
408: Collection<AnnotationMirror> annotations = _methodDecl
409: .getAnnotationMirrors();
410: for (AnnotationMirror a : annotations) {
411: AnnotationType at = a.getAnnotationType();
412: AnnotationTypeDeclaration atd = at.getDeclaration();
413:
414: /*
415: When performing annotation processing, the ATD here might be null if the apt.exe runtime
416: is unable to resolve the type to something specific. This will happen when a type referenced
417: in a source file is invalid because of a bad / missing import, mis-spelling, etc. When
418: this happens, annotation processing should merilly continue and let javac.exe report
419: the type errors. Better to do that than to throw an NPE here.
420: */
421: if (atd == null)
422: continue;
423:
424: Collection<AnnotationMirror> metaAnnotations = atd
425: .getAnnotationMirrors();
426:
427: /*
428: Look for annotations that are meta-annotated with @InterceptorAnnotation
429: */
430: for (AnnotationMirror ma : metaAnnotations) {
431: if (ma
432: .getAnnotationType()
433: .getDeclaration()
434: .getQualifiedName()
435: .equals(
436: "org.apache.beehive.controls.spi.svc.InterceptorAnnotation")) {
437: /*
438: found an interceptor-based annotation, add it!
439: */
440: AptAnnotationHelper ia = new AptAnnotationHelper(ma);
441: DeclaredType serviceType = (DeclaredType) ia
442: .getObjectValue("service");
443: String intf = serviceType.toString();
444: ret.add(intf);
445:
446: break;
447: }
448: }
449: }
450:
451: return ret;
452: }
453:
454: Collection<String> _interceptorServiceNames;
455:
456: }
|