package org.apache.beehive.controls.runtime.generator.apt;
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Header:$
*/
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.FieldDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.declaration.*;
import com.sun.mirror.apt.*;
import com.sun.mirror.type.*;
import java.util.*;
import org.apache.beehive.controls.runtime.bean.ControlBeanContext;
import org.apache.beehive.controls.runtime.generator.*;
import org.apache.beehive.controls.api.bean.*;
import org.apache.beehive.controls.api.versioning.Version;
import org.apache.beehive.controls.api.versioning.VersionRequired;
import java.util.Set;
import java.io.File;
import java.io.IOException;
public class ControlClientAnnotationProcessor extends TwoPhaseAnnotationProcessor
{
public ControlClientAnnotationProcessor(
Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env )
{
super( atds,env );
}
@Override
public void check( Declaration d )
{
if ( d instanceof FieldDeclaration )
checkControlField( (FieldDeclaration)d );
// if @Control is used on something other than a field, the Java lang
// checker should produce an error due to the @Target violation.
if ( d instanceof TypeDeclaration )
checkControlClientType( (TypeDeclaration)d );
// When a control is instantiated declaratively, values may be assigned to
// the control's properties declaratively as well. The property constraint
// validator is called here to ensure all values assigned satisfy any
// constraints declared in the properties.
try
{
AnnotationConstraintAptValidator.validate(d);
}
catch (IllegalArgumentException iae)
{
printError(d, "propertyset.illegal.argument.error", iae.getMessage());
}
}
private static void addControlType(Map<TypeDeclaration,Set<TypeMirror>> clientsMap, TypeDeclaration clientType,
TypeMirror controlFieldType)
{
Set<TypeMirror> controlTypes = clientsMap.get( clientType );
if ( controlTypes == null )
{
controlTypes = new HashSet<TypeMirror>();
clientsMap.put( clientType, controlTypes );
}
controlTypes.add( controlFieldType );
}
/**
* Each control client requires a manifest that documents the controls that it references.
*
* @throws CodeGenerationException
*/
@Override
public void generate() throws CodeGenerationException
{
super.generate();
// The annotation processor may be passed multiple control client types. Build a map that
// links each control client type with the set of control types that it uses.
Map<TypeDeclaration,Set<TypeMirror>> clientsMap = new HashMap<TypeDeclaration,Set<TypeMirror>>();
for (AnnotationTypeDeclaration atd : _atds)
{
if (atd.getSimpleName().equals("Control") )
{
AnnotationProcessorEnvironment env = getAnnotationProcessorEnvironment();
Collection<Declaration> decls = env.getDeclarationsAnnotatedWith(atd);
for (Declaration decl : decls)
{
if ( decl instanceof FieldDeclaration )
{
FieldDeclaration fd = (FieldDeclaration)decl;
TypeDeclaration clientType = fd.getDeclaringType();
TypeMirror controlFieldType = fd.getType();
addControlType( clientsMap, clientType, controlFieldType );
//
// If this field is public or protected, add the control type to any derived class.
//
Collection<Modifier> modifiers = fd.getModifiers();
if ( modifiers.contains( Modifier.PUBLIC ) || modifiers.contains( Modifier.PROTECTED ) )
{
Collection<TypeDeclaration> specifiedTypeDeclartions = env.getSpecifiedTypeDeclarations();
for (TypeDeclaration td : specifiedTypeDeclartions)
{
if ( td instanceof ClassDeclaration )
{
ClassType superclass = ( ( ClassDeclaration ) td ).getSuperclass();
while ( superclass != null )
{
if ( superclass.getDeclaration().equals( clientType ) )
{
addControlType( clientsMap, td, controlFieldType );
break;
}
superclass = superclass.getSuperclass();
}
}
}
}
}
}
}
else if (atd.getSimpleName().equals("ControlReferences"))
{
Collection<Declaration> decls = getAnnotationProcessorEnvironment().getDeclarationsAnnotatedWith(atd);
for (Declaration decl : decls)
{
if ( decl instanceof TypeDeclaration )
{
TypeDeclaration clientType = (TypeDeclaration)decl;
Set<TypeMirror> controlTypes = clientsMap.get( clientType );
if ( controlTypes == null )
{
controlTypes = new HashSet<TypeMirror>();
clientsMap.put( clientType, controlTypes );
}
// Read ControlReferences annotation
AnnotationMirror controlMirror = null;
for (AnnotationMirror annot : clientType.getAnnotationMirrors())
{
if (annot.getAnnotationType().getDeclaration().getQualifiedName().equals(
"org.apache.beehive.controls.api.bean.ControlReferences"))
{
controlMirror = annot;
break;
}
}
assert( controlMirror != null );
// Add each control type listed in the ControlReferences annotation
AptAnnotationHelper controlAnnot = new AptAnnotationHelper(controlMirror);
Collection<AnnotationValue> references = (Collection<AnnotationValue>)controlAnnot.getObjectValue("value");
if ( references != null )
{
for ( AnnotationValue av : references )
{
TypeMirror crType = (TypeMirror)av.getValue();
controlTypes.add( crType );
}
}
}
}
}
}
// For each client type:
// 1 - emit a controls client manifest in the same dir as the client type's class.
// 2 - emit a controls client initializer class in the same pkg/dir as the client type's class
Filer f = getAnnotationProcessorEnvironment().getFiler();
Set<TypeDeclaration> clientTypes = clientsMap.keySet();
for ( TypeDeclaration clientType : clientTypes )
{
// Emit manifest
String clientPkg = clientType.getPackage().getQualifiedName();
File clientManifestName =
new File( clientType.getSimpleName() + ControlClientManifest.FILE_EXTENSION );
ControlClientManifest mf = new ControlClientManifest( clientType.getQualifiedName() );
try
{
Set<TypeMirror> controlTypes = clientsMap.get( clientType );
for ( TypeMirror controlType : controlTypes )
{
InterfaceDeclaration controlIntfOrExt = getControlInterfaceOrExtension(controlType);
InterfaceDeclaration controlIntf = getMostDerivedControlInterface( controlIntfOrExt );
assert controlIntf != null : "Can't find most derived control intf for=" + controlIntfOrExt;
ControlInterface annot = controlIntf.getAnnotation(ControlInterface.class);
String defBinding = annot.defaultBinding();
defBinding = ControlBeanContext.resolveDefaultBinding( defBinding, controlIntf.getQualifiedName() );
mf.addControlType( controlIntfOrExt.getQualifiedName(), defBinding );
}
mf.emit( f, clientPkg, clientManifestName, null );
}
catch ( IOException ie )
{
printError( clientType, "controls.client.manifest.ioerror" );
ie.printStackTrace( );
}
// Emit initializer
AnnotationProcessorEnvironment env = getAnnotationProcessorEnvironment();
Generator genClass = new AptControlClient( clientType, this );
if ( genClass != null )
{
try
{
List<GeneratorOutput> genList = genClass.getGenerateOutput(env.getFiler());
if (genList == null || genList.size() == 0)
return;
for (GeneratorOutput genOut : genList)
{
getGenerator().generate(genOut);
}
}
catch (IOException ioe)
{
throw new CodeGenerationException("Code generation failure: ", ioe);
}
}
}
}
@Override
public void generate(Declaration decl)
{
}
private void checkControlField( FieldDeclaration f )
{
TypeMirror fieldType = f.getType();
// Make sure that this field doesn't try to override another that's inherited.
String fieldName = f.getSimpleName();
TypeDeclaration declaringType = f.getDeclaringType();
if ( declaringType instanceof ClassDeclaration )
{
for ( ClassType i = ( ( ClassDeclaration ) declaringType ).getSuperclass(); i != null; i = i.getSuperclass() )
{
ClassDeclaration decl = i.getDeclaration();
if ( decl != null )
{
for ( FieldDeclaration baseClassField : decl.getFields() )
{
if ( fieldName.equals( baseClassField.getSimpleName() ) )
{
Collection<Modifier> modifiers = baseClassField.getModifiers();
if ( modifiers.contains( Modifier.PROTECTED ) || modifiers.contains( Modifier.PUBLIC ) )
{
printError( f, "control.field.override", decl.getQualifiedName() );
}
}
}
}
}
}
// Valid control field instances can be of an interface type
// or a class type.
if ( fieldType instanceof InterfaceType )
{
// Valid interface type decls must be annotated w/ @ControlInterface
// or @ControlExtension.
Declaration fieldTypeDecl = ((InterfaceType)fieldType).getDeclaration();
if ( fieldTypeDecl.getAnnotation(ControlInterface.class) == null &&
fieldTypeDecl.getAnnotation(ControlExtension.class) == null )
printError( f, "control.field.bad.interfacetype" );
}
else if ( fieldType instanceof ClassType )
{
// Valid class type decls must implements the ControlBean API.
// Walk the implementation inheritance hierarchy, seeing if one of the
// classes implements ControlBean.
//
// REVIEW: Does NOT check if the interfaces might implement ControlBean!
// This is unnecessary for our impl, since our generated bean class directly
// implements ControlBean, but other impls may choose to do otherwise.
boolean foundControlBean = false;
ClassType classType = (ClassType)fieldType;
if (classType.getDeclaration() != null)
{
outer: while ( classType != null )
{
Collection<InterfaceType> intfs = classType.getSuperinterfaces();
for ( InterfaceType intfType : intfs )
{
if ( intfType.getDeclaration().getQualifiedName().equals( "org.apache.beehive.controls.api.bean.ControlBean" ) )
{
foundControlBean = true;
break outer;
}
}
classType = classType.getSuperclass();
}
if ( !foundControlBean )
printError( f, "control.field.bad.classtype" );
// Valid generated beans should only "implement" the control interface/extension, and no others
classType = (ClassType)fieldType;
Collection<InterfaceType> intfs = classType.getSuperinterfaces();
if ( intfs.size() != 1 )
{
printError( f, "control.field.bad.classtype.badinterface" );
}
for ( InterfaceType intfType : intfs )
{
if ( intfType.getDeclaration().getAnnotation(ControlExtension.class) == null &&
intfType.getDeclaration().getAnnotation(ControlInterface.class) == null)
{
printError( f, "control.field.bad.classtype.badinterface");
}
}
}
else
{
// TODO: This could be a ControlBean type that is going to be generated by
// the current APT processing iteration. It should be possible to do more
// specific verification here using the getTypeDeclaration API on
// AnnotationProcessorEnvironment. In any event, the implementation of
// getControlInterface will properly handle this case, and if it cannot a
// malformed type error will be generated.
}
}
else
{
printError( f, "control.field.bad.type" );
}
// Enforce any versioning requirements this control field has.
//
// Since our generate() does some detailed grovelling of control types, make sure that
// will not result in an error by doing that grovelling now. Control types may be
// malformed if the source for those types has errors (yet the apt type may still exist!).
try
{
InterfaceDeclaration controlIntfOrExt = getControlInterfaceOrExtension(fieldType);
InterfaceDeclaration controlIntf = getMostDerivedControlInterface( controlIntfOrExt );
if ( controlIntf != null )
{
enforceVersionRequired( f, controlIntf );
}
else
{
printError( f, "control.field.type.malformed" );
}
}
catch ( CodeGenerationException cge )
{
printError( f, "control.field.type.malformed" );
}
assert declaringType != null : "Field " + f + " has no declaring type!";
if ( declaringType.getDeclaringType() != null )
printError( f, "control.field.in.inner.class" );
Collection<Modifier> mods = f.getModifiers();
if ( mods.contains( Modifier.TRANSIENT ))
printError( f, "transient.control.field" );
if ( mods.contains( Modifier.STATIC ))
printError( f, "static.control.field" );
}
private void checkControlClientType( TypeDeclaration t )
{
// validate @ControlReferences
AnnotationMirror controlMirror = null;
for (AnnotationMirror annot : t.getAnnotationMirrors())
{
if (annot.getAnnotationType().getDeclaration().getQualifiedName().equals(
"org.apache.beehive.controls.api.bean.ControlReferences"))
{
controlMirror = annot;
break;
}
}
// Bail out if no @ControlReferences annotation found
if ( controlMirror == null )
return;
AptAnnotationHelper controlAnnot = new AptAnnotationHelper(controlMirror);
//
// Validate that the types listed in the ControlReferences annotations are actually
// control types.
//
Collection<AnnotationValue> references = (Collection<AnnotationValue>)controlAnnot.getObjectValue("value");
if ( references != null )
{
for ( AnnotationValue av : references )
{
DeclaredType crType = (DeclaredType)av.getValue();
if ( crType instanceof InterfaceType )
{
// Valid interface type decls must be annotated w/ @ControlInterface
// or @ControlExtension.
Declaration typeDecl = crType.getDeclaration();
if ( typeDecl.getAnnotation(ControlInterface.class) == null &&
typeDecl.getAnnotation(ControlExtension.class) == null )
printError( t, "control.reference.bad.interfacetype" );
}
}
}
}
/**
* Given a InterfaceType or ClassType, returns the InterfaceType for the control type's
* public interface/extension.
* @param intfOrBeanClass
* @return
*/
private InterfaceDeclaration getControlInterfaceOrExtension( TypeMirror intfOrBeanClass )
{
if (intfOrBeanClass instanceof InterfaceType)
{
return ((InterfaceType)intfOrBeanClass).getDeclaration();
}
else if (intfOrBeanClass instanceof ClassType)
{
ClassType classType = (ClassType)intfOrBeanClass;
// If the bean type declaration cannot be found, then the only (valid) possibility
// is that it is a generated type from the current processor pass. See if a base
// interface type can be determined from the current processor input list.
if (classType.getDeclaration() == null)
{
//
// Compute the bean type name, and the associated interface name by stripping
// the "Bean" suffix
//
String className = classType.toString();
String intfName = className.substring(0, className.length() - 4);
AnnotationProcessorEnvironment ape = getAnnotationProcessorEnvironment();
InterfaceDeclaration id = (InterfaceDeclaration)ape.getTypeDeclaration(intfName);
if (id == null)
{
// The specified class name may not be fully qualified. In this case, the
// best we can do is look for a best fit match against the input types
for (TypeDeclaration td :ape.getSpecifiedTypeDeclarations())
{
if (td instanceof InterfaceDeclaration &&
td.getSimpleName().equals(intfName))
{
return (InterfaceDeclaration)td;
}
}
}
return id;
}
else
{
// direct supers only
Collection<InterfaceType> intfs = classType.getSuperinterfaces();
// per the code in checkControlField, this set must be of size 1
// and the 1 super interface must be a control interface/extension
assert ( intfs.size() == 1 );
for ( InterfaceType intfType : intfs )
return intfType.getDeclaration();
}
}
else
{
throw new CodeGenerationException( "Param not a interface or class type");
}
return null;
}
/**
* Given a control interface or extension, do a BFS of its inheritance heirarchy for
* the first one marked with @ControlInterface. This represents the point in the
* heirarchy where use of @ControlExtension changes to use of @ControlInterface.
*
* @param controlIntfOrExt an interface annotated with @ControlInterface or @ControlExtension.
* @return most derived interface in the heirarchy annotated with @ControlInterface, null
* if no such interface found.
*/
private InterfaceDeclaration getMostDerivedControlInterface( InterfaceDeclaration controlIntfOrExt )
{
Queue<InterfaceDeclaration> q = new LinkedList<InterfaceDeclaration>();
InterfaceDeclaration id = controlIntfOrExt;
while ( id != null )
{
if ( id.getAnnotation(ControlInterface.class) != null )
break;
Collection<InterfaceType> supers = id.getSuperinterfaces();
for ( InterfaceType s : supers )
q.offer( s.getDeclaration() );
id = q.poll();
}
return id;
}
/**
* Enforces the VersionRequired annotation for control fields.
*/
private void enforceVersionRequired( FieldDeclaration f, InterfaceDeclaration controlIntf )
{
VersionRequired versionRequired = f.getAnnotation(VersionRequired.class);
Version versionPresent = controlIntf.getAnnotation(Version.class);
if ( versionRequired != null )
{
int majorRequired = versionRequired.major();
int minorRequired = versionRequired.minor();
if ( majorRequired < 0 ) // no real version requirement
return;
int majorPresent = -1;
int minorPresent = -1;
if ( versionPresent != null )
{
majorPresent = versionPresent.major();
minorPresent = versionPresent.minor();
if ( majorRequired <= majorPresent &&
(minorRequired < 0 || minorRequired <= minorPresent) )
{
// Version requirement is satisfied
return;
}
}
//
// Version requirement failed
//
printError( f, "control.field.bad.version", f.getSimpleName(), majorRequired, minorRequired,
majorPresent, minorPresent );
}
}
/**
* Returns the CodeGenerator instance supporting this processor, instantiating a new
* generator instance if necessary.
*/
protected CodeGenerator getGenerator()
{
if (_generator == null)
{
//
// Locate the class that wraps the Velocity code generation process
//
AnnotationProcessorEnvironment env = getAnnotationProcessorEnvironment();
try
{
_generator = new VelocityGenerator(env);
}
catch (Exception e)
{
throw new CodeGenerationException("Unable to create code generator", e);
}
}
return _generator;
}
CodeGenerator _generator;
}