KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > controls > runtime > generator > apt > ControlClientAnnotationProcessor


1 package org.apache.beehive.controls.runtime.generator.apt;
2
3 /*
4  * Copyright 2004 The Apache Software Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * $Header:$
19  */

20
21 import com.sun.mirror.apt.AnnotationProcessorEnvironment;
22 import com.sun.mirror.declaration.AnnotationTypeDeclaration;
23 import com.sun.mirror.declaration.FieldDeclaration;
24 import com.sun.mirror.declaration.TypeDeclaration;
25 import com.sun.mirror.declaration.*;
26 import com.sun.mirror.apt.*;
27 import com.sun.mirror.type.*;
28
29 import java.util.*;
30
31 import org.apache.beehive.controls.runtime.bean.ControlBeanContext;
32 import org.apache.beehive.controls.runtime.generator.*;
33 import org.apache.beehive.controls.api.bean.*;
34 import org.apache.beehive.controls.api.versioning.Version;
35 import org.apache.beehive.controls.api.versioning.VersionRequired;
36
37 import java.util.Set JavaDoc;
38 import java.io.File JavaDoc;
39 import java.io.IOException JavaDoc;
40
41 public class ControlClientAnnotationProcessor extends TwoPhaseAnnotationProcessor
42 {
43
44     public ControlClientAnnotationProcessor(
45         Set JavaDoc<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env )
46     {
47         super( atds,env );
48     }
49
50     @Override JavaDoc
51     public void check( Declaration d )
52     {
53         if ( d instanceof FieldDeclaration )
54             checkControlField( (FieldDeclaration)d );
55
56         // if @Control is used on something other than a field, the Java lang
57
// checker should produce an error due to the @Target violation.
58

59         if ( d instanceof TypeDeclaration )
60             checkControlClientType( (TypeDeclaration)d );
61         
62         // When a control is instantiated declaratively, values may be assigned to
63
// the control's properties declaratively as well. The property constraint
64
// validator is called here to ensure all values assigned satisfy any
65
// constraints declared in the properties.
66
try
67         {
68             AnnotationConstraintAptValidator.validate(d);
69         }
70         catch (IllegalArgumentException JavaDoc iae)
71         {
72             printError(d, "propertyset.illegal.argument.error", iae.getMessage());
73         }
74     }
75
76     private static void addControlType(Map<TypeDeclaration,Set JavaDoc<TypeMirror>> clientsMap, TypeDeclaration clientType,
77                                        TypeMirror controlFieldType)
78     {
79         Set JavaDoc<TypeMirror> controlTypes = clientsMap.get( clientType );
80
81         if ( controlTypes == null )
82         {
83             controlTypes = new HashSet<TypeMirror>();
84             clientsMap.put( clientType, controlTypes );
85         }
86
87         controlTypes.add( controlFieldType );
88     }
89     
90     /**
91      * Each control client requires a manifest that documents the controls that it references.
92      *
93      * @throws CodeGenerationException
94      */

95     @Override JavaDoc
96     public void generate() throws CodeGenerationException
97     {
98         super.generate();
99
100         // The annotation processor may be passed multiple control client types. Build a map that
101
// links each control client type with the set of control types that it uses.
102

103         Map<TypeDeclaration,Set JavaDoc<TypeMirror>> clientsMap = new HashMap<TypeDeclaration,Set JavaDoc<TypeMirror>>();
104
105         for (AnnotationTypeDeclaration atd : _atds)
106         {
107             if (atd.getSimpleName().equals("Control") )
108             {
109                 AnnotationProcessorEnvironment env = getAnnotationProcessorEnvironment();
110                 Collection<Declaration> decls = env.getDeclarationsAnnotatedWith(atd);
111                 for (Declaration decl : decls)
112                 {
113                     if ( decl instanceof FieldDeclaration )
114                     {
115                         FieldDeclaration fd = (FieldDeclaration)decl;
116                         TypeDeclaration clientType = fd.getDeclaringType();
117                         TypeMirror controlFieldType = fd.getType();
118                         addControlType( clientsMap, clientType, controlFieldType );
119                         
120                         //
121
// If this field is public or protected, add the control type to any derived class.
122
//
123
Collection<Modifier> modifiers = fd.getModifiers();
124                         if ( modifiers.contains( Modifier.PUBLIC ) || modifiers.contains( Modifier.PROTECTED ) )
125                         {
126                             Collection<TypeDeclaration> specifiedTypeDeclartions = env.getSpecifiedTypeDeclarations();
127                             for (TypeDeclaration td : specifiedTypeDeclartions)
128                             {
129                                 if ( td instanceof ClassDeclaration )
130                                 {
131                                     ClassType superclass = ( ( ClassDeclaration ) td ).getSuperclass();
132                                     
133                                     while ( superclass != null )
134                                     {
135                                         if ( superclass.getDeclaration().equals( clientType ) )
136                                         {
137                                             addControlType( clientsMap, td, controlFieldType );
138                                             break;
139                                         }
140                                         
141                                         superclass = superclass.getSuperclass();
142                                     }
143                                 }
144                             }
145                         }
146                     }
147                 }
148             }
149             else if (atd.getSimpleName().equals("ControlReferences"))
150             {
151                 Collection<Declaration> decls = getAnnotationProcessorEnvironment().getDeclarationsAnnotatedWith(atd);
152                 for (Declaration decl : decls)
153                 {
154                     if ( decl instanceof TypeDeclaration )
155                     {
156                         TypeDeclaration clientType = (TypeDeclaration)decl;
157                         Set JavaDoc<TypeMirror> controlTypes = clientsMap.get( clientType );
158                         if ( controlTypes == null )
159                         {
160                             controlTypes = new HashSet<TypeMirror>();
161                             clientsMap.put( clientType, controlTypes );
162                         }
163
164                         // Read ControlReferences annotation
165
AnnotationMirror controlMirror = null;
166                         for (AnnotationMirror annot : clientType.getAnnotationMirrors())
167                         {
168                             if (annot.getAnnotationType().getDeclaration().getQualifiedName().equals(
169                                     "org.apache.beehive.controls.api.bean.ControlReferences"))
170                             {
171                                 controlMirror = annot;
172                                 break;
173                             }
174                         }
175
176                         assert( controlMirror != null );
177
178                         // Add each control type listed in the ControlReferences annotation
179
AptAnnotationHelper controlAnnot = new AptAnnotationHelper(controlMirror);
180                         Collection<AnnotationValue> references = (Collection<AnnotationValue>)controlAnnot.getObjectValue("value");
181                         if ( references != null )
182                         {
183                             for ( AnnotationValue av : references )
184                             {
185                                 TypeMirror crType = (TypeMirror)av.getValue();
186                                 controlTypes.add( crType );
187                             }
188                         }
189                     }
190                 }
191             }
192         }
193
194         // For each client type:
195
// 1 - emit a controls client manifest in the same dir as the client type's class.
196
// 2 - emit a controls client initializer class in the same pkg/dir as the client type's class
197

198         Filer f = getAnnotationProcessorEnvironment().getFiler();
199         Set JavaDoc<TypeDeclaration> clientTypes = clientsMap.keySet();
200         for ( TypeDeclaration clientType : clientTypes )
201         {
202             // Emit manifest
203

204             String JavaDoc clientPkg = clientType.getPackage().getQualifiedName();
205             File JavaDoc clientManifestName =
206                 new File JavaDoc( clientType.getSimpleName() + ControlClientManifest.FILE_EXTENSION );
207
208             ControlClientManifest mf = new ControlClientManifest( clientType.getQualifiedName() );
209
210             try
211             {
212                 Set JavaDoc<TypeMirror> controlTypes = clientsMap.get( clientType );
213                 for ( TypeMirror controlType : controlTypes )
214                 {
215                     InterfaceDeclaration controlIntfOrExt = getControlInterfaceOrExtension(controlType);
216                     InterfaceDeclaration controlIntf = getMostDerivedControlInterface( controlIntfOrExt );
217
218                     assert controlIntf != null : "Can't find most derived control intf for=" + controlIntfOrExt;
219
220                     ControlInterface annot = controlIntf.getAnnotation(ControlInterface.class);
221                     String JavaDoc defBinding = annot.defaultBinding();
222
223                     defBinding = ControlBeanContext.resolveDefaultBinding( defBinding, controlIntf.getQualifiedName() );
224
225                     mf.addControlType( controlIntfOrExt.getQualifiedName(), defBinding );
226                 }
227
228                 mf.emit( f, clientPkg, clientManifestName, null );
229             }
230             catch ( IOException JavaDoc ie )
231             {
232                 printError( clientType, "controls.client.manifest.ioerror" );
233                 ie.printStackTrace( );
234             }
235
236             // Emit initializer
237

238             AnnotationProcessorEnvironment env = getAnnotationProcessorEnvironment();
239             Generator genClass = new AptControlClient( clientType, this );
240
241             if ( genClass != null )
242             {
243                 try
244                 {
245                     List<GeneratorOutput> genList = genClass.getGenerateOutput(env.getFiler());
246                     if (genList == null || genList.size() == 0)
247                         return;
248
249                     for (GeneratorOutput genOut : genList)
250                     {
251                         getGenerator().generate(genOut);
252                     }
253                 }
254                 catch (IOException JavaDoc ioe)
255                 {
256                     throw new CodeGenerationException("Code generation failure: ", ioe);
257                 }
258             }
259         }
260     }
261     
262     @Override JavaDoc
263     public void generate(Declaration decl)
264     {
265     }
266
267     private void checkControlField( FieldDeclaration f )
268     {
269         TypeMirror fieldType = f.getType();
270
271         // Make sure that this field doesn't try to override another that's inherited.
272
String JavaDoc fieldName = f.getSimpleName();
273         TypeDeclaration declaringType = f.getDeclaringType();
274         
275         if ( declaringType instanceof ClassDeclaration )
276         {
277             for ( ClassType i = ( ( ClassDeclaration ) declaringType ).getSuperclass(); i != null; i = i.getSuperclass() )
278             {
279                 ClassDeclaration decl = i.getDeclaration();
280                 
281                 if ( decl != null )
282                 {
283                     for ( FieldDeclaration baseClassField : decl.getFields() )
284                     {
285                         if ( fieldName.equals( baseClassField.getSimpleName() ) )
286                         {
287                             Collection<Modifier> modifiers = baseClassField.getModifiers();
288                             
289                             if ( modifiers.contains( Modifier.PROTECTED ) || modifiers.contains( Modifier.PUBLIC ) )
290                             {
291                                 printError( f, "control.field.override", decl.getQualifiedName() );
292                             }
293                         }
294                     }
295                 }
296             }
297         }
298         
299         // Valid control field instances can be of an interface type
300
// or a class type.
301
if ( fieldType instanceof InterfaceType )
302         {
303             // Valid interface type decls must be annotated w/ @ControlInterface
304
// or @ControlExtension.
305
Declaration fieldTypeDecl = ((InterfaceType)fieldType).getDeclaration();
306             if ( fieldTypeDecl.getAnnotation(ControlInterface.class) == null &&
307                  fieldTypeDecl.getAnnotation(ControlExtension.class) == null )
308                 printError( f, "control.field.bad.interfacetype" );
309         }
310         else if ( fieldType instanceof ClassType )
311         {
312             // Valid class type decls must implements the ControlBean API.
313

314             // Walk the implementation inheritance hierarchy, seeing if one of the
315
// classes implements ControlBean.
316
//
317
// REVIEW: Does NOT check if the interfaces might implement ControlBean!
318
// This is unnecessary for our impl, since our generated bean class directly
319
// implements ControlBean, but other impls may choose to do otherwise.
320
boolean foundControlBean = false;
321             ClassType classType = (ClassType)fieldType;
322
323             if (classType.getDeclaration() != null)
324             {
325                 outer: while ( classType != null )
326                 {
327                     Collection<InterfaceType> intfs = classType.getSuperinterfaces();
328                     for ( InterfaceType intfType : intfs )
329                     {
330                         if ( intfType.getDeclaration().getQualifiedName().equals( "org.apache.beehive.controls.api.bean.ControlBean" ) )
331                         {
332                             foundControlBean = true;
333                             break outer;
334                         }
335                     }
336                     classType = classType.getSuperclass();
337                 }
338                 if ( !foundControlBean )
339                     printError( f, "control.field.bad.classtype" );
340
341                 // Valid generated beans should only "implement" the control interface/extension, and no others
342
classType = (ClassType)fieldType;
343                 Collection<InterfaceType> intfs = classType.getSuperinterfaces();
344                 if ( intfs.size() != 1 )
345                 {
346                     printError( f, "control.field.bad.classtype.badinterface" );
347                 }
348
349                 for ( InterfaceType intfType : intfs )
350                 {
351                     if ( intfType.getDeclaration().getAnnotation(ControlExtension.class) == null &&
352                          intfType.getDeclaration().getAnnotation(ControlInterface.class) == null)
353                     {
354                         printError( f, "control.field.bad.classtype.badinterface");
355                     }
356                 }
357             }
358             else
359             {
360                 // TODO: This could be a ControlBean type that is going to be generated by
361
// the current APT processing iteration. It should be possible to do more
362
// specific verification here using the getTypeDeclaration API on
363
// AnnotationProcessorEnvironment. In any event, the implementation of
364
// getControlInterface will properly handle this case, and if it cannot a
365
// malformed type error will be generated.
366
}
367          }
368          else
369          {
370              printError( f, "control.field.bad.type" );
371          }
372
373          // Enforce any versioning requirements this control field has.
374
//
375
// Since our generate() does some detailed grovelling of control types, make sure that
376
// will not result in an error by doing that grovelling now. Control types may be
377
// malformed if the source for those types has errors (yet the apt type may still exist!).
378
try
379          {
380              InterfaceDeclaration controlIntfOrExt = getControlInterfaceOrExtension(fieldType);
381              InterfaceDeclaration controlIntf = getMostDerivedControlInterface( controlIntfOrExt );
382
383              if ( controlIntf != null )
384              {
385                  enforceVersionRequired( f, controlIntf );
386              }
387              else
388              {
389                  printError( f, "control.field.type.malformed" );
390              }
391          }
392          catch ( CodeGenerationException cge )
393          {
394              printError( f, "control.field.type.malformed" );
395          }
396
397          assert declaringType != null : "Field " + f + " has no declaring type!";
398
399          if ( declaringType.getDeclaringType() != null )
400              printError( f, "control.field.in.inner.class" );
401
402         Collection<Modifier> mods = f.getModifiers();
403
404          if ( mods.contains( Modifier.TRANSIENT ))
405              printError( f, "transient.control.field" );
406
407          if ( mods.contains( Modifier.STATIC ))
408              printError( f, "static.control.field" );
409
410     }
411
412     private void checkControlClientType( TypeDeclaration t )
413     {
414         // validate @ControlReferences
415
AnnotationMirror controlMirror = null;
416
417         for (AnnotationMirror annot : t.getAnnotationMirrors())
418         {
419             if (annot.getAnnotationType().getDeclaration().getQualifiedName().equals(
420                     "org.apache.beehive.controls.api.bean.ControlReferences"))
421             {
422                 controlMirror = annot;
423                 break;
424             }
425         }
426
427         // Bail out if no @ControlReferences annotation found
428
if ( controlMirror == null )
429             return;
430
431         AptAnnotationHelper controlAnnot = new AptAnnotationHelper(controlMirror);
432
433         //
434
// Validate that the types listed in the ControlReferences annotations are actually
435
// control types.
436
//
437

438         Collection<AnnotationValue> references = (Collection<AnnotationValue>)controlAnnot.getObjectValue("value");
439
440         if ( references != null )
441         {
442             for ( AnnotationValue av : references )
443             {
444                 DeclaredType crType = (DeclaredType)av.getValue();
445                 if ( crType instanceof InterfaceType )
446                 {
447                     // Valid interface type decls must be annotated w/ @ControlInterface
448
// or @ControlExtension.
449
Declaration typeDecl = crType.getDeclaration();
450                     if ( typeDecl.getAnnotation(ControlInterface.class) == null &&
451                          typeDecl.getAnnotation(ControlExtension.class) == null )
452                          printError( t, "control.reference.bad.interfacetype" );
453                 }
454             }
455         }
456     }
457
458     /**
459      * Given a InterfaceType or ClassType, returns the InterfaceType for the control type's
460      * public interface/extension.
461      * @param intfOrBeanClass
462      * @return
463      */

464     private InterfaceDeclaration getControlInterfaceOrExtension( TypeMirror intfOrBeanClass )
465     {
466         if (intfOrBeanClass instanceof InterfaceType)
467         {
468             return ((InterfaceType)intfOrBeanClass).getDeclaration();
469         }
470         else if (intfOrBeanClass instanceof ClassType)
471         {
472             ClassType classType = (ClassType)intfOrBeanClass;
473
474             // If the bean type declaration cannot be found, then the only (valid) possibility
475
// is that it is a generated type from the current processor pass. See if a base
476
// interface type can be determined from the current processor input list.
477
if (classType.getDeclaration() == null)
478             {
479                 //
480
// Compute the bean type name, and the associated interface name by stripping
481
// the "Bean" suffix
482
//
483
String JavaDoc className = classType.toString();
484                 String JavaDoc intfName = className.substring(0, className.length() - 4);
485                 AnnotationProcessorEnvironment ape = getAnnotationProcessorEnvironment();
486                 InterfaceDeclaration id = (InterfaceDeclaration)ape.getTypeDeclaration(intfName);
487                 if (id == null)
488                 {
489                     // The specified class name may not be fully qualified. In this case, the
490
// best we can do is look for a best fit match against the input types
491
for (TypeDeclaration td :ape.getSpecifiedTypeDeclarations())
492                     {
493                         if (td instanceof InterfaceDeclaration &&
494                             td.getSimpleName().equals(intfName))
495                         {
496                             return (InterfaceDeclaration)td;
497                         }
498                     }
499                 }
500                 return id;
501             }
502             else
503             {
504                 // direct supers only
505
Collection<InterfaceType> intfs = classType.getSuperinterfaces();
506
507                 // per the code in checkControlField, this set must be of size 1
508
// and the 1 super interface must be a control interface/extension
509
assert ( intfs.size() == 1 );
510                 for ( InterfaceType intfType : intfs )
511                     return intfType.getDeclaration();
512             }
513         }
514         else
515         {
516             throw new CodeGenerationException( "Param not a interface or class type");
517         }
518
519         return null;
520     }
521
522     /**
523      * Given a control interface or extension, do a BFS of its inheritance heirarchy for
524      * the first one marked with @ControlInterface. This represents the point in the
525      * heirarchy where use of @ControlExtension changes to use of @ControlInterface.
526      *
527      * @param controlIntfOrExt an interface annotated with @ControlInterface or @ControlExtension.
528      * @return most derived interface in the heirarchy annotated with @ControlInterface, null
529      * if no such interface found.
530      */

531     private InterfaceDeclaration getMostDerivedControlInterface( InterfaceDeclaration controlIntfOrExt )
532     {
533         Queue<InterfaceDeclaration> q = new LinkedList<InterfaceDeclaration>();
534
535         InterfaceDeclaration id = controlIntfOrExt;
536         while ( id != null )
537         {
538             if ( id.getAnnotation(ControlInterface.class) != null )
539                 break;
540
541             Collection<InterfaceType> supers = id.getSuperinterfaces();
542             for ( InterfaceType s : supers )
543                 q.offer( s.getDeclaration() );
544
545             id = q.poll();
546         }
547
548         return id;
549     }
550
551     /**
552      * Enforces the VersionRequired annotation for control fields.
553      */

554     private void enforceVersionRequired( FieldDeclaration f, InterfaceDeclaration controlIntf )
555     {
556         VersionRequired versionRequired = f.getAnnotation(VersionRequired.class);
557         Version versionPresent = controlIntf.getAnnotation(Version.class);
558
559         if ( versionRequired != null )
560         {
561             int majorRequired = versionRequired.major();
562             int minorRequired = versionRequired.minor();
563
564             if ( majorRequired < 0 ) // no real version requirement
565
return;
566
567             int majorPresent = -1;
568             int minorPresent = -1;
569             if ( versionPresent != null )
570             {
571                 majorPresent = versionPresent.major();
572                 minorPresent = versionPresent.minor();
573
574                 if ( majorRequired <= majorPresent &&
575                      (minorRequired < 0 || minorRequired <= minorPresent) )
576                 {
577                     // Version requirement is satisfied
578
return;
579                 }
580             }
581
582             //
583
// Version requirement failed
584
//
585
printError( f, "control.field.bad.version", f.getSimpleName(), majorRequired, minorRequired,
586                         majorPresent, minorPresent );
587         }
588     }
589
590     /**
591      * Returns the CodeGenerator instance supporting this processor, instantiating a new
592      * generator instance if necessary.
593      */

594     protected CodeGenerator getGenerator()
595     {
596         if (_generator == null)
597         {
598             //
599
// Locate the class that wraps the Velocity code generation process
600
//
601
AnnotationProcessorEnvironment env = getAnnotationProcessorEnvironment();
602
603             try
604             {
605                 _generator = new VelocityGenerator(env);
606             }
607             catch (Exception JavaDoc e)
608             {
609                 throw new CodeGenerationException("Unable to create code generator", e);
610             }
611         }
612         return _generator;
613     }
614
615     CodeGenerator _generator;
616 }
617
Popular Tags