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.io.IOException;
022: import java.io.Writer;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.HashMap;
026: import java.util.List;
027:
028: import com.sun.mirror.apt.Filer;
029: import com.sun.mirror.declaration.AnnotationMirror;
030: import com.sun.mirror.declaration.ClassDeclaration;
031: import com.sun.mirror.declaration.Declaration;
032: import com.sun.mirror.declaration.FieldDeclaration;
033: import com.sun.mirror.declaration.InterfaceDeclaration;
034: import com.sun.mirror.declaration.MethodDeclaration;
035: import com.sun.mirror.type.InterfaceType;
036: import com.sun.mirror.type.TypeMirror;
037:
038: import org.apache.beehive.controls.api.bean.ControlImplementation;
039: import org.apache.beehive.controls.api.events.Client;
040: import org.apache.beehive.controls.api.events.EventHandler;
041: import org.apache.beehive.controls.api.versioning.VersionSupported;
042: import org.apache.beehive.controls.api.versioning.Version;
043: import org.apache.beehive.controls.runtime.generator.apt.TwoPhaseAnnotationProcessor;
044:
045: /**
046: * The AptControlImplementation class provides validation and metadata management when
047: * processing a ControlImplementation class.
048: */
049: public class AptControlImplementation extends AptType implements
050: Generator {
051: /**
052: * Constructs a new AptControlImplementation instance where information is derived
053: * from APT metadata
054: * @param decl the annotated declaration
055: */
056: public AptControlImplementation(Declaration decl,
057: TwoPhaseAnnotationProcessor ap) {
058: _ap = ap;
059: if (!(decl instanceof ClassDeclaration)) {
060: _ap.printError(decl, "control.implementation.badclass");
061: return;
062: }
063: _implDecl = (ClassDeclaration) decl;
064: setDeclaration(_implDecl);
065:
066: _super Class = initSuperClass();
067:
068: _contexts = initContexts();
069:
070: _controls = initControls();
071:
072: _clients = initClients();
073:
074: initEventAdaptors();
075:
076: //
077: // Check serializability of the implementation class. Any non-transient implementation
078: // must implement the java.io.Serializable marker interface to indicate that the author
079: // has considered serializability.
080: //
081: ControlImplementation implAnnot = _implDecl
082: .getAnnotation(ControlImplementation.class);
083: if (!implAnnot.isTransient()) {
084: if (!isSerializable()) {
085: _ap.printError(decl,
086: "control.implementation.unserializable");
087: }
088: }
089:
090: //
091: // Construct a new initializer class from this implementation class
092: //
093: _init = new ImplInitializer(this );
094:
095: if (getControlInterface() == null) {
096: _ap.printError(decl,
097: "control.implementation.missing.interface");
098: return;
099: }
100:
101: _versionSupported = initVersionSupported();
102:
103: enforceVersionSupported();
104: }
105:
106: /**
107: * Initializes the super interface that this ControlImpl extends (or null if a
108: * base class)
109: */
110: private AptControlImplementation initSuperClass() {
111: if (_implDecl == null || _implDecl.getSuperclass() == null)
112: return null;
113:
114: ClassDeclaration super Decl = _implDecl.getSuperclass()
115: .getDeclaration();
116: if (super Decl != null
117: && super Decl
118: .getAnnotation(org.apache.beehive.controls.api.bean.ControlImplementation.class) != null) {
119: return new AptControlImplementation(super Decl, _ap);
120: }
121:
122: return null;
123: }
124:
125: /**
126: * Returns the super interface for this interface
127: */
128: public AptControlImplementation getSuperClass() {
129: return _super Class;
130: }
131:
132: /**
133: * Initializes the list of ContextField declared directly by this ControlImpl
134: */
135: private ArrayList<AptContextField> initContexts() {
136: ArrayList<AptContextField> contexts = new ArrayList<AptContextField>();
137:
138: if (_implDecl == null || _implDecl.getFields() == null)
139: return contexts;
140:
141: Collection<FieldDeclaration> declaredFields = _implDecl
142: .getFields();
143: for (FieldDeclaration fieldDecl : declaredFields) {
144: if (fieldDecl
145: .getAnnotation(org.apache.beehive.controls.api.context.Context.class) != null)
146: contexts.add(new AptContextField(this , fieldDecl, _ap));
147: }
148: return contexts;
149: }
150:
151: /**
152: * Returns the list of ContextFields declared directly by this ControlImplementation
153: */
154: public ArrayList<AptContextField> getContexts() {
155: return _contexts;
156: }
157:
158: /**
159: * Returns true if the implemenation class contains any nested services
160: */
161: public boolean hasContexts() {
162: return _contexts.size() != 0;
163: }
164:
165: /**
166: * Initializes the list of ControlFields for this ControlImpl
167: */
168: private ArrayList<AptControlField> initControls() {
169: ArrayList<AptControlField> fields = new ArrayList<AptControlField>();
170:
171: if (_implDecl == null || _implDecl.getFields() == null)
172: return fields;
173:
174: Collection<FieldDeclaration> declaredFields = _implDecl
175: .getFields();
176: for (FieldDeclaration fieldDecl : declaredFields) {
177: if (fieldDecl
178: .getAnnotation(org.apache.beehive.controls.api.bean.Control.class) != null)
179: fields.add(new AptControlField(this , fieldDecl, _ap));
180: }
181: return fields;
182: }
183:
184: /**
185: * Returns true if the implemenation class contains any nested controls
186: */
187: public boolean hasControls() {
188: return _controls.size() != 0;
189: }
190:
191: /**
192: * Initializes the list of ClientFields declared directly by this ControlImpl
193: */
194: protected ArrayList<AptClientField> initClients() {
195: ArrayList<AptClientField> clients = new ArrayList<AptClientField>();
196:
197: if (_implDecl == null || _implDecl.getFields() == null)
198: return clients;
199:
200: Collection<FieldDeclaration> declaredFields = _implDecl
201: .getFields();
202: for (FieldDeclaration fieldDecl : declaredFields) {
203: if (fieldDecl.getAnnotation(Client.class) != null)
204: clients.add(new AptClientField(this , fieldDecl));
205: }
206: return clients;
207: }
208:
209: /**
210: * Returns the list of ClientFields declared directly by this ControlImplementation
211: */
212: public ArrayList<AptClientField> getClients() {
213: return _clients;
214: }
215:
216: /**
217: * Returns the VersionSupported annotation, if any.
218: */
219: public VersionSupported getVersionSupported() {
220: return _versionSupported;
221: }
222:
223: /**
224: * Returns true if the implemenation class contains any nested event proxies
225: */
226: public boolean hasClients() {
227: return _clients.size() != 0;
228: }
229:
230: /**
231: * Returns the field with the specified name
232: */
233: public AptField getField(String name) {
234: for (AptField genField : _contexts)
235: if (genField.getName().equals(name))
236: return genField;
237: for (AptField genField : _clients)
238: if (genField.getName().equals(name))
239: return genField;
240:
241: return null;
242: }
243:
244: public AptEventField getControlField(String name) {
245: for (AptControlField controlField : _controls)
246: if (controlField.getName().equals(name))
247: return controlField;
248:
249: return null;
250: }
251:
252: /**
253: * Returns the list of fully qualified class names for types that are derived
254: * from this Generator
255: */
256: public String[] getGeneratedTypes() {
257: return new String[] { _init.getClassName() };
258: }
259:
260: /**
261: * Returns the information necessary to generate a ImplInitializer from this
262: * ControlImplementation.
263: */
264: public List<GeneratorOutput> getCheckOutput(Filer filer)
265: throws IOException {
266: HashMap<String, Object> map = new HashMap<String, Object>();
267: map.put("impl", this ); // control implementation
268: map.put("init", _init); // control impl initializer
269:
270: Writer writer = new IndentingWriter(filer
271: .createSourceFile(_init.getClassName()));
272: GeneratorOutput genOut = new GeneratorOutput(
273: writer,
274: "org/apache/beehive/controls/runtime/generator/ImplInitializer.vm",
275: map);
276: ArrayList<GeneratorOutput> genList = new ArrayList<GeneratorOutput>(
277: 1);
278: genList.add(genOut);
279: return genList;
280: }
281:
282: /**
283: * Returns the list of generated files derived from this Generator during the
284: * generate phase of annotation processing.
285: */
286: public List<GeneratorOutput> getGenerateOutput(Filer filer)
287: throws IOException {
288: return null;
289: }
290:
291: /**
292: * Returns the ControlInterface implemented by this ControlImpl.
293: */
294: public AptControlInterface getControlInterface() {
295: if (_implDecl == null || _implDecl.getSuperinterfaces() == null)
296: return null;
297:
298: Collection<InterfaceType> super Interfaces = _implDecl
299: .getSuperinterfaces();
300: for (InterfaceType intfType : super Interfaces) {
301: InterfaceDeclaration intfDecl = intfType.getDeclaration();
302: if (intfDecl != null
303: && intfDecl
304: .getAnnotation(org.apache.beehive.controls.api.bean.ControlInterface.class) != null)
305: return new AptControlInterface(intfDecl, _ap);
306: }
307:
308: return null;
309: }
310:
311: /**
312: * Initializes the list of EventAdaptors for this ControlImpl
313: */
314: protected void initEventAdaptors() {
315: if (_implDecl == null || _implDecl.getMethods() == null)
316: return;
317:
318: for (MethodDeclaration implMethod : _implDecl.getMethods()) {
319: //
320: // Do a quick check for the presence of the EventHandler annotation on methods
321: //
322: if (implMethod.getAnnotation(EventHandler.class) == null
323: || implMethod.toString().equals("<clinit>()"))
324: continue;
325:
326: //
327: // EventHandler annotations on private methods cause compilation error.
328: //
329: if (isPrivateMethod(implMethod)) {
330: _ap.printError(implMethod,
331: "eventhandler.method.is.private");
332: continue;
333: }
334:
335: //
336: // If found, we must actually read the value using an AnnotationMirror, since it
337: // contains a Class element (eventSet) that cannot be loaded
338: //
339: AnnotationMirror handlerMirror = null;
340: for (AnnotationMirror annot : implMethod
341: .getAnnotationMirrors()) {
342: if (annot == null
343: || annot.getAnnotationType() == null
344: || annot.getAnnotationType().getDeclaration() == null
345: || annot.getAnnotationType().getDeclaration()
346: .getQualifiedName() == null)
347: return;
348:
349: if (annot
350: .getAnnotationType()
351: .getDeclaration()
352: .getQualifiedName()
353: .equals(
354: "org.apache.beehive.controls.api.events.EventHandler")) {
355: handlerMirror = annot;
356: break;
357: }
358: }
359: if (handlerMirror == null) {
360: throw new CodeGenerationException(
361: "Unable to find EventHandler annotation on "
362: + implMethod);
363: }
364:
365: AptAnnotationHelper handlerAnnot = new AptAnnotationHelper(
366: handlerMirror);
367:
368: //
369: // Locate the EventField based upon the field element value
370: //
371: String fieldName = (String) handlerAnnot
372: .getObjectValue("field");
373: AptEventField eventField = (AptEventField) getField(fieldName);
374: if (eventField == null) {
375: // eventField == null means this field isn't interesting for the purposes
376: // of this processor (control impls). However, only emit an error message
377: // if the field isn't on a nested control
378: if (getControlField(fieldName) == null)
379: _ap.printError(implMethod,
380: "eventhandler.field.not.found", fieldName);
381:
382: continue;
383: }
384:
385: //
386: // Locate the EventSet based upon the eventSet element value
387: //
388: TypeMirror tm = (TypeMirror) (handlerAnnot
389: .getObjectValue("eventSet"));
390: if (tm == null)
391: continue;
392: String setName = tm.toString();
393: AptControlInterface controlIntf = eventField
394: .getControlInterface();
395:
396: // todo: remove workaround once bug has been resolved.
397: /* Workaround for JIRA issue BEEHIVE-1143, eventset name may
398: contain a '$' seperator between the outer class and inner class.
399: Should be a '.' seperator. Only applies to Eclipse APT. This
400: workaround is also present in AptControlClient.initEventAdapters
401: */
402: if (tm.getClass().getName().startsWith("org.eclipse.")) {
403: setName = setName.replace('$', '.');
404: }
405: // end of workaround
406:
407: AptEventSet eventSet = controlIntf.getEventSet(setName);
408: if (eventSet == null) {
409: _ap.printError(implMethod,
410: "eventhandler.eventset.not.found", setName);
411: continue;
412: }
413:
414: //
415: // Register a new EventAdaptor for the EventSet, if none exists already
416: //
417: EventAdaptor adaptor = eventField.getEventAdaptor(eventSet);
418: if (adaptor == null) {
419: adaptor = new EventAdaptor(eventField, eventSet);
420: eventField.addEventAdaptor(eventSet, adaptor);
421: }
422:
423: //
424: // Locate the EventSet method based upon the eventName element value. Once
425: // found, add a new AptEventHandler to the adaptor for this event.
426: //
427: boolean found = false;
428: String eventName = (String) handlerAnnot
429: .getObjectValue("eventName");
430: AptMethod handlerMethod = new AptMethod(implMethod, _ap);
431: for (AptEvent controlEvent : eventSet.getEvents()) {
432: if (controlEvent == null
433: || controlEvent.getName() == null
434: || !controlEvent.getName().equals(eventName))
435: continue;
436: if (controlEvent.getArgTypes() == null)
437: continue;
438:
439: //
440: // BUGBUG: If the arguments are parameterized, then the event handler
441: // might declare a specific bound version of the type, so a direct
442: // comparison will fail. If parameterized, we don't validate.
443: //
444: if (controlEvent.hasParameterizedArguments()
445: || controlEvent.getArgTypes().equals(
446: handlerMethod.getArgTypes())) {
447: adaptor.addHandler(controlEvent,
448: new AptEventHandler(controlEvent,
449: implMethod, _ap));
450: found = true;
451: break;
452: }
453: }
454: if (!found) {
455: _ap.printError(implMethod,
456: "eventhandler.method.not.found", setName);
457: }
458: }
459: }
460:
461: private VersionSupported initVersionSupported() {
462: if (_implDecl == null)
463: return null;
464: return _implDecl.getAnnotation(VersionSupported.class);
465: }
466:
467: /**
468: * Enforces the VersionRequired annotation for control extensions.
469: */
470: private void enforceVersionSupported() {
471: if (_versionSupported != null) {
472: int majorSupported = _versionSupported.major();
473: int minorSupported = _versionSupported.minor();
474:
475: if (majorSupported < 0) // no real version support requirement
476: return;
477:
478: AptControlInterface ci = getControlInterface();
479: if (ci == null)
480: return;
481:
482: int majorPresent = -1;
483: int minorPresent = -1;
484: Version ciVersion = ci.getVersion();
485: if (ciVersion != null) {
486: majorPresent = ciVersion.major();
487: minorPresent = ciVersion.minor();
488:
489: if (majorSupported >= majorPresent
490: && (minorSupported < 0 || minorSupported >= minorPresent)) {
491: // Version requirement is satisfied
492: return;
493: }
494: }
495:
496: //
497: // Version requirement failed
498: //
499:
500: _ap.printError(_implDecl, "versionsupported.failed",
501: _implDecl.getSimpleName(), majorSupported,
502: minorSupported, majorPresent, minorPresent);
503: }
504: }
505:
506: /**
507: * Does this control impl on one of it superclasses implement java.io.Serializable?
508: * @return true if this control impl or one of its superclasses implements java.io.Serializable.
509: */
510: protected boolean isSerializable() {
511:
512: for (InterfaceType super Intf : _implDecl.getSuperinterfaces()) {
513: if (super Intf.toString().equals("java.io.Serializable")) {
514: return true;
515: }
516: }
517:
518: // check to see if the superclass is serializable
519: return _super Class != null && _super Class.isSerializable();
520: }
521:
522: private ClassDeclaration _implDecl;
523: private TwoPhaseAnnotationProcessor _ap;
524: private AptControlImplementation _super Class;
525: private ArrayList<AptContextField> _contexts;
526: private ArrayList<AptClientField> _clients;
527: private ArrayList<AptControlField> _controls;
528: private ImplInitializer _init;
529: private VersionSupported _versionSupported;
530: }
|