Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
   *
   * Copyright (c) 2007-2011 Oracle and/or its affiliates. All rights reserved.
   *
   * The contents of this file are subject to the terms of either the GNU
   * General Public License Version 2 only ("GPL") or the Common Development
   * and Distribution License("CDDL") (collectively, the "License").  You
   * may not use this file except in compliance with the License.  You can
  * obtain a copy of the License at
  * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
  * or packager/legal/LICENSE.txt.  See the License for the specific
  * language governing permissions and limitations under the License.
  *
  * When distributing the software, include this License Header Notice in each
  * file and include the License file at packager/legal/LICENSE.txt.
  *
  * GPL Classpath Exception:
  * Oracle designates this particular file as subject to the "Classpath"
  * exception as provided by Oracle in the GPL Version 2 section of the License
  * file that accompanied this code.
  *
  * Modifications:
  * If applicable, add the following below the License Header, with the fields
  * enclosed by brackets [] replaced by your own identifying information:
  * "Portions Copyright [year] [name of copyright owner]"
  *
  * Contributor(s):
  * If you wish your version of this file to be governed by only the CDDL or
  * only the GPL Version 2, indicate your decision by adding "[Contributor]
  * elects to include this software in this distribution under the [CDDL or GPL
  * Version 2] license."  If you don't indicate a single choice of license, a
  * recipient has the option to distribute your version of this file under
  * either the CDDL, the GPL Version 2 or to extend the choice of license to
  * its licensees as provided above.  However, if you add GPL Version 2 code
  * and therefore, elected the GPL Version 2 license, then the option applies
  * only if the new code is made subject to such option by the copyright
  * holder.
  */
 package org.jvnet.hk2.config.generator;
 
 
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
Generates org.jvnet.hk2.config.ConfigInjector implementations for org.jvnet.hk2.config.Configured objects and org.jvnet.hk2.config.ConfigBeanProxy subtypes.

Author(s):
Kohsuke Kawaguchi
    private final AnnotationProcessorEnvironment env;
    private JCodeModel cm;
    final TypeMath math;
    
Reference to the org.jvnet.hk2.config.ConfigBeanProxy type.
    private final TypeDeclaration configBeanProxy;
        this. = env;
        this. = new TypeMath(env);
    }
    public void process() {
         = new JCodeModel();
        for(Declaration d : .getDeclarationsAnnotatedWith(ann))
            d.accept(this);
        try {
            .build(new FilerCodeWriter(.getFiler()));
        } catch (IOException e) {
            throw new Error(e);
        }
         = null;
    }

    
For each class annotated with org.jvnet.hk2.config.Configured.
    public void visitClassDeclaration(ClassDeclaration clz) {
        try {
            new ClassGenerator(clz,false).generate();
        } catch (JClassAlreadyExistsException e) {
            .getMessager().printError(clz.getPosition(),e.toString());
        }
    }

    
    public void visitInterfaceDeclaration(InterfaceDeclaration clz) {
        try {
            if(!isSubType(clz,)) {
                .getMessager().printError(clz.getPosition(),
                    clz.getQualifiedName()+" has @Configured but doesn't extend ConfigBeanProxy");
            } else
                new ClassGenerator(clz,true).generate();
        } catch (JClassAlreadyExistsException e) {
            .getMessager().printError(clz.getPosition(),e.toString());
        }
    }
    private boolean isSubType(TypeDeclaration subTypeTypeDeclaration baseType) {
        Types u = .getTypeUtils();
        return u.isSubtype(u.getDeclaredType(subType),u.getDeclaredType(baseType));
    }
    /*package*/ class ClassGenerator {
        final TypeDeclaration clz;
        final JDefinedClass injector;
        final JClass targetType;
        final JAnnotationUse service;
        final MultiMap<String,Stringmetadata = new MultiMap<String,String>();
        
        private Property key=null;

        
If true, generate a ConfigInjector that extends from org.jvnet.hk2.config.NoopConfigInjector. This is used as the metadata place holder for org.jvnet.hk2.config.ConfigBeanProxys.

If false, generate a real org.jvnet.hk2.config.ConfigInjector.

See the test-config test module for this difference in action.

        private final boolean generateNoopConfigInjector;
        public ClassGenerator(TypeDeclaration clzboolean generateNoopConfigInjectorthrows JClassAlreadyExistsException {
            this. = clz;
            this. = generateNoopConfigInjector;
            Configured c = clz.getAnnotation(Configured.class);
            String name = clz.getQualifiedName();
             = .ref(name);
            // [RESULT]
            // @Service(name='...')
            // @InjectionTarget(target)
            // public class XYZInjector extends ConfigInjector<XYZ>
             = ._class(name+"Injector");
            String elementName = c.name();
            if(c.local()) {
                if(elementName.length()>0) {
                    .getMessager().printError(clz.getPosition(),"@Configured.local and @Configured.name is mutually exclusive");
                    elementName = ""// error recovery
                }
            } else {
                if(elementName.length()==0) // infer default
                    elementName = Dom.convertName(clz.getSimpleName());
            }
             = .annotate(Service.class).param("name",elementName);
            .annotate(InjectionTarget.class).param("value",);
            if(generateNoopConfigInjector) {
                ._extends(.ref(NoopConfigInjector.class));
                 = null;
                 = null;
                 = null;
            } else {
                ._extends(.ref(ConfigInjector.class).narrow());
                // [RESULT]
                // public void inject(Dom dom, Property target) { ... }
                 = .method(.void.class"inject");
                .param(Dom.class"dom");
                .param("target");
                .body();
                 = .method(.,void.class,"injectAttribute");
                addReinjectionParam();
                 = .method(.,void.class,"injectElement");
                addReinjectionParam();
            }
            .add(.,name);
            // locate additional contracts for the target.
            for (TypeDeclaration t : ContractFinder.find(clz))
                .add(.,t.getQualifiedName());
        }
        private void addReinjectionParam(JMethod method) {
            method.param(Dom.class,"dom");
            method.param(String.class,"name");
            method.param("target");
        }

        
Visits all annotated fields/methods and generates the body of the org.jvnet.hk2.config.ConfigInjector.inject(org.jvnet.hk2.config.Dom,java.lang.Object) code.
        public void generate() {
            Stack<TypeDeclarationq = new Stack<TypeDeclaration>();
            Set<TypeDeclarationvisited = new HashSet<TypeDeclaration>();
            q.push();
            while(!q.isEmpty()) {
                TypeDeclaration t = q.pop();
                if(!visited.add(t)) continue;   // been here already
                for (FieldDeclaration f : t.getFields())
                    generate(new Property.Field(f));
                for (MethodDeclaration m : t.getMethods())
                    generate(new Property.Method(m));
                for (InterfaceType it : .getSuperinterfaces())
                    q.add(it.getDeclaration());
                if (t instanceof ClassDeclaration) {
                    ClassDeclaration cd = (ClassDeclarationt;
                    if(cd.getSuperclass()!=null)
                        q.add(cd.getSuperclass().getDeclaration());
                }
            }
            .param("metadata".toCommaSeparatedString());
        }
        private void generate(Property p) {
            Attribute a = p.getAnnotation(Attribute.class);
            Element e = p.getAnnotation(Element.class);
            if(a!=null) {
                new AttributeMethodGenerator(p,a).generate();
                if(e!=null)
                    .getMessager().printError(p.decl().getPosition(),"Cannot have both @Element and @Attribute at the same time");
            } else {
                if(e!=null)
                    new ElementMethodGenerator(p,e).generate();
            }
            // Updates #key with error check.
            if(p.isKey()) {
                if(!=null) {
                    .getMessager().printError(p.decl().getPosition(),"Multiple key properties");
                    .getMessager().printError(.decl().getPosition(),"Another one is at here");
                }
                 = p;
            }
        }

        
Generates a single injection method, which injects value(s) of a particular element/attribute name.
        private abstract class MethodGenerator {
            final JBlock body;
            final JVar $dom;
            final JVar $target;
            
Element name or attribute name. A special case is "*" for elements that indicate substitute-by-type.
            final String xmlName;
            final Property p;
            private int iota=1;
            
Erasure of p.type()
            final TypeMirror erasure;
            
If this is a multi-value property, the packer knows how to create a collection value.
            final Packer packer;
            
The type of individual item. If this is a multi-value property, this is a type of the collection item, otherwise the same as erasure.
            final TypeMirror itemType;
            
Converter for itemType.
            /*semi-final*/ Converter conv;
            MethodGenerator(String methodNamePrefixJMethod reinjectionMethodProperty pString xmlName) {
                this. = p.inferName(xmlName);
                this. = p;
                if() {
                     = null;
                     = null;
                     = null;
                } else {
                    JMethod m = .method(.,void.classmethodNamePrefix+p.seedName());
                     = m.param(Dom.class,"dom");
                     = m.param(,"target");
                     = m.body();
                    .body().invoke(m).arg().arg();
                    reinjectionMethod.body()._if(JExpr.lit(this.).invoke("equals").arg(JExpr.ref("name")))
                        ._then().invoke(m).arg().arg();
                }
                 = erasure(p.type());
                 = createPacker(p.type(),);
                 = ==null ?  : erasure(.itemType());
            }
            private void assign(JExpression rhs) {
                .assign(,,rhs);
            }

            
Returns '@xmlName' for attributes and '<xmlName>' for elements.
            protected abstract String xmlTokenName();
            protected void generate() {
                 = createConverter();
                .addMetadata(xmlTokenName(),);
                if(!isVariableExpansion() && .apply(,null)!=.ref(String.class))
                    .getMessager().printError(.decl().getPosition(),"variableExpansion=false is only allowed on String");
                if(!) {
                    JVar value = var(
                        !=null ? .ref(List.class).narrow(.sourceType()) : .sourceType(),getXmlValue());
                    if(!isRequired())
                        ._if(value.eq(JExpr._null()))._then()._return();
                    if(!=null)
                        handleMultiValue(value);
                    else
                        assign(.as(value,));
                }
                if(.isKey())
                    addKey();
            }

            
Returns true if the property must have a value, or if it's optional.
            protected abstract boolean isRequired();

            
Returns true if the property is a referenec to another element
            protected abstract boolean isReference();

            
Returns true if the property is a a subject of variable expansion.
            protected abstract boolean isVariableExpansion();

            
Return true if this property is @FromElement("*"), which means finding a match by types
            protected abstract boolean isAllElementMatch();

            
Obtains the source value(s) from org.jvnet.hk2.config.Dom.
            protected abstract JExpression getXmlValue();
            private void addKey() {
                .add(.xmlTokenName());
            }

            
Invokes a method on DOM by adjusting the name for plural.
            final JInvocation invokeDom(String methodName) {
                if(!=null)    methodName+='s';
                return .invoke(methodName);
            }
            
            private void handleMultiValue(JVar values) {
                // [RESULT]
                // List<S> values = dom.leafElements("...");
                // <packer init>
                // for( S v : values ) {
                //   <packer set>(<as>(v));
                // }
                //  ... assign ...
                .start(values.invoke("size"));
                JForEach forEach = .forEach(.sourceType(), id(), values);
                .pack(forEach.body(),.as(forEach.var(),.itemType()), forEach.var());
                assign(.end());
            }

            
Creates a variable
            protected JVar var(JType tJExpression init) {
                return .decl(t,id(),init);
            }
            protected JVar var(Class tJExpression init) {
                return var(.ref(t),init);
            }

            
Creates an unique id.
            private String id() {
                return "v"+(++);
            }
            private Packer createPacker(TypeMirror typeTypeMirror erasure) {
                if(erasure instanceof ArrayType) {
                    // T=X[]
                    return new ArrayPacker((ArrayType)erasure);
                }
                TypeMirror itemType = .isCollection(type);
                if(itemType!=null) {
                    // T=Collection[]
                    return new ListPacker(type,itemType);
                }
                TypeMirror mapType = ..apply(type.getTypeDeclaration(Map.class.getName()));
                if(mapType!=null) {
                    // T=Map<...>
                    DeclaredType d = (DeclaredType)mapType;
                    Iterator<TypeMirroritr = d.getActualTypeArguments().iterator();
                    itr.next();
                    return new MapPacker(itr.next());
                }
                return null;
            }
            abstract class Packer {
                abstract TypeMirror itemType();
                
Starts packing.
                abstract void start(JExpression $valueSize);
                
Adds one more item to the pack.
                abstract void pack(JBlock blockJExpression itemJExpression sourceValue);

                
Returns the packed value to be set.
                abstract JExpression end();
            }
            final class ArrayPacker extends Packer {
                private JVar $array,$index;
                private final JType arrayT;
                private final JType componentT;
                private final ArrayType at;
                public ArrayPacker(ArrayType t) {
                    this. = t;
                    this. = .apply(itemType(),null);
                    this. = .array();
                }
                TypeMirror itemType() {
                    return .getComponentType();
                }
                void start(JExpression $valueSize) {
                    // [RESULT]
                    // T[] x = new T[values.size()];
                     = var(, JExpr.newArray($valueSize));
                     = var(int.class,JExpr.lit(0));
                }
                void pack(JBlock blockJExpression itemJExpression sourceValue) {
                    // [RESULT]
                    // x[i++] = <rhs>;
                    block.assign(.component(.incr()),item);
                }
                JExpression end() {
                    return ;
                }
            }
            final class ListPacker extends Packer {
                private JVar $list;
                private final JClass collectionType,itemType;
                private final TypeMirror itemT;
                public ListPacker(TypeMirror collectionTypeTypeMirror itemType) {
                    this. = .apply(collectionType,null).boxify();
                    this.       = .apply(itemType,null).boxify();
                    this. = itemType;
                }
                TypeMirror itemType() {
                    return ;
                }
                void start(JExpression $valueSize) {
                    // [RESULT]
                    // T x = new ArrayList<T>(values.size());
                     = var(,JExpr._new(implType()).arg($valueSize));
                }

                
Figure out the concrete implementation class to be used.
                JType implType() {
                    if(.ref(Set.class).isAssignableFrom())
                        return .ref(HashSet.class).narrow();
                    return .ref(ArrayList.class).narrow();
                }
                void pack(JBlock blockJExpression itemJExpression sourceValue) {
                    // [RESULT]
                    // x.add(<rhs>);
                    block.invoke(,"add").arg(item);
                }
                JExpression end() {
                    return ;
                }
            }
            final class MapPacker extends Packer {
                private JVar $map;
                private final TypeMirror itemT;
                public MapPacker(TypeMirror itemType) {
                    this. = itemType;
                }
                TypeMirror itemType() {
                    return ;
                }
                void start(JExpression $valueSize) {
                    // [RESULT]
                    // T x = new HashMap<T>();
                     = var(Map.class,JExpr._new(.ref(HashMap.class)).arg($valueSize));
                }
                void pack(JBlock blockJExpression itemJExpression itemDom) {
                    // [RESULT]
                    // x.put(dom.getKey(),<rhs>);
                    block.invoke(,"put").arg(itemDom.invoke("getKey")).arg(item);
                }
                JExpression end() {
                    return ;
                }
            }
            private Converter createConverter(TypeMirror itemType) {
                try {
                    // is this a leaf value?
                    ..apply(itemType, JExpr._null());
                    return new LeafConverter();
                } catch (UnsupportedOperationException e) {
                    // nope
                }
                // try to handle it as a reference
                if (itemType instanceof DeclaredType) {
                    if (!(itemType instanceof ClassType || itemType instanceof InterfaceType)) {
                        .getMessager().printError(.decl().getPosition(),
                        "I only inject interfaces or classes,  "+itemType+" is not one of them");
                        return new NodeConverter();
                    }
                    TypeDeclaration decl = ((DeclaredType)itemType).getDeclaration();
                    Configured cfg = decl.getAnnotation(Configured.class);
                    if(cfg!=null) {
                        // node value
                        if(isReference())
                            return new ReferenceConverter();
                        else
                            return new NodeConverter();
                    }
                }
                if(isAllElementMatch()) {
                    return new NodeByTypeConverter(itemType);
                }
                .getMessager().printError(.decl().getPosition(),
                    "I don't know how to inject "+itemType+" from configuration");
                return new NodeConverter(); // error recovery
            }

            
Encapsulates the source value representation in org.jvnet.hk2.config.Dom.
            abstract class Converter {
                
Generates an expression that converts 'rhs'.

Parameters:
targetType The expected type of the expression, so that the generated expression can contain cast operation if necessary.
                abstract JExpression as(JExpression rhsTypeMirror targetType);
                
Source value type as returned by org.jvnet.hk2.config.Dom.
                abstract JClass sourceType();

                
True if the XML representation of the source value is a leaf (string value) as opposed to node (an XML fragment.)
                abstract boolean isLeaf();
                
                abstract void addMetadata(String key,TypeMirror itemType);
                protected final String makeCollectionIfNecessary(String s) {
                    if(!=null)    return "collection:"+s;
                    else                return s;
                }
            }
            class LeafConverter extends Converter {
                JExpression as(JExpression rhsTypeMirror targetType) {
                    return ..apply(targetTyperhs);
                }
                JClass sourceType() {
                    return .ref(String.class);
                }
                boolean isLeaf() {
                    return true;
                }
                void addMetadata(String key,TypeMirror itemType) {
                    .add(key,makeCollectionIfNecessary("leaf"));
                }
            }
            class NodeConverter extends Converter {
                JExpression as(JExpression rhsTypeMirror targetType) {
                    return JExpr.cast(.apply(targetType,null),rhs.invoke("get"));
                }
                JClass sourceType() {
                    return .ref(Dom.class);
                }
                boolean isLeaf() {
                    return false;
                }
                void addMetadata(String key,TypeMirror itemType) {
                    .add(key,makeCollectionIfNecessary(itemType.toString()));
                }
            }
            class NodeByTypeConverter extends Converter {
                final JClass sourceType;
                NodeByTypeConverter(TypeMirror sourceType) {
                    this. = .apply(sourceType,null).boxify();
                }
                JExpression as(JExpression rhsTypeMirror targetType) {
                    return rhs;
                }
                JClass sourceType() {
                    return ;
                }
                boolean isLeaf() {
                    return false;
                }
                void addMetadata(String key,TypeMirror itemType) {
                    // TODO: we need to indicate that there's open-ended match here
                }
            }
            class ReferenceConverter extends Converter {
                JExpression as(JExpression rhsTypeMirror targetType) {
                    return JExpr.invoke("reference").arg().arg(rhs).arg(.apply(targetType,null).boxify().dotclass());
                }
                JClass sourceType() {
                    return .ref(String.class);
                }
                boolean isLeaf() {
                    return true;
                }
                void addMetadata(String key,TypeMirror itemType) {
                    .add(key,makeCollectionIfNecessary("leaf"));
                    .add(key"reference");
                }
            }
        }
        private final class AttributeMethodGenerator extends MethodGenerator {
            private final Attribute a;
            private AttributeMethodGenerator(Property pAttribute a) {
                super("attribute_"pa.value());
                this. = a;
            }
            protected String xmlTokenName() {
                return '@'+;
            }
            protected boolean isRequired() {
                return .required();
            }
            protected boolean isReference() {
                return .reference();
            }
            protected boolean isVariableExpansion() {
                return .variableExpansion();
            }
            protected boolean isAllElementMatch() {
                return false;
            }
            protected boolean hasDefault() {
                boolean noDefaultValue = 
                    .defaultValue().length() == 1 && .defaultValue().charAt(0) == '\u0000';
                return (!noDefaultValue);
            }
            
Generates the injector that reads an attribute and sets the value.
            @Override
            protected void generate() {
                .add(xmlTokenName(),isRequired()?"required":"optional");
                if (this.hasDefault()) {
                    if (.defaultValue().indexOf(',')!=-1) {
                        .add(xmlTokenName(), '"' + "default:" + .defaultValue() + '"');
                    } else {
                        .add(xmlTokenName(), "default:" + .defaultValue());                        
                    }
                }
                String ant = "";
                try {
                    Class c = .dataType();
                } catch(MirroredTypeException me) { //hack?
                     ant = getCanonicalTypeFrom(me);
                }
                if (ant.length() == 0) { //take it from the return type of method
                    Property.Method m = (Property.Method)// Method needn't be Property's inner class
                    String typeReturnedByMethodDecl = m.decl.getReturnType().toString();
                    .add(xmlTokenName(), "datatype:" + typeReturnedByMethodDecl);
                } else {
                    .add(xmlTokenName(), "datatype:" + ant);
                }
                super.generate();
            }
            protected JExpression getXmlValue() {
                if(!isVariableExpansion() && !=null) {
                    .getMessager().printError(.decl().getPosition(),
                        "collection attribute property is inconsistent with variableExpansion=false");
                }
                return invokeDom(isVariableExpansion()?"attribute":"rawAttribute").arg();
            }
        }
        
        private final class ElementMethodGenerator extends MethodGenerator {
            private final Element e;
            private ElementMethodGenerator(Property pElement e) {
                super("element_"pe.value());
                this. = e;
            }
            protected String xmlTokenName() {
                return '<'++'>';
            }
            protected JExpression getXmlValue() {
                String name;
                if(.isLeaf()) {
                    if(isVariableExpansion())
                        name = "leafElement";
                    else
                        name = "rawLeafElement";
                } else {
                    assert isVariableExpansion();   // this error is checked earlier.
                    if(.equals("*")) {
                        return invokeDom("nodeByTypeElement").arg(.apply(,null).boxify().dotclass());
                    } else
                        name = "nodeElement";
                }
                return invokeDom(name).arg();
            }
            @Override
            protected void generate() {
                super.generate();
                if (==null) {
                    for (AnnotationMirror am : .decl().getAnnotationMirrors()) {
                        if (!am.toString().contains("hk2"))
                            .add(xmlTokenName(), am.toString());
                    }
                }
            }
            protected boolean isRequired() {
                return .required();
            }
            protected boolean isReference() {
                return .reference();
            }
            protected boolean isVariableExpansion() {
                return .variableExpansion();
            }
            protected boolean isAllElementMatch() {
                return .value().equals("*");
            }
        }
    }
    private static String getCanonicalTypeFrom(MirroredTypeException me) {
        TypeMirror tm = me.getTypeMirror();
        if (tm instanceof DeclaredType) {
            DeclaredType dec = (DeclaredTypetm;
            String qn = dec.getDeclaration().getQualifiedName();
            return ( qn );
        }
        return "";  //ok?
    }
    private TypeMirror erasure(TypeMirror type) {
        return .getTypeUtils().getErasure(type);
    }

    
Takes com.sun.mirror.type.TypeMirror and returns the corresponding com.sun.codemodel.JType.
    final APTTypeVisitor<JType,VoidTO_JTYPE = new APTTypeVisitor<JType,Void>() {
        protected JType onPrimitiveType(PrimitiveType typeVoid param) {
            switch (type.getKind()) {
            case :   return .;
            case :      return .;
            case :      return .;
            case :    return .;
            case :     return .;
            case :       return .;
            case :      return .;
            case :     return .;
            }
            throw new AssertionError();
        }
        protected JType onArrayType(ArrayType typeVoid param) {
            return apply(type.getComponentType(),null).array();
        }
        protected JType onClassType(ClassType typeVoid param) {
            // TODO: generics support
            return .ref(type.getDeclaration().getQualifiedName());
        }
        protected JType onInterfaceType(InterfaceType typeVoid param) {
            // TODO: generics support
            return .ref(type.getDeclaration().getQualifiedName());
        }
        protected JType onTypeVariable(TypeVariable typeVoid param) {
            throw new UnsupportedOperationException();
        }
        protected JType onVoidType(VoidType typeVoid param) {
            return .;
        }
        protected JType onWildcard(WildcardType typeVoid param) {
            throw new UnsupportedOperationException();
        }
    };