Package org.apache.beehive.controls.runtime.generator.apt

Source Code of org.apache.beehive.controls.runtime.generator.apt.ControlClientAnnotationProcessor

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;
}
TOP

Related Classes of org.apache.beehive.controls.runtime.generator.apt.ControlClientAnnotationProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.