0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: * $Header:$
0018: */
0019: package org.apache.beehive.controls.runtime.generator;
0020:
0021: import java.io.*;
0022: import java.lang.reflect.*;
0023: import java.lang.annotation.*;
0024: import java.net.*;
0025: import java.util.*;
0026:
0027: import com.sun.mirror.apt.Filer;
0028: import com.sun.mirror.declaration.AnnotationMirror;
0029: import com.sun.mirror.declaration.AnnotationTypeDeclaration;
0030: import com.sun.mirror.declaration.Declaration;
0031: import com.sun.mirror.declaration.InterfaceDeclaration;
0032: import com.sun.mirror.declaration.MethodDeclaration;
0033: import com.sun.mirror.declaration.TypeDeclaration;
0034: import com.sun.mirror.type.DeclaredType;
0035: import com.sun.mirror.type.InterfaceType;
0036: import com.sun.mirror.type.MirroredTypesException;
0037:
0038: import org.apache.beehive.controls.api.bean.ControlChecker;
0039: import org.apache.beehive.controls.api.bean.ControlExtension;
0040: import org.apache.beehive.controls.api.bean.ControlInterface;
0041: import org.apache.beehive.controls.api.bean.ExternalPropertySets;
0042: import org.apache.beehive.controls.api.events.EventSet;
0043: import org.apache.beehive.controls.api.packaging.FeatureInfo;
0044: import org.apache.beehive.controls.api.packaging.ManifestAttribute;
0045: import org.apache.beehive.controls.api.packaging.ManifestAttributes;
0046: import org.apache.beehive.controls.api.properties.PropertySet;
0047: import org.apache.beehive.controls.api.versioning.Version;
0048: import org.apache.beehive.controls.api.versioning.VersionRequired;
0049: import org.apache.beehive.controls.runtime.generator.apt.TwoPhaseAnnotationProcessor;
0050: import org.apache.beehive.controls.runtime.generator.apt.CheckerAnnotationProcessorEnvironmentImpl;
0051:
0052: /**
0053: * The AptControlInterface provides validation and metadata management for a ControlInterface
0054: * or ControlExtension class during APT processing. It is also used to model the interface
0055: * to contextual services, since they parallel the conventions of control interfaces.
0056: */
0057: public class AptControlInterface extends AptType implements Generator {
0058: /**
0059: * Constructs a new AptControlInterface instance where interface information is derived
0060: * from an APT interface declaration
0061: * @param decl the annotated Declaration
0062: * @param ap the top-level annotation processor
0063: */
0064: public AptControlInterface(Declaration decl,
0065: TwoPhaseAnnotationProcessor ap) {
0066: _ap = ap;
0067:
0068: //
0069: // Verify that the @ControlInterface/@ControlExtension annotations are only used on an
0070: // interface.
0071: // Note: AptControlInterface is also used to construct the operation and event model
0072: // for contextual services (see AptContextField). Becaue contextual sevices can actually
0073: // be classes as well as interfaces, the test below has to be specific to the annotated
0074: // use cases
0075: //
0076: if (!(decl instanceof InterfaceDeclaration)
0077: && (decl.getAnnotation(ControlExtension.class) != null || decl
0078: .getAnnotation(ControlInterface.class) != null)) {
0079: _ap.printError(decl,
0080: "control.interface.annotation.badlocation");
0081: return;
0082: }
0083:
0084: _intfDecl = (InterfaceDeclaration) decl;
0085: setDeclaration(_intfDecl);
0086:
0087: _isExtension = initIsExtension();
0088:
0089: _super Class = initSuperClass();
0090:
0091: _operations = initOperations();
0092:
0093: _intfProps = initIntfProperties();
0094:
0095: _propertySets = initPropertySets();
0096:
0097: _eventSets = initEventSets();
0098:
0099: _featureInfo = initFeatureInfo();
0100:
0101: _version = initVersion();
0102: _versionRequired = initVersionRequired();
0103:
0104: //
0105: // Construct a bean instance for this interface
0106: //
0107: _bean = new ControlBean(this );
0108:
0109: //
0110: // Enforce VersionRequired semantics
0111: //
0112: enforceVersionRequired();
0113:
0114: //
0115: // Do work specific to control extensions
0116: //
0117:
0118: if (isExtension()) {
0119: //
0120: // If this is an control extension, run the control-author-specified
0121: // checker class to perform additional validation.
0122: //
0123: check();
0124: }
0125: }
0126:
0127: /**
0128: * Returns the parent control interface or extension type from which the control
0129: * interface is derived (or null, if it is at the root of the interface hierarchy)
0130: */
0131: public InterfaceType getSuperType() {
0132: if (_intfDecl.getSuperinterfaces() == null)
0133: return null;
0134:
0135: for (InterfaceType intfType : _intfDecl.getSuperinterfaces()) {
0136: InterfaceDeclaration super Decl = intfType.getDeclaration();
0137: if (super Decl != null) {
0138: if (super Decl.getAnnotation(ControlExtension.class) != null
0139: || super Decl
0140: .getAnnotation(ControlInterface.class) != null) {
0141: _super Decl = super Decl;
0142: return intfType;
0143: }
0144: }
0145: }
0146:
0147: return null;
0148: }
0149:
0150: /**
0151: * Initializes the super interface that this ControlInterface extends (or sets it to null
0152: * if a base interface)
0153: */
0154: private AptControlInterface initSuperClass() {
0155: //
0156: // Look for a super interface that is either a control interface or extension.
0157: // If found, return it.
0158: //
0159: InterfaceType super Type = getSuperType();
0160: if (super Type == null) {
0161: // At this point, we're processing the root of the interface heirarchy,
0162: // which is not permitted to be a ControlExtension (that would imply a
0163: // ControlExtension that wasn't actually extending a ControlInterface).
0164: if (isExtension()) {
0165: _ap.printError(_intfDecl,
0166: "control.extension.badinterface");
0167: }
0168:
0169: return null;
0170: }
0171:
0172: InterfaceDeclaration super Decl = super Type.getDeclaration();
0173: if (super Decl != null) {
0174: if (super Decl.getAnnotation(ControlExtension.class) != null
0175: || super Decl.getAnnotation(ControlInterface.class) != null) {
0176: _super Decl = super Decl;
0177: AptControlInterface super Intf = new AptControlInterface(
0178: _super Decl, _ap);
0179:
0180: if (!isExtension() && super Intf.isExtension()) {
0181: _ap.printError(_intfDecl,
0182: "control.interface.badinterface");
0183: }
0184: return super Intf;
0185: }
0186: }
0187:
0188: return null;
0189: }
0190:
0191: /**
0192: * Returns the super interface for this interface
0193: */
0194: public AptControlInterface getSuperClass() {
0195: return _super Class;
0196: }
0197:
0198: /**
0199: * Initializes the list of operations declared by this AptControlInterface
0200: */
0201: private AptMethodSet<AptOperation> initOperations() {
0202: AptMethodSet<AptOperation> operList = new AptMethodSet<AptOperation>();
0203:
0204: if (_intfDecl == null)
0205: return operList;
0206:
0207: //
0208: // Add the methods from the current interface and all super interfaces *other*
0209: // than the one from which control inheritance or extension is defined. These
0210: // exceptions are handled on the super ControlInterface (the return value
0211: // of AptControlInterface.initSuperClass())
0212: //
0213: // Do this by:
0214: // - initially populate the check vector with the control interface
0215: // - iterate through the check vector, examining each interface to:
0216: // * ignore the super interface
0217: // * add all declared interface methods to the operations list
0218: // * add any super interfaces to the Vector (avoiding recursion)
0219: // - the iteration continues until all superinterfaces have been processed
0220: //
0221: Vector<InterfaceDeclaration> checkIntfs = new Vector<InterfaceDeclaration>();
0222: checkIntfs.add(_intfDecl);
0223:
0224: for (int i = 0; i < checkIntfs.size(); i++) {
0225: InterfaceDeclaration intfDecl = checkIntfs.elementAt(i);
0226: if (intfDecl.equals(_super Decl))
0227: continue;
0228:
0229: if (intfDecl.getMethods() == null)
0230: continue;
0231:
0232: // Add all declared methods, but ignore the mystery <clinit> methods
0233: for (MethodDeclaration methodDecl : intfDecl.getMethods())
0234: if (!methodDecl.toString().equals("<clinit>()"))
0235: operList
0236: .add(new AptOperation(this , methodDecl, _ap));
0237:
0238: if (intfDecl.getSuperinterfaces() == null)
0239: continue;
0240:
0241: for (InterfaceType super Type : intfDecl
0242: .getSuperinterfaces()) {
0243: InterfaceDeclaration super Decl = super Type
0244: .getDeclaration();
0245: if (super Decl != null
0246: && !checkIntfs.contains(super Decl))
0247: checkIntfs.add(super Decl);
0248: }
0249: }
0250:
0251: return operList;
0252: }
0253:
0254: /**
0255: * Returns the list of ControlOperations declared directly by this AptControlInterface
0256: */
0257: public Collection<AptOperation> getOperations() {
0258: return _operations.getMethods();
0259: }
0260:
0261: /**
0262: * Returns the total number of operations for this control interface
0263: */
0264: public int getOperationCount() {
0265: int count = _operations.size();
0266: if (_super Class != null)
0267: count += _super Class.getOperationCount();
0268:
0269: return count;
0270: }
0271:
0272: /**
0273: * Initializes the list of PropertySets declared or referenced by this AptControlInterface
0274: */
0275: private ArrayList<AptPropertySet> initPropertySets() {
0276: ArrayList<AptPropertySet> propSets = new ArrayList<AptPropertySet>();
0277:
0278: if (_intfDecl == null)
0279: return propSets;
0280:
0281: // TODO: enforce presence of prefixes when multiple property sets w/ the same
0282: // property name exist
0283:
0284: //
0285: // Add the intrinsic/base property set
0286: //
0287:
0288: TypeDeclaration basePropsDecl = _ap
0289: .getAnnotationProcessorEnvironment()
0290: .getTypeDeclaration(
0291: "org.apache.beehive.controls.api.properties.BaseProperties");
0292: if (basePropsDecl != null) {
0293: propSets.add(new AptPropertySet(null, basePropsDecl, _ap));
0294: }
0295:
0296: //
0297: // Add external property sets
0298: //
0299: ExternalPropertySets extPropsAnnotation = _intfDecl
0300: .getAnnotation(ExternalPropertySets.class);
0301: if (extPropsAnnotation != null) {
0302: if (isExtension()) {
0303: _ap.printError(_intfDecl,
0304: "extpropertyset.illegal.usage");
0305: }
0306:
0307: try {
0308: Class[] extProps = extPropsAnnotation.value();
0309: } catch (MirroredTypesException mte) {
0310: Collection<String> extProps = mte.getQualifiedNames();
0311: for (String extPropName : extProps) {
0312: TypeDeclaration extPropDecl = _ap
0313: .getAnnotationProcessorEnvironment()
0314: .getTypeDeclaration(extPropName);
0315: if (extPropDecl != null) {
0316: AptPropertySet extPropSet = new AptPropertySet(
0317: null, extPropDecl, _ap);
0318: propSets.add(extPropSet);
0319: } else {
0320: _ap.printError(_intfDecl,
0321: "extpropertyset.type.not.found",
0322: extPropName);
0323: }
0324: }
0325: }
0326: }
0327:
0328: //
0329: // Add nested property sets
0330: //
0331:
0332: if (_intfDecl.getNestedTypes() == null)
0333: return propSets;
0334:
0335: for (TypeDeclaration innerDecl : _intfDecl.getNestedTypes()) {
0336: boolean fError = false;
0337: if (innerDecl.getAnnotation(PropertySet.class) != null) {
0338: if (!(innerDecl instanceof AnnotationTypeDeclaration)) {
0339: _ap.printError(innerDecl,
0340: "propertyset.not.annotation.type");
0341: fError = true;
0342: }
0343:
0344: Retention ret = innerDecl
0345: .getAnnotation(Retention.class);
0346: if (ret == null
0347: || ret.value() != RetentionPolicy.RUNTIME) {
0348: _ap.printError(innerDecl,
0349: "propertyset.missing.retention");
0350: fError = true;
0351: }
0352:
0353: if (isExtension()) {
0354: _ap.printError(innerDecl,
0355: "propertyset.illegal.usage.2");
0356: fError = true;
0357: }
0358:
0359: if (!fError)
0360: propSets
0361: .add(new AptPropertySet(
0362: this ,
0363: (AnnotationTypeDeclaration) innerDecl,
0364: _ap));
0365: }
0366: }
0367:
0368: //
0369: // Detect the presence of locally declared bound or constrained properties
0370: // Enforce property name (including prefix) uniqueness across all propertysets on this interface.
0371: //
0372:
0373: Set<String> propertyNames = new HashSet<String>();
0374:
0375: for (AptPropertySet propSet : propSets) {
0376: for (AptProperty prop : propSet.getProperties()) {
0377: if (prop.isBound())
0378: _hasBoundProperties = true;
0379:
0380: if (prop.isConstrained())
0381: _hasConstrainedProperties = true;
0382:
0383: String propName = prop.getAccessorName();
0384:
0385: if (propertyNames.contains(propName)) {
0386: _ap.printError(_intfDecl,
0387: "propertyset.duplicate.property.names",
0388: propName, propSet.getShortName());
0389: } else {
0390: propertyNames.add(propName);
0391: }
0392: }
0393: }
0394:
0395: return propSets;
0396: }
0397:
0398: /**
0399: * Returns the list of PropertySets declared directly by this AptControlInterface
0400: */
0401: public Collection<AptPropertySet> getPropertySets() {
0402: return _propertySets;
0403: }
0404:
0405: /**
0406: * Returns the total number of properties for this control interface
0407: */
0408: public int getPropertyCount() {
0409: int count;
0410: if (_super Class == null)
0411: count = 0;
0412: else
0413: count = _super Class.getPropertyCount();
0414:
0415: for (AptPropertySet propertySet : _propertySets) {
0416: // if a property set is set to optional and hasSetters is set to false,
0417: // there isn't a getter or setter available for that property
0418: if (propertySet.hasSetters() || !propertySet.isOptional()) {
0419: count += propertySet.getProperties().size();
0420: }
0421: }
0422:
0423: count += _intfProps.size();
0424: return count;
0425: }
0426:
0427: /**
0428: * Returns the list of properties defined by getter and setter methods in this control interface.
0429: */
0430: public Collection<AptControlInterfaceProperty> getInterfaceProperties() {
0431: return _intfProps;
0432: }
0433:
0434: /**
0435: * Returns true if the interface has any bound properties associated with it.
0436: */
0437: public boolean hasBoundProperties() {
0438: if (_super Class != null && _super Class.hasBoundProperties())
0439: return true;
0440:
0441: return _hasBoundProperties;
0442: }
0443:
0444: /**
0445: * Returns true if this interface is the first interface in the inheritance hierarchy
0446: * to declare support for bound properties. This is used to declared PropertyChangeListener
0447: * registration methods for the bean once (and only once).
0448: */
0449: public boolean addsBoundPropertySupport() {
0450: //
0451: // If a super interface has already added support, then not added here
0452: //
0453: if (_super Class != null
0454: && _super Class.addsBoundPropertySupport())
0455: return false;
0456:
0457: return hasBoundProperties();
0458: }
0459:
0460: /**
0461: * Returns true if any properties declared directly by this control interface are constrained
0462: * properties. This <b>will not</b> reflect the attributes of properties declared on
0463: * an interface from which this interface derives.
0464: */
0465: public boolean hasConstrainedProperties() {
0466: if (_super Class != null
0467: && _super Class.hasConstrainedProperties())
0468: return true;
0469:
0470: return _hasConstrainedProperties;
0471: }
0472:
0473: /**
0474: * Returns true if this interface is the first interface in the inheritance hierarchy
0475: * to declare support for constrained properties. This is used to declared
0476: * VetoableChangeListener registration methods for the bean once (and only once).
0477: */
0478: public boolean addsConstrainedPropertySupport() {
0479: //
0480: // If a super interface has already added support, then not added here
0481: //
0482: if (_super Class != null
0483: && _super Class.addsConstrainedPropertySupport())
0484: return false;
0485:
0486: return hasConstrainedProperties();
0487: }
0488:
0489: /**
0490: * Initializes the list of EventSets declared by this AptControlInterface
0491: */
0492: private ArrayList<AptEventSet> initEventSets() {
0493: ArrayList<AptEventSet> eventSets = new ArrayList<AptEventSet>();
0494:
0495: if (_intfDecl == null || _intfDecl.getNestedTypes() == null)
0496: return eventSets;
0497:
0498: for (TypeDeclaration innerDecl : _intfDecl.getNestedTypes()) {
0499: // HACKHACK: There appear to be mirror API bugs where calling getAnnotation()
0500: // on certain entity types will result in an endless loop. For now, work around
0501: // this by a priori filtering... but this mechanism will drop errors that appear
0502: // on an inapropriate type (see check below)
0503: if (!(innerDecl instanceof InterfaceDeclaration))
0504: continue;
0505:
0506: if (innerDecl.getAnnotation(EventSet.class) != null) {
0507:
0508: if (!(innerDecl instanceof InterfaceDeclaration)) {
0509: _ap.printError(innerDecl, "eventset.illegal.usage");
0510: } else {
0511: eventSets.add(new AptEventSet(this ,
0512: (InterfaceDeclaration) innerDecl, _ap));
0513: }
0514: }
0515: }
0516: return eventSets;
0517: }
0518:
0519: /**
0520: * Returns the list of AptEventSet declared directly by this AptControlInterface
0521: */
0522: public Collection<AptEventSet> getEventSets() {
0523: return _eventSets;
0524: }
0525:
0526: /**
0527: * Returns the total number of operations for this control interface
0528: */
0529: public int getEventSetCount() {
0530: int count = _eventSets.size();
0531: if (_super Class != null)
0532: count += _super Class.getEventSetCount();
0533:
0534: return count;
0535: }
0536:
0537: /**
0538: * Returns the number of event sets declared in this control interface.
0539: * Does not include eventset's declared in super class(es).
0540: */
0541: public int getLocalEventSetCount() {
0542: return _eventSets.size();
0543: }
0544:
0545: /**
0546: * Returns the AptEventSet with the specified name
0547: */
0548: public AptEventSet getEventSet(String name) {
0549: for (AptEventSet eventSet : getEventSets())
0550: if (eventSet.getClassName().equals(name))
0551: return eventSet;
0552:
0553: if (_super Class != null)
0554: return _super Class.getEventSet(name);
0555:
0556: return null;
0557: }
0558:
0559: /**
0560: * Returns the FeatureInfo attributes for this control interface
0561: */
0562: public FeatureInfo getFeatureInfo() {
0563: return _featureInfo;
0564: }
0565:
0566: /**
0567: * Returns the list of fully qualified class names for types that are derived
0568: * from this Generator
0569: */
0570: public String[] getGeneratedTypes() {
0571: return new String[] { _bean.getClassName() };
0572: }
0573:
0574: /**
0575: * Returns the Version annotation, if any.
0576: */
0577: public Version getVersion() {
0578: return _version;
0579: }
0580:
0581: /**
0582: * Returns the VersionRequired annotation, if any.
0583: */
0584: public VersionRequired getVersionRequired() {
0585: return _versionRequired;
0586: }
0587:
0588: /**
0589: * Returns the information necessary to generate a ControlBean from this AptControlInterface
0590: */
0591: public List<GeneratorOutput> getCheckOutput(Filer filer)
0592: throws IOException {
0593: HashMap<String, Object> map = new HashMap<String, Object>();
0594:
0595: map.put("intf", this ); // the control interface
0596: map.put("bean", _bean);
0597:
0598: ArrayList<GeneratorOutput> genList = new ArrayList<GeneratorOutput>();
0599:
0600: //
0601: // the ControlBean class
0602: //
0603: Writer beanWriter = new IndentingWriter(filer
0604: .createSourceFile(_bean.getClassName()));
0605: GeneratorOutput beanSource = new GeneratorOutput(
0606: beanWriter,
0607: "org/apache/beehive/controls/runtime/generator/ControlBean.vm",
0608: map);
0609: genList.add(beanSource);
0610:
0611: //
0612: // the ControlBean BeanInfo class
0613: //
0614: Writer beanInfoWriter = new IndentingWriter(filer
0615: .createSourceFile(_bean.getBeanInfoName()));
0616: GeneratorOutput beanInfoSource = new GeneratorOutput(
0617: beanInfoWriter,
0618: "org/apache/beehive/controls/runtime/generator/ControlBeanInfo.vm",
0619: map);
0620: genList.add(beanInfoSource);
0621:
0622: return genList;
0623: }
0624:
0625: /**
0626: * Returns the information necessary to generate a packaging information from this
0627: * AptControlInterface. Since this information is not needed during type validation,
0628: * it can be delated until the generate phase.
0629: */
0630: public List<GeneratorOutput> getGenerateOutput(Filer filer)
0631: throws IOException {
0632: HashMap<String, Object> map = new HashMap<String, Object>();
0633:
0634: map.put("intf", this ); // the control interface
0635: map.put("bean", _bean);
0636:
0637: ArrayList<GeneratorOutput> genList = new ArrayList<GeneratorOutput>();
0638:
0639: //
0640: // the ControlBean MANIFEST.MF section
0641: //
0642: Writer manifestWriter = filer.createTextFile(
0643: Filer.Location.CLASS_TREE, _bean.getPackage(),
0644: new File(_bean.getShortName() + ".class.manifest"),
0645: null);
0646: GeneratorOutput beanManifest = new GeneratorOutput(
0647: manifestWriter,
0648: "org/apache/beehive/controls/runtime/generator/ControlManifest.vm",
0649: map);
0650: genList.add(beanManifest);
0651:
0652: return genList;
0653: }
0654:
0655: /**
0656: * Returns true if this interface is a ControlExtension (jcx) interface, false
0657: * otherwise.
0658: */
0659: public boolean isExtension() {
0660: return _isExtension;
0661: }
0662:
0663: /**
0664: * Returns the most-derived interface in the inheritance chain that is annotated
0665: * with @ControlInterface. It represents the point in the inheritance chain
0666: * where @ControlInterface becomes @ControlExtension (i.e., anything interface derived from
0667: * the 'most-derived interface' is annotated with @ControlExtension). May return
0668: * null if the inheritance chain is malformed.
0669: */
0670: public AptControlInterface getMostDerivedInterface() {
0671: //
0672: // Walk up ControlInterface chain looking for the 1st instance annotated
0673: // w/ @ControlInterface (as opposed to @ControlExtension)
0674: //
0675: // REVIEW: TBD rules for inheritance of @ControlInterface will affect this.
0676: // Probably need to keep walking and examine each @ControlInterface in the chain.
0677: // Run all checkers in chain? Make checkers responsible for invoking their base
0678: // class-defined checkers?
0679: //
0680:
0681: AptControlInterface ancestor = getSuperClass();
0682: while (ancestor != null) {
0683: if (!ancestor.isExtension())
0684: break;
0685:
0686: ancestor = ancestor.getSuperClass();
0687: }
0688:
0689: return ancestor;
0690: }
0691:
0692: /**
0693: * Returns a classloader that can be used to load external classes
0694: */
0695: public ClassLoader getExternalClassLoader() {
0696: Map<String, String> opts = _ap
0697: .getAnnotationProcessorEnvironment().getOptions();
0698: String classpath = opts.get("-classpath");
0699:
0700: if (classpath != null) {
0701: String[] cpEntries = classpath.split(File.pathSeparator);
0702: ArrayList a = new ArrayList();
0703: for (String e : cpEntries) {
0704: try {
0705: File f = (new File(e)).getCanonicalFile();
0706: URL u = f.toURL();
0707: a.add(u);
0708: } catch (Exception ex) {
0709: System.err
0710: .println("getExternalClassLoader(): bad cp entry="
0711: + e);
0712: System.err.println("Exception processing e=" + ex);
0713: }
0714: }
0715: URL[] urls = new URL[a.size()];
0716: urls = (URL[]) a.toArray(urls);
0717:
0718: return new URLClassLoader(urls, ControlChecker.class
0719: .getClassLoader());
0720: }
0721:
0722: return null;
0723: }
0724:
0725: //
0726: // These are defined by the JAR spec, Name-value pair section
0727: //
0728: static final String alphaNum = "ABCDEFGHIJKLMNOPQRSUVWXYZabcdefghijklmnopqrstuvwyz0123456789";
0729: static final String headerChar = alphaNum + "_-";
0730:
0731: /**
0732: * Validates a manifest attribute. If the attribute is invalid, it will generate
0733: * appropriate APT messager entries and return false, else return true.
0734: */
0735: private boolean isValidManifestAttribute(ManifestAttribute attr) {
0736: String name = attr.name();
0737: String value = attr.value();
0738: boolean isValid = true;
0739:
0740: /*
0741: Note, the null-check for "name" is necessary when the annotation processor is hosted inside
0742: of an IDE where the name attribute of the ManifestAttribute metadata can be null
0743: temporarily. In order to "keep going", just report a warning and proceed.
0744: */
0745: if (name == null || name.length() == 0) {
0746: _ap.printError(_intfDecl,
0747: "manifestattribute.illegal.name.1");
0748: isValid = false;
0749: } else {
0750: if (alphaNum.indexOf(name.charAt(0)) < 0) {
0751: _ap.printError(_intfDecl,
0752: "manifestattribute.illegal.name.2");
0753: isValid = false;
0754: }
0755: for (int i = 1; i < name.length(); i++) {
0756: if (headerChar.indexOf(name.charAt(i)) < 0) {
0757: _ap.printError(_intfDecl,
0758: "manifestattribute.illegal.name.3", name
0759: .charAt(i));
0760: isValid = false;
0761: break;
0762: }
0763: }
0764: }
0765:
0766: /*
0767: Note, the null-check for "value" is necessary when the annotation processor is hosted inside
0768: of an IDE where the value attribute of the ManifestAttribute metadata can be null
0769: temporarily. In order to "keep going", just report a warning and proceed.
0770: */
0771: if (value == null || value.length() == 0) {
0772: _ap.printError(_intfDecl,
0773: "manifestattribute.illegal.name.4");
0774: isValid = false;
0775: } else {
0776: // TODO: validate string contents are valid UTF-8?
0777: }
0778:
0779: return isValid;
0780: }
0781:
0782: /**
0783: * Returns the array of ManifestAttributes associated with the AptControlInterface
0784: */
0785: public HashMap<String, String> getManifestAttributes() {
0786: HashMap<String, String> attributes = new HashMap<String, String>();
0787:
0788: if (_intfDecl == null)
0789: return attributes;
0790:
0791: try {
0792: ManifestAttributes annotAttrs = _intfDecl
0793: .getAnnotation(ManifestAttributes.class);
0794: if (annotAttrs != null) {
0795: ManifestAttribute[] attrs = (ManifestAttribute[]) annotAttrs
0796: .value();
0797: for (int i = 0; i < attrs.length; i++) {
0798: if (isValidManifestAttribute(attrs[i]))
0799: attributes.put(attrs[i].name(), attrs[i]
0800: .value());
0801: }
0802: }
0803: ManifestAttribute annotAttr = _intfDecl
0804: .getAnnotation(ManifestAttribute.class);
0805: if (annotAttr != null) {
0806: if (isValidManifestAttribute(annotAttr))
0807: attributes.put(annotAttr.name(), annotAttr.value());
0808: }
0809: return attributes;
0810: } catch (Exception e) {
0811: e.printStackTrace();
0812: return attributes;
0813: }
0814: }
0815:
0816: /**
0817: * Computes whether this interface is a ControlInterface or a ControlExtension
0818: */
0819: private boolean initIsExtension() {
0820: if (_intfDecl == null)
0821: return false;
0822:
0823: return _intfDecl.getAnnotation(ControlExtension.class) != null;
0824: }
0825:
0826: /**
0827: * Returns the FeatureInfo annotation for this control interface, or null if there is none.
0828: */
0829: private FeatureInfo initFeatureInfo() {
0830: if (_intfDecl == null)
0831: return null;
0832: return _intfDecl.getAnnotation(FeatureInfo.class);
0833: }
0834:
0835: /**
0836: * Returns the Version annotation for this control interface, or null if there is none.
0837: */
0838: private Version initVersion() {
0839: if (_intfDecl == null)
0840: return null;
0841: return _intfDecl.getAnnotation(Version.class);
0842: }
0843:
0844: /**
0845: * Returns the VersionRequired annotation for this control interface, or null if there is none.
0846: */
0847: private VersionRequired initVersionRequired() {
0848: if (_intfDecl == null)
0849: return null;
0850: return _intfDecl.getAnnotation(VersionRequired.class);
0851: }
0852:
0853: /**
0854: * Enforces the VersionRequired annotation for control extensions.
0855: */
0856: private void enforceVersionRequired() {
0857: if (_versionRequired != null) {
0858: if (!isExtension()) {
0859: _ap.printError(_intfDecl,
0860: "versionrequired.illegal.usage");
0861: return;
0862: }
0863:
0864: int majorRequired = _versionRequired.major();
0865: int minorRequired = _versionRequired.minor();
0866:
0867: if (majorRequired < 0) // no real version requirement
0868: return;
0869:
0870: AptControlInterface ci = getMostDerivedInterface();
0871: if (ci == null)
0872: return;
0873:
0874: int majorPresent = -1;
0875: int minorPresent = -1;
0876: Version ciVersion = ci._version;
0877: if (ciVersion != null) {
0878: majorPresent = ciVersion.major();
0879: minorPresent = ciVersion.minor();
0880:
0881: if (majorRequired <= majorPresent
0882: && (minorRequired < 0 || minorRequired <= minorPresent)) {
0883: // Version requirement is satisfied
0884: return;
0885: }
0886: }
0887:
0888: //
0889: // Version requirement failed
0890: //
0891: _ap.printError(_intfDecl, "versionrequired.failed",
0892: _intfDecl.getSimpleName(), majorRequired,
0893: minorRequired, majorPresent, minorPresent);
0894: }
0895: }
0896:
0897: /**
0898: * Runs control-specific checker class (if specified)
0899: */
0900: public void check() {
0901: //
0902: // Find the nearest @ControlInterface, which is where the relevant control checker
0903: // annotation will be found.
0904: //
0905:
0906: AptControlInterface mostDerived = (AptControlInterface) getMostDerivedInterface();
0907: if (mostDerived == null)
0908: return;
0909:
0910: InterfaceDeclaration intfDecl = mostDerived._intfDecl;
0911:
0912: if (intfDecl == null)
0913: return;
0914:
0915: AnnotationMirror controlMirror = null;
0916:
0917: for (AnnotationMirror annot : intfDecl.getAnnotationMirrors()) {
0918: if (annot
0919: .getAnnotationType()
0920: .getDeclaration()
0921: .getQualifiedName()
0922: .equals(
0923: "org.apache.beehive.controls.api.bean.ControlInterface")) {
0924: controlMirror = annot;
0925: break;
0926: }
0927: }
0928:
0929: assert (controlMirror != null) : "Found a control interface that isn't annotated properly: "
0930: + intfDecl;
0931:
0932: AptAnnotationHelper controlAnnot = new AptAnnotationHelper(
0933: controlMirror);
0934:
0935: //
0936: // Read the name of the checker class from the @ControlInterface annotation,
0937: // dynamically load and run it.
0938: //
0939:
0940: DeclaredType checkerMirror = (DeclaredType) controlAnnot
0941: .getObjectValue("checker");
0942: if (checkerMirror == null) {
0943: // try the deprecated 'checkerClass' attribute
0944: checkerMirror = (DeclaredType) controlAnnot
0945: .getObjectValue("checkerClass");
0946: }
0947:
0948: if (checkerMirror != null
0949: && checkerMirror.getDeclaration() != null) {
0950: // TODO: optimize to not invoke default checker?
0951: String checkerName = checkerMirror.toString();
0952:
0953: try {
0954: ClassLoader loader = getExternalClassLoader();
0955:
0956: Class checkerClass = loader.loadClass(checkerName);
0957: if (!ControlChecker.class
0958: .isAssignableFrom(checkerClass)) {
0959: _ap.printError(intfDecl,
0960: "control.interface.illegal.checker",
0961: intfDecl.getSimpleName(), checkerName);
0962: } else {
0963:
0964: Constructor ctor = checkerClass.getConstructor();
0965:
0966: ControlChecker checker = (ControlChecker) ctor
0967: .newInstance();
0968: CheckerAnnotationProcessorEnvironmentImpl ape = new CheckerAnnotationProcessorEnvironmentImpl(
0969: _ap);
0970: checker.check(_intfDecl, ape);
0971: }
0972: } catch (Exception e) {
0973: _ap.printError(intfDecl,
0974: "control.interface.checker.load.failed",
0975: intfDecl.getSimpleName(), checkerName);
0976: }
0977: }
0978: }
0979:
0980: /**
0981: * Build a list of properties defined by getter/setter methods on this control interface.
0982: */
0983: private ArrayList<AptControlInterfaceProperty> initIntfProperties() {
0984:
0985: HashMap<String, AptControlInterfaceProperty> intfPropMap = new HashMap<String, AptControlInterfaceProperty>();
0986:
0987: Collection<AptOperation> ops = getOperations();
0988: for (AptOperation op : ops) {
0989: String opName = op.getName();
0990: if (!op.isPublic()) {
0991: continue;
0992: }
0993:
0994: if (isGetter(op)) {
0995: String propertyName = getIntfPropertyName(op);
0996: if (intfPropMap.containsKey(propertyName)) {
0997: intfPropMap.get(propertyName).setGetterName(opName);
0998: } else {
0999: intfPropMap.put(propertyName,
1000: new AptControlInterfaceProperty(
1001: propertyName, opName, null));
1002: }
1003: } else if (isSetter(op)) {
1004: String propertyName = getIntfPropertyName(op);
1005: if (intfPropMap.containsKey(propertyName)) {
1006: intfPropMap.get(propertyName).setSetterName(opName);
1007: } else {
1008: intfPropMap.put(propertyName,
1009: new AptControlInterfaceProperty(
1010: propertyName, null, opName));
1011: }
1012: } else if (isIsGetter(op)) {
1013: String propertyName = getIntfPropertyName(op);
1014: if (intfPropMap.containsKey(propertyName)) {
1015: intfPropMap.get(propertyName).setGetterName(opName);
1016: } else {
1017: intfPropMap.put(propertyName,
1018: new AptControlInterfaceProperty(
1019: propertyName, opName, null));
1020: }
1021: }
1022: }
1023: return new ArrayList<AptControlInterfaceProperty>(intfPropMap
1024: .values());
1025: }
1026:
1027: /**
1028: * Does the method have a Java Beans getter method signature.
1029: * @param method AptMethod instance
1030: * @return true if getter
1031: */
1032: private boolean isGetter(AptMethod method) {
1033: String methodName = method.getName();
1034:
1035: if (methodName.length() < 4)
1036: return false;
1037:
1038: if (!methodName.startsWith("get"))
1039: return false;
1040:
1041: if (method.getArgList().length() > 0)
1042: return false;
1043:
1044: if ("void".equals(method.getReturnType()))
1045: return false;
1046:
1047: return true;
1048: }
1049:
1050: /**
1051: * Does the method have a Java Beans getter method signature (is varient).
1052: * @param method AptMethod instance.
1053: * @return true if 'is' getter.
1054: */
1055: private boolean isIsGetter(AptMethod method) {
1056: String methodName = method.getName();
1057:
1058: if (methodName.length() < 3)
1059: return false;
1060:
1061: if (!methodName.startsWith("is"))
1062: return false;
1063:
1064: if (method.getArgList().length() > 0)
1065: return false;
1066:
1067: if (!"boolean".equals(method.getReturnType()))
1068: return false;
1069:
1070: return true;
1071: }
1072:
1073: /**
1074: * Does the method have a Java Beans setter method signature (is varient).
1075: * @param method AptMethod instance.
1076: * @return true if setter.
1077: */
1078: private boolean isSetter(AptMethod method) {
1079: String methodName = method.getName();
1080:
1081: if (methodName.length() < 4)
1082: return false;
1083:
1084: if (!methodName.startsWith("set"))
1085: return false;
1086:
1087: String argList = method.getArgList();
1088: if (argList.length() == 0)
1089: return false;
1090:
1091: if (argList.indexOf(',') > -1)
1092: return false;
1093:
1094: if (!"void".equals(method.getReturnType()))
1095: return false;
1096:
1097: return true;
1098: }
1099:
1100: /**
1101: * Generate a property name from a method name.
1102: * @param method AptMethod instance.
1103: * @return property name.
1104: */
1105: private String getIntfPropertyName(AptMethod method) {
1106: String opName = method.getName();
1107:
1108: int prefixIdx = 3;
1109: if (opName.startsWith("is"))
1110: prefixIdx = 2;
1111:
1112: if (opName.length() == prefixIdx + 1)
1113: return "" + Character.toLowerCase(opName.charAt(prefixIdx));
1114:
1115: return Character.toLowerCase(opName.charAt(prefixIdx))
1116: + opName.substring(prefixIdx + 1);
1117: }
1118:
1119: private ArrayList<AptControlInterfaceProperty> _intfProps;
1120: private AptControlInterface _super Class;
1121: private AptMethodSet<AptOperation> _operations;
1122: private ArrayList<AptPropertySet> _propertySets;
1123: private boolean _isExtension; // true if ControlExtension, else ControlInterface
1124: private boolean _hasBoundProperties;
1125: private boolean _hasConstrainedProperties;
1126: private ArrayList<AptEventSet> _eventSets;
1127: private ControlBean _bean;
1128: private FeatureInfo _featureInfo;
1129: private Version _version;
1130: private VersionRequired _versionRequired;
1131: private InterfaceDeclaration _intfDecl;
1132: private InterfaceDeclaration _super Decl;
1133: private TwoPhaseAnnotationProcessor _ap;
1134: }
|