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.netui.compiler;
0020:
0021: import org.apache.beehive.netui.compiler.typesystem.declaration.*;
0022: import org.apache.beehive.netui.compiler.typesystem.env.CoreAnnotationProcessorEnv;
0023: import org.apache.beehive.netui.compiler.typesystem.env.Filer;
0024: import org.apache.beehive.netui.compiler.typesystem.env.Messager;
0025: import org.apache.beehive.netui.compiler.typesystem.type.ArrayType;
0026: import org.apache.beehive.netui.compiler.typesystem.type.ClassType;
0027: import org.apache.beehive.netui.compiler.typesystem.type.DeclaredType;
0028: import org.apache.beehive.netui.compiler.typesystem.type.InterfaceType;
0029: import org.apache.beehive.netui.compiler.typesystem.type.ReferenceType;
0030: import org.apache.beehive.netui.compiler.typesystem.type.TypeInstance;
0031: import org.apache.beehive.netui.compiler.typesystem.type.TypeVariable;
0032: import org.apache.beehive.netui.compiler.typesystem.util.SourcePosition;
0033:
0034: import java.io.File;
0035: import java.net.URI;
0036: import java.net.URISyntaxException;
0037: import java.util.ArrayList;
0038: import java.util.Collection;
0039: import java.util.Collections;
0040: import java.util.Iterator;
0041: import java.util.List;
0042: import java.util.Map;
0043: import java.util.Set;
0044: import java.util.HashMap;
0045:
0046: public class CompilerUtils implements JpfLanguageConstants {
0047: private static final String ERROR_STRING = "<error>";
0048: private static final TypeDeclaration ERROR_TYPE_DECLARATION = new ErrorTypeDeclaration();
0049:
0050: public static boolean isJpfAnnotation(
0051: AnnotationInstance annotation, String unqualifiedName) {
0052: String annotationName = getDeclaration(
0053: annotation.getAnnotationType()).getQualifiedName();
0054: return annotationName.equals(ANNOTATION_QUALIFIER
0055: + unqualifiedName);
0056: }
0057:
0058: public static AnnotationInstance getAnnotation(
0059: ClassDeclaration decl, String unqualifiedName,
0060: boolean inherited) {
0061: if (!inherited)
0062: return getAnnotation(decl, unqualifiedName);
0063:
0064: do {
0065: AnnotationInstance ann = getAnnotation(decl,
0066: unqualifiedName);
0067: if (ann != null)
0068: return ann;
0069: ClassType super Type = decl.getSuperclass();
0070: TypeDeclaration super TypeDecl = getDeclaration(super Type);
0071: decl = super TypeDecl instanceof ClassDeclaration ? (ClassDeclaration) super TypeDecl
0072: : null;
0073: } while (decl != null);
0074:
0075: return null;
0076: }
0077:
0078: public static AnnotationInstance getAnnotation(Declaration element,
0079: String unqualifiedName) {
0080: return getAnnotationFullyQualified(element,
0081: ANNOTATION_QUALIFIER + unqualifiedName);
0082: }
0083:
0084: public static AnnotationInstance getAnnotationFullyQualified(
0085: Declaration element, String fullyQualifiedName) {
0086: AnnotationInstance[] annotations = element
0087: .getAnnotationInstances();
0088:
0089: for (int ii = 0; ii < annotations.length; ii++) {
0090: AnnotationInstance i = annotations[ii];
0091: String iName = getDeclaration(i.getAnnotationType())
0092: .getQualifiedName();
0093: if (fullyQualifiedName.equals(iName))
0094: return i;
0095: }
0096:
0097: return null;
0098: }
0099:
0100: public static AnnotationValue getAnnotationValue(
0101: Declaration element, String annotationName, String valueName) {
0102: AnnotationInstance ann = getAnnotation(element, annotationName);
0103: return ann != null ? getAnnotationValue(ann, valueName, true)
0104: : null;
0105: }
0106:
0107: /**
0108: * If the given annotation exists, assert that the given member is not null</code>, and return it; otherwise,
0109: * if the given annotation does not exist, return null</code>.
0110: */
0111: private static AnnotationValue assertAnnotationValue(
0112: Declaration element, String annotationName,
0113: String valueName, boolean defaultIsNull) {
0114: AnnotationInstance ann = getAnnotation(element, annotationName);
0115:
0116: if (ann == null) {
0117: return null;
0118: } else {
0119: return getAnnotationValue(ann, valueName, defaultIsNull);
0120: }
0121: }
0122:
0123: public static String getStringValue(Declaration element,
0124: String annotationName, String memberName,
0125: boolean defaultIsNull) {
0126: return (String) getValue(element, annotationName, memberName,
0127: defaultIsNull);
0128: }
0129:
0130: public static AnnotationValue getAnnotationValue(
0131: AnnotationInstance annotation, String memberName,
0132: boolean defaultIsNull) {
0133: Map valuesPresent = annotation.getElementValues();
0134:
0135: for (Iterator ii = valuesPresent.entrySet().iterator(); ii
0136: .hasNext();) {
0137: Map.Entry i = (Map.Entry) ii.next();
0138: if (memberName.equals(((AnnotationTypeElementDeclaration) i
0139: .getKey()).getSimpleName())) {
0140: return (AnnotationValue) i.getValue();
0141: }
0142: }
0143:
0144: //
0145: // We didn't find it. If necessary, look for the default value.
0146: //
0147: if (defaultIsNull)
0148: return null;
0149:
0150: AnnotationTypeDeclaration typeDecl = annotation
0151: .getAnnotationType().getAnnotationTypeDeclaration();
0152: if (typeDecl == null)
0153: return null; // type declaration is null in the case of error type
0154:
0155: AnnotationTypeElementDeclaration[] members = typeDecl
0156: .getAnnotationMembers();
0157: for (int i = 0; i < members.length; i++) {
0158: AnnotationTypeElementDeclaration member = members[i];
0159: if (memberName.equals(member.getSimpleName()))
0160: return member.getDefaultValue();
0161:
0162: }
0163:
0164: return null;
0165: }
0166:
0167: public static List getStringArrayValue(Declaration element,
0168: String annotationName, String memberName,
0169: boolean defaultIsNull) {
0170: AnnotationValue value = assertAnnotationValue(element,
0171: annotationName, memberName, defaultIsNull);
0172: if (value == null)
0173: return null;
0174: ArrayList ret = new ArrayList();
0175: getValues(value, ret, false);
0176: return ret;
0177: }
0178:
0179: public static Boolean getBooleanValue(Declaration element,
0180: String annotationName, String memberName,
0181: boolean defaultIsNull) {
0182: AnnotationValue value = assertAnnotationValue(element,
0183: annotationName, memberName, defaultIsNull);
0184: return value != null ? (Boolean) value.getValue() : null;
0185: }
0186:
0187: public static String getString(AnnotationInstance annotation,
0188: String memberName, boolean defaultIsNull) {
0189: AnnotationValue value = getAnnotationValue(annotation,
0190: memberName, defaultIsNull);
0191: return value != null ? (String) value.getValue()
0192: : (defaultIsNull ? null : "");
0193: }
0194:
0195: public static TypeInstance getTypeInstance(
0196: AnnotationInstance annotation, String memberName,
0197: boolean defaultIsNull) {
0198: AnnotationValue value = getAnnotationValue(annotation,
0199: memberName, defaultIsNull);
0200: if (value == null)
0201: return null;
0202: Object typeInstance = value.getValue();
0203: if (isErrorString(typeInstance))
0204: return new ErrorTypeInstance();
0205: assert typeInstance instanceof TypeInstance : "expected TypeInstance, got "
0206: + typeInstance + "(value=" + typeInstance + ")";
0207: return (TypeInstance) typeInstance;
0208: }
0209:
0210: public static String getEnumFieldName(
0211: AnnotationInstance annotation, String memberName,
0212: boolean defaultIsNull) {
0213: AnnotationValue value = getAnnotationValue(annotation,
0214: memberName, defaultIsNull);
0215: return value != null ? getEnumFieldName(value) : null;
0216: }
0217:
0218: public static List getStringArray(AnnotationInstance annotation,
0219: String memberName, boolean defaultIsNull) {
0220: AnnotationValue value = getAnnotationValue(annotation,
0221: memberName, defaultIsNull);
0222: if (value == null)
0223: return null;
0224: ArrayList ret = new ArrayList();
0225: getValues(value, ret, false);
0226: return ret;
0227: }
0228:
0229: public static DeclaredType getDeclaredType(
0230: AnnotationInstance annotation, String memberName,
0231: boolean defaultIsNull) {
0232: return (DeclaredType) getReferenceType(annotation, memberName,
0233: defaultIsNull);
0234: }
0235:
0236: public static ReferenceType getReferenceType(
0237: AnnotationInstance annotation, String memberName,
0238: boolean defaultIsNull) {
0239: AnnotationValue value = getAnnotationValue(annotation,
0240: memberName, defaultIsNull);
0241:
0242: // If the type is the string "<error>", it means that there is already an error related to the type itself.
0243: if (value != null && isErrorString(value.getValue()))
0244: return null;
0245:
0246: return value != null ? (ReferenceType) value.getValue() : null;
0247: }
0248:
0249: public static Integer getInteger(AnnotationInstance annotation,
0250: String memberName, boolean defaultIsNull) {
0251: AnnotationValue value = getAnnotationValue(annotation,
0252: memberName, defaultIsNull);
0253: if (value == null)
0254: return defaultIsNull ? null : new Integer(0);
0255: Object result = value.getValue();
0256:
0257: // If the value isn't of type Integer, silently return 0 -- it's a compile error in user code that will
0258: // be caught by javac.
0259: return result instanceof Integer ? (Integer) result
0260: : new Integer(0);
0261: }
0262:
0263: public static boolean isErrorString(Object str) {
0264: return ERROR_STRING.equals(str);
0265: }
0266:
0267: public static Long getLong(AnnotationInstance annotation,
0268: String memberName, boolean defaultIsNull) {
0269: AnnotationValue value = getAnnotationValue(annotation,
0270: memberName, defaultIsNull);
0271: if (value == null)
0272: return defaultIsNull ? null : new Long(0);
0273: Object result = value.getValue();
0274:
0275: // If the value isn't of type Long, silently return 0 -- it's a compile error in user code that will
0276: // be caught by javac.
0277: return result instanceof Long ? (Long) result : new Long(0);
0278: }
0279:
0280: public static Float getFloat(AnnotationInstance annotation,
0281: String memberName, boolean defaultIsNull) {
0282: AnnotationValue value = getAnnotationValue(annotation,
0283: memberName, defaultIsNull);
0284: if (value == null)
0285: return defaultIsNull ? null : new Float(0);
0286: Object result = value.getValue();
0287:
0288: // If the value isn't of type Float, silently return 0 -- it's a compile error in user code that will
0289: // be caught by javac.
0290: return result instanceof Float ? (Float) result : new Float(0);
0291: }
0292:
0293: public static Double getDouble(AnnotationInstance annotation,
0294: String memberName, boolean defaultIsNull) {
0295: AnnotationValue value = getAnnotationValue(annotation,
0296: memberName, defaultIsNull);
0297: if (value == null)
0298: return defaultIsNull ? null : new Double(0);
0299: Object result = value.getValue();
0300:
0301: // If the value isn't of type Double, silently return 0 -- it's a compile error in user code that will
0302: // be caught by javac.
0303: return result instanceof Double ? (Double) result : new Double(
0304: 0);
0305: }
0306:
0307: public static Boolean getBoolean(AnnotationInstance annotation,
0308: String memberName, boolean defaultIsNull) {
0309: AnnotationValue value = getAnnotationValue(annotation,
0310: memberName, defaultIsNull);
0311: if (value == null)
0312: return defaultIsNull ? null : Boolean.FALSE;
0313: Object result = value.getValue();
0314:
0315: // If the value isn't of type Boolean, silently return false -- it's a compile error in user code that will
0316: // be caught by javac.
0317: return result instanceof Boolean ? (Boolean) result
0318: : Boolean.FALSE;
0319: }
0320:
0321: public static Object getValue(Declaration element,
0322: String annotationName, String memberName,
0323: boolean defaultIsNull) {
0324: AnnotationValue value = assertAnnotationValue(element,
0325: annotationName, memberName, defaultIsNull);
0326: return value != null ? value.getValue() : null;
0327: }
0328:
0329: public static List getAnnotationArrayValue(Declaration element,
0330: String annotationName, String memberName,
0331: boolean defaultIsNull) {
0332: AnnotationValue value = assertAnnotationValue(element,
0333: annotationName, memberName, defaultIsNull);
0334: if (value == null)
0335: return null;
0336: ArrayList ret = new ArrayList();
0337: getValues(value, ret, true);
0338: return ret;
0339: }
0340:
0341: public static List getAnnotationArray(
0342: AnnotationInstance annotation, String memberName,
0343: boolean defaultIsNull) {
0344: AnnotationValue value = getAnnotationValue(annotation,
0345: memberName, defaultIsNull);
0346: return getAnnotationArray(value);
0347: }
0348:
0349: public static List getAnnotationArray(AnnotationValue value) {
0350: if (value == null)
0351: return null;
0352: ArrayList ret = new ArrayList();
0353: getValues(value, ret, true);
0354: return ret;
0355: }
0356:
0357: private static void getValues(AnnotationValue arrayValue,
0358: List translatedValues, boolean weedOutErrorType) {
0359: List values = (List) arrayValue.getValue();
0360: for (Iterator ii = values.iterator(); ii.hasNext();) {
0361: Object i = ii.next();
0362: Object value = i instanceof AnnotationValue ? ((AnnotationValue) i)
0363: .getValue()
0364: : i;
0365: if (!weedOutErrorType || !isErrorString(value))
0366: translatedValues.add(value);
0367: }
0368: }
0369:
0370: public static String getQualifiedName(AnnotationInstance annotation) {
0371: return getDeclaration(annotation.getAnnotationType())
0372: .getQualifiedName();
0373: }
0374:
0375: public static String getSimpleName(AnnotationInstance annotation) {
0376: return getDeclaration(annotation.getAnnotationType())
0377: .getSimpleName();
0378: }
0379:
0380: public static AnnotationInstance getAnnotation(
0381: AnnotationInstance annotation, String memberName,
0382: boolean defaultIsNull) {
0383: AnnotationValue value = getAnnotationValue(annotation,
0384: memberName, defaultIsNull);
0385: return value != null ? (AnnotationInstance) value.getValue()
0386: : null;
0387: }
0388:
0389: public static MethodDeclaration getClassMethod(
0390: TypeDeclaration jclass, String methodName,
0391: String desiredAnnotation) {
0392: return getClassMethod(jclass, methodName, desiredAnnotation,
0393: false);
0394: }
0395:
0396: private static MethodDeclaration getClassMethod(
0397: TypeDeclaration type, String methodName,
0398: String desiredAnnotation, boolean onlyPublicOrProtected) {
0399: if (!(type instanceof ClassDeclaration))
0400: return null;
0401:
0402: ClassDeclaration jclass = (ClassDeclaration) type;
0403: MethodDeclaration[] methods = jclass.getMethods();
0404:
0405: for (int i = 0; i < methods.length; i++) {
0406: MethodDeclaration method = methods[i];
0407: if (!onlyPublicOrProtected
0408: || method.hasModifier(Modifier.PROTECTED)
0409: || method.hasModifier(Modifier.PUBLIC)) {
0410: if (methodName.equals(method.getSimpleName())
0411: && (desiredAnnotation == null || getAnnotation(
0412: method, desiredAnnotation) != null)) {
0413: return method;
0414: }
0415: }
0416: }
0417:
0418: ClassType super class = jclass.getSuperclass();
0419:
0420: if (super class != null) {
0421: return getClassMethod(getDeclaration(super class),
0422: methodName, desiredAnnotation, true);
0423: }
0424:
0425: return null;
0426: }
0427:
0428: public static FieldDeclaration getClassField(
0429: TypeDeclaration jclass, String fieldName,
0430: String desiredAnnotation) {
0431: return getClassField(jclass, fieldName, desiredAnnotation,
0432: false);
0433: }
0434:
0435: private static FieldDeclaration getClassField(TypeDeclaration type,
0436: String fieldName, String desiredAnnotation,
0437: boolean onlyPublicOrProtected) {
0438: if (!(type instanceof ClassDeclaration))
0439: return null;
0440:
0441: ClassDeclaration jclass = (ClassDeclaration) type;
0442: FieldDeclaration[] fields = jclass.getFields();
0443:
0444: for (int i = 0; i < fields.length; i++) {
0445: FieldDeclaration field = fields[i];
0446: if (!onlyPublicOrProtected
0447: || field.hasModifier(Modifier.PROTECTED)
0448: || field.hasModifier(Modifier.PUBLIC)) {
0449: if (fieldName.equals(field.getSimpleName())
0450: && (desiredAnnotation == null || getAnnotation(
0451: field, desiredAnnotation) != null)) {
0452: return field;
0453: }
0454: }
0455: }
0456:
0457: ClassType super class = jclass.getSuperclass();
0458:
0459: if (super class != null) {
0460: return getClassField(getDeclaration(super class), fieldName,
0461: desiredAnnotation, true);
0462: }
0463:
0464: return null;
0465: }
0466:
0467: public static MethodDeclaration[] getClassMethods(
0468: TypeDeclaration jclass, String desiredAnnotation) {
0469: Collection results = new ArrayList();
0470: getClassMethods(jclass, desiredAnnotation, false, results);
0471: return (MethodDeclaration[]) results
0472: .toArray(new MethodDeclaration[results.size()]);
0473: }
0474:
0475: private static void getClassMethods(TypeDeclaration type,
0476: String desiredAnnotation, boolean onlyPublicOrPrivate,
0477: Collection results) {
0478: if (!(type instanceof ClassDeclaration))
0479: return;
0480:
0481: ClassDeclaration jclass = (ClassDeclaration) type;
0482: MethodDeclaration[] methods = jclass.getMethods();
0483:
0484: for (int i = 0; i < methods.length; i++) {
0485: MethodDeclaration method = methods[i];
0486:
0487: if (!onlyPublicOrPrivate
0488: || method.hasModifier(Modifier.PROTECTED)
0489: || method.hasModifier(Modifier.PUBLIC)) {
0490: if (desiredAnnotation == null
0491: || getAnnotation(method, desiredAnnotation) != null) {
0492: boolean isDuplicate = false;
0493:
0494: //
0495: // Make sure we're not adding a duplicate method -- one that was already overridden.
0496: //
0497: if (onlyPublicOrPrivate) {
0498: ParameterDeclaration[] methodParams = method
0499: .getParameters();
0500:
0501: for (Iterator j = results.iterator(); j
0502: .hasNext()
0503: && !isDuplicate;) {
0504: MethodDeclaration existingMethod = (MethodDeclaration) j
0505: .next();
0506:
0507: if (existingMethod.getSimpleName().equals(
0508: method.getSimpleName())) {
0509: ParameterDeclaration[] existingMethodParams = existingMethod
0510: .getParameters();
0511:
0512: if (existingMethodParams.length == methodParams.length) {
0513: isDuplicate = true;
0514:
0515: for (int k = 0; k < existingMethodParams.length; ++k) {
0516: ParameterDeclaration existingMethodParam = existingMethodParams[k];
0517: ParameterDeclaration methodParam = methodParams[k];
0518:
0519: if (!existingMethodParam
0520: .getType()
0521: .equals(
0522: methodParam
0523: .getType())) {
0524: isDuplicate = false;
0525: break;
0526: }
0527: }
0528: }
0529: }
0530: }
0531: }
0532:
0533: if (!isDuplicate)
0534: results.add(method);
0535: }
0536: }
0537: }
0538:
0539: ClassType super class = jclass.getSuperclass();
0540:
0541: if (super class != null
0542: && !getDeclaration(super class).getQualifiedName()
0543: .startsWith("java.lang.")) {
0544: getClassMethods(getDeclaration(super class),
0545: desiredAnnotation, true, results);
0546: }
0547: }
0548:
0549: public static Collection getClassFields(TypeDeclaration jclass) {
0550: Collection results = new ArrayList();
0551: getClassFields(jclass, false, results);
0552: return results;
0553: }
0554:
0555: private static void getClassFields(TypeDeclaration type,
0556: boolean onlyPublicOrPrivate, Collection results) {
0557: if (!(type instanceof ClassDeclaration))
0558: return;
0559:
0560: ClassDeclaration jclass = (ClassDeclaration) type;
0561: FieldDeclaration[] fields = jclass.getFields();
0562:
0563: for (int i = 0; i < fields.length; i++) {
0564: FieldDeclaration field = fields[i];
0565: if (!onlyPublicOrPrivate
0566: || field.hasModifier(Modifier.PROTECTED)
0567: || field.hasModifier(Modifier.PUBLIC)) {
0568: results.add(field);
0569: }
0570: }
0571:
0572: ClassType super class = jclass.getSuperclass();
0573: if (super class != null)
0574: getClassFields(getDeclaration(super class), true, results);
0575: }
0576:
0577: public static Collection getClassNestedTypes(TypeDeclaration jclass) {
0578: Collection results = new ArrayList();
0579: getClassNestedTypes(jclass, false, results);
0580: return results;
0581: }
0582:
0583: private static void getClassNestedTypes(TypeDeclaration type,
0584: boolean onlyPublicOrPrivate, Collection results) {
0585: if (!(type instanceof ClassDeclaration))
0586: return;
0587:
0588: ClassDeclaration jclass = (ClassDeclaration) type;
0589: TypeDeclaration[] nestedTypes = jclass.getNestedTypes();
0590:
0591: for (int i = 0; i < nestedTypes.length; i++) {
0592: TypeDeclaration nestedType = nestedTypes[i];
0593: if (!onlyPublicOrPrivate
0594: || nestedType.hasModifier(Modifier.PROTECTED)
0595: || nestedType.hasModifier(Modifier.PUBLIC)) {
0596: results.add(nestedType);
0597: }
0598: }
0599:
0600: ClassType super class = jclass.getSuperclass();
0601: if (super class != null)
0602: getClassNestedTypes(getDeclaration(super class), true,
0603: results);
0604: }
0605:
0606: /**
0607: * Get a Class.forName-able string for the given type signature.
0608: */
0609: public static String getFormClassName(TypeDeclaration jclass,
0610: CoreAnnotationProcessorEnv env) {
0611: if (isAssignableFrom(STRUTS_FORM_CLASS_NAME, jclass, env)) {
0612: return getLoadableName(jclass);
0613: } else if (isAssignableFrom(BEA_XMLOBJECT_CLASS_NAME, jclass,
0614: env)) {
0615: return XML_FORM_CLASS_NAME;
0616: } else if (isAssignableFrom(APACHE_XMLOBJECT_CLASS_NAME,
0617: jclass, env)) {
0618: return XML_FORM_CLASS_NAME;
0619: } else {
0620: return ANY_FORM_CLASS_NAME;
0621: }
0622: }
0623:
0624: public static String getFormClassName(DeclaredType jclass,
0625: CoreAnnotationProcessorEnv env) {
0626: return getFormClassName(getDeclaration(jclass), env);
0627: }
0628:
0629: public static boolean isAbsoluteURL(String path) {
0630: try {
0631: return new URI(path).getScheme() != null;
0632: } catch (URISyntaxException e) {
0633: return false;
0634: }
0635: }
0636:
0637: public static boolean isAssignableFrom(String className,
0638: TypeInstance type, CoreAnnotationProcessorEnv env) {
0639: if (!(type instanceof DeclaredType))
0640: return false;
0641: return isAssignableFrom(className,
0642: getDeclaration((DeclaredType) type), env);
0643: }
0644:
0645: public static boolean isAssignableFrom(TypeDeclaration base,
0646: TypeDeclaration typeDecl) {
0647: if (base != null && typeDecl != null) {
0648: if (typesAreEqual(typeDecl, base))
0649: return true;
0650:
0651: if (typeDecl instanceof ClassDeclaration) {
0652: ClassType super class = ((ClassDeclaration) typeDecl)
0653: .getSuperclass();
0654: if (super class != null
0655: && isAssignableFrom(base,
0656: getDeclaration(super class)))
0657: return true;
0658: }
0659:
0660: InterfaceType[] super Interfaces = typeDecl
0661: .getSuperinterfaces();
0662: for (int i = 0; i < super Interfaces.length; i++) {
0663: InterfaceType super Interface = super Interfaces[i];
0664: if (isAssignableFrom(base,
0665: getDeclaration(super Interface)))
0666: return true;
0667: }
0668: }
0669:
0670: return false;
0671: }
0672:
0673: public static boolean isAssignableFrom(TypeInstance base,
0674: TypeDeclaration cl) {
0675: if (!(base instanceof DeclaredType))
0676: return false;
0677: return isAssignableFrom(getDeclaration((DeclaredType) base), cl);
0678: }
0679:
0680: public static boolean isAssignableFrom(TypeDeclaration base,
0681: TypeInstance cl) {
0682: if (!(cl instanceof DeclaredType))
0683: return false;
0684: return isAssignableFrom(base, getDeclaration((DeclaredType) cl));
0685: }
0686:
0687: public static boolean isAssignableFrom(String className,
0688: TypeDeclaration cl, CoreAnnotationProcessorEnv env) {
0689: TypeDeclaration base = env.getTypeDeclaration(className);
0690: return isAssignableFrom(base, cl);
0691: }
0692:
0693: public static boolean isOfClass(TypeInstance type,
0694: String className, CoreAnnotationProcessorEnv env) {
0695: if (!(type instanceof DeclaredType))
0696: return false;
0697: return typesAreEqual(getDeclaration((DeclaredType) type), env
0698: .getTypeDeclaration(className));
0699: }
0700:
0701: public static boolean typesAreEqual(TypeDeclaration t1,
0702: TypeDeclaration t2) {
0703: assert t1 != null;
0704: if (t2 == null)
0705: return false;
0706: return t1.getQualifiedName().equals(t2.getQualifiedName());
0707: }
0708:
0709: public static TypeDeclaration getOuterClass(
0710: MemberDeclaration classMember) {
0711: return classMember instanceof ClassDeclaration ? (ClassDeclaration) classMember
0712: : classMember.getDeclaringType();
0713: }
0714:
0715: public static TypeDeclaration getOutermostClass(
0716: MemberDeclaration classMember) {
0717: TypeDeclaration containingClass;
0718: while ((containingClass = classMember.getDeclaringType()) != null) {
0719: classMember = containingClass;
0720: }
0721:
0722: assert classMember instanceof ClassDeclaration : classMember
0723: .getClass().getName();
0724: return (ClassDeclaration) classMember;
0725: }
0726:
0727: public static boolean hasDefaultConstructor(TypeDeclaration jclass) {
0728: if (!(jclass instanceof ClassDeclaration))
0729: return false;
0730:
0731: ConstructorDeclaration[] constructors = ((ClassDeclaration) jclass)
0732: .getConstructors();
0733:
0734: for (int i = 0; i < constructors.length; i++) {
0735: ConstructorDeclaration ctor = constructors[i];
0736: if (ctor.getParameters().length == 0)
0737: return true;
0738: }
0739:
0740: return false;
0741: }
0742:
0743: private static Declaration findElement(Collection elements,
0744: String elementName) {
0745: for (Iterator ii = elements.iterator(); ii.hasNext();) {
0746: Object element = ii.next();
0747: Declaration decl = (Declaration) element;
0748: if (decl.getSimpleName().equals(elementName))
0749: return decl;
0750: }
0751:
0752: return null;
0753: }
0754:
0755: public static FieldDeclaration findField(TypeDeclaration jclass,
0756: String fieldName) {
0757: return (FieldDeclaration) findElement(getClassFields(jclass),
0758: fieldName);
0759: }
0760:
0761: public static ClassDeclaration findInnerClass(
0762: TypeDeclaration jclass, String innerClassName) {
0763: return (ClassDeclaration) findElement(
0764: getClassNestedTypes(jclass), innerClassName);
0765: }
0766:
0767: public static String getEnumFieldName(AnnotationValue enumMember) {
0768: if (enumMember == null || enumMember.getValue() == null)
0769: return "";
0770: else
0771: return enumMember.getValue().toString();
0772: }
0773:
0774: /**
0775: * Get the qualified name of the given class, with '$' used to separate inner classes; the returned string can be
0776: * used with Class.forName().
0777: */
0778: public static String getLoadableName(TypeDeclaration jclass) {
0779: TypeDeclaration containingClass = jclass.getDeclaringType();
0780:
0781: if (containingClass == null) {
0782: return jclass.getQualifiedName();
0783: } else {
0784: return getLoadableName(containingClass) + '$'
0785: + jclass.getSimpleName();
0786: }
0787: }
0788:
0789: public static String getLoadableName(DeclaredType jclass) {
0790: return getLoadableName(getDeclaration(jclass));
0791: }
0792:
0793: public static File getSourceFile(TypeDeclaration decl,
0794: boolean mustBeNonNull) {
0795: decl = getOutermostClass(decl);
0796: SourcePosition position = decl.getPosition();
0797: if (mustBeNonNull)
0798: assert position != null : "no source file for "
0799: + decl.toString();
0800: return position != null ? position.file() : null;
0801: }
0802:
0803: public static class ExtendedCoreAnnotationProcessorEnv implements
0804: CoreAnnotationProcessorEnv {
0805: private CoreAnnotationProcessorEnv _env;
0806: private boolean _useEqualsToCompareAnnotations;
0807: private HashMap _attributes;
0808:
0809: public ExtendedCoreAnnotationProcessorEnv(
0810: CoreAnnotationProcessorEnv env,
0811: boolean useEqualsToCompareAnnotations) {
0812: _env = env;
0813: _useEqualsToCompareAnnotations = useEqualsToCompareAnnotations;
0814: }
0815:
0816: public boolean useEqualsToCompareAnnotations() {
0817: return _useEqualsToCompareAnnotations;
0818: }
0819:
0820: public Map getOptions() {
0821: return _env.getOptions();
0822: }
0823:
0824: public Messager getMessager() {
0825: return _env.getMessager();
0826: }
0827:
0828: public Filer getFiler() {
0829: return _env.getFiler();
0830: }
0831:
0832: public TypeDeclaration[] getSpecifiedTypeDeclarations() {
0833: return _env.getSpecifiedTypeDeclarations();
0834: }
0835:
0836: public TypeDeclaration getTypeDeclaration(String s) {
0837: return _env.getTypeDeclaration(s);
0838: }
0839:
0840: public Declaration[] getDeclarationsAnnotatedWith(
0841: AnnotationTypeDeclaration annotationTypeDeclaration) {
0842: return _env
0843: .getDeclarationsAnnotatedWith(annotationTypeDeclaration);
0844: }
0845:
0846: public void setAttribute(String propertyName, Object value) {
0847: if (_attributes == null)
0848: _attributes = new HashMap();
0849: _attributes.put(propertyName, value);
0850: }
0851:
0852: public Object getAttribute(String propertyName) {
0853: return _attributes != null ? _attributes.get(propertyName)
0854: : null;
0855: }
0856: }
0857:
0858: public static boolean annotationsAreEqual(AnnotationInstance a1,
0859: AnnotationInstance a2, boolean allowExactDuplicates,
0860: CoreAnnotationProcessorEnv env) {
0861: assert a1 != null;
0862: if (a2 == null)
0863: return false;
0864:
0865: //
0866: // TODO: This entire method is a workaround for a bug in APT where an annotation may not equal itelf.
0867: // If this behavior changes, we want to rely on equals(), not this deep comparison, which is more expensive
0868: // and wrong if the two annotations 'look' exactly the same.
0869: //
0870: if (!allowExactDuplicates
0871: && env instanceof ExtendedCoreAnnotationProcessorEnv
0872: && ((ExtendedCoreAnnotationProcessorEnv) env)
0873: .useEqualsToCompareAnnotations()) {
0874: return a1.equals(a2);
0875: }
0876:
0877: Map vals1 = a1.getElementValues();
0878: Map vals2 = a2.getElementValues();
0879:
0880: if (vals1.size() != vals2.size())
0881: return false;
0882:
0883: Iterator ents1 = vals1.entrySet().iterator();
0884: Iterator ents2 = vals2.entrySet().iterator();
0885: while (ents1.hasNext()) {
0886: Map.Entry entry1 = (Map.Entry) ents1.next();
0887: Map.Entry entry2 = (Map.Entry) ents2.next();
0888:
0889: if (!((AnnotationTypeElementDeclaration) entry1.getKey())
0890: .getSimpleName().equals(
0891: ((AnnotationTypeElementDeclaration) entry2
0892: .getKey()).getSimpleName()))
0893: return false;
0894: Object val1 = ((AnnotationValue) entry1.getValue())
0895: .getValue();
0896: Object val2 = ((AnnotationValue) entry2.getValue())
0897: .getValue();
0898:
0899: if (val1 instanceof Collection) {
0900: if (!(val2 instanceof Collection))
0901: return false;
0902: Collection list1 = (Collection) val1;
0903: Collection list2 = (Collection) val2;
0904: if (list1.size() != list2.size())
0905: return false;
0906: Iterator j1 = list1.iterator();
0907: Iterator j2 = list2.iterator();
0908:
0909: while (j1.hasNext()) {
0910: Object o1 = ((AnnotationValue) j1.next())
0911: .getValue();
0912: Object o2 = ((AnnotationValue) j2.next())
0913: .getValue();
0914:
0915: if (o1 instanceof AnnotationInstance) {
0916: if (!(o2 instanceof AnnotationInstance))
0917: return false;
0918: if (!annotationsAreEqual(
0919: (AnnotationInstance) o1,
0920: (AnnotationInstance) o2,
0921: allowExactDuplicates, env))
0922: return false;
0923: } else {
0924: if (!o1.equals(o2))
0925: return false;
0926: }
0927: }
0928: } else if (val1 instanceof AnnotationInstance) {
0929: if (!(val2 instanceof AnnotationInstance))
0930: return false;
0931: if (!annotationsAreEqual((AnnotationInstance) val1,
0932: (AnnotationInstance) val2,
0933: allowExactDuplicates, env))
0934: return false;
0935: } else if (!val1.equals(val2)) {
0936: return false;
0937: }
0938: }
0939:
0940: return true;
0941: }
0942:
0943: public static class BeanPropertyDescriptor {
0944: private String _propertyName;
0945: private String _type;
0946:
0947: public BeanPropertyDescriptor(String propertyName, String type) {
0948: _propertyName = propertyName;
0949: _type = type;
0950: }
0951:
0952: public String getPropertyName() {
0953: return _propertyName;
0954: }
0955:
0956: public String getType() {
0957: return _type;
0958: }
0959: }
0960:
0961: public static class BeanPropertyDeclaration extends
0962: BeanPropertyDescriptor {
0963: private MethodDeclaration _getter;
0964:
0965: public BeanPropertyDeclaration(String propertyName,
0966: String type, MethodDeclaration getter) {
0967: super (propertyName, type);
0968: _getter = getter;
0969: }
0970:
0971: public MethodDeclaration getGetter() {
0972: return _getter;
0973: }
0974: }
0975:
0976: public static BeanPropertyDeclaration getBeanProperty(
0977: MethodDeclaration method) {
0978: if (method.hasModifier(Modifier.PUBLIC)
0979: && !method.hasModifier(Modifier.STATIC)) {
0980: String returnType = method.getReturnType().toString();
0981:
0982: if (!returnType.equals("void")
0983: && method.getParameters().length == 0) {
0984: String methodName = method.getSimpleName();
0985: String propertyName = null;
0986:
0987: if (methodName.startsWith(GETTER_PREFIX)
0988: && methodName.length() > GETTER_PREFIX.length()) {
0989: propertyName = methodName.substring(GETTER_PREFIX
0990: .length());
0991: } else if (methodName.startsWith(BOOLEAN_GETTER_PREFIX)
0992: && returnType.equals("boolean")
0993: && methodName.length() > BOOLEAN_GETTER_PREFIX
0994: .length()) {
0995: propertyName = methodName
0996: .substring(BOOLEAN_GETTER_PREFIX.length());
0997: }
0998:
0999: if (propertyName != null) {
1000: //
1001: // If the first two letters are uppercase, we don't change the first character to lowercase.
1002: // This is so that something like getURI has a property name of 'URI' (see JavaBeans spec).
1003: //
1004: if (propertyName.length() == 1) {
1005: propertyName = propertyName.toLowerCase();
1006: } else if (!Character.isUpperCase(propertyName
1007: .charAt(1))) {
1008: propertyName = Character
1009: .toLowerCase(propertyName.charAt(0))
1010: + propertyName.substring(1);
1011: }
1012:
1013: return new BeanPropertyDeclaration(propertyName,
1014: returnType, method);
1015: }
1016: }
1017: }
1018:
1019: return null;
1020: }
1021:
1022: public static Collection getBeanProperties(ClassDeclaration type,
1023: boolean getInheritedProperties) {
1024: MethodDeclaration[] methods = getInheritedProperties ? getClassMethods(
1025: type, null)
1026: : type.getMethods();
1027: ArrayList ret = new ArrayList();
1028:
1029: for (int i = 0; i < methods.length; i++) {
1030: MethodDeclaration method = methods[i];
1031:
1032: if (method.hasModifier(Modifier.PUBLIC)) {
1033: BeanPropertyDeclaration bpd = getBeanProperty(method);
1034: if (bpd != null)
1035: ret.add(bpd);
1036: }
1037: }
1038:
1039: return ret;
1040: }
1041:
1042: public static boolean isPageFlowClass(ClassDeclaration jclass,
1043: CoreAnnotationProcessorEnv env) {
1044: return getAnnotation(jclass, CONTROLLER_TAG_NAME) != null
1045: && isAssignableFrom(JPF_BASE_CLASS, jclass, env);
1046: }
1047:
1048: public static String removeFileExtension(String uri) {
1049: int lastDot = uri.lastIndexOf('.');
1050: return uri.substring(0, lastDot);
1051: }
1052:
1053: public static TypeDeclaration inferTypeFromPath(
1054: String webappRelativePath, CoreAnnotationProcessorEnv env) {
1055: assert webappRelativePath.startsWith("/") : webappRelativePath;
1056: String className = removeFileExtension(webappRelativePath
1057: .substring(1));
1058: return env.getTypeDeclaration(className.replace('/', '.'));
1059: }
1060:
1061: /**
1062: * Infers the Struts module path from the given controller class.
1063: */
1064: public static String inferModulePathFromType(TypeDeclaration type) {
1065: PackageDeclaration pkg = type.getPackage();
1066: return pkg != null ? '/' + pkg.getQualifiedName().replace('.',
1067: '/') : "/";
1068: }
1069:
1070: public static TypeDeclaration getDeclaration(DeclaredType type) {
1071: TypeDeclaration decl = type != null ? type.getDeclaration()
1072: : null;
1073: return decl != null ? decl : ERROR_TYPE_DECLARATION;
1074: }
1075:
1076: private static class ErrorTypeInstance implements TypeInstance {
1077: public String toString() {
1078: return ERROR_STRING;
1079: }
1080: }
1081:
1082: private static class ErrorTypeDeclaration implements
1083: TypeDeclaration {
1084: private static final InterfaceType[] SUPERINTERFACES = new InterfaceType[0];
1085: private static final FieldDeclaration[] FIELDS = new FieldDeclaration[0];
1086: private static final MethodDeclaration[] METHODS = new MethodDeclaration[0];
1087: private static final TypeDeclaration[] NESTED_TYPES = new TypeDeclaration[0];
1088: private static final AnnotationInstance[] ANNOTATIONS = new AnnotationInstance[0];
1089:
1090: public PackageDeclaration getPackage() {
1091: throw new IllegalStateException("not implemented ");
1092: }
1093:
1094: public String getQualifiedName() {
1095: return ERROR_STRING;
1096: }
1097:
1098: /*
1099: public Collection getFormalTypeParameters()
1100: {
1101: return Collections.EMPTY_LIST;
1102: }
1103: */
1104:
1105: public InterfaceType[] getSuperinterfaces() {
1106: return SUPERINTERFACES;
1107: }
1108:
1109: public FieldDeclaration[] getFields() {
1110: return FIELDS;
1111: }
1112:
1113: public MethodDeclaration[] getMethods() {
1114: return METHODS;
1115: }
1116:
1117: public TypeDeclaration[] getNestedTypes() {
1118: return NESTED_TYPES;
1119: }
1120:
1121: public TypeDeclaration getDeclaringType() {
1122: return null;
1123: }
1124:
1125: public String getDocComment() {
1126: throw new IllegalStateException("not implemented ");
1127: }
1128:
1129: public AnnotationInstance[] getAnnotationInstances() {
1130: return ANNOTATIONS;
1131: }
1132:
1133: /*
1134: public Annotation getAnnotation( Class s )
1135: {
1136: throw new IllegalStateException( "not implemented " );
1137: }
1138: */
1139:
1140: public Set getModifiers() {
1141: return Collections.EMPTY_SET;
1142: }
1143:
1144: public String getSimpleName() {
1145: return getQualifiedName();
1146: }
1147:
1148: public SourcePosition getPosition() {
1149: throw new IllegalStateException("not implemented ");
1150: }
1151:
1152: public boolean hasModifier(Modifier modifier) {
1153: return false;
1154: }
1155:
1156: /*
1157: public void accept( DeclarationVisitor declarationVisitor )
1158: {
1159: throw new IllegalStateException( "not implemented " );
1160: }
1161: */
1162: }
1163:
1164: /**
1165: * This is the same logic that we have in the runtime, in PageFlowRequestProcessor. Can't share the code, though.
1166: */
1167: public static boolean isAbsoluteURI(String uri) {
1168: //
1169: // This method needs to be fast, so it can't use java.net.URI.
1170: //
1171: if (uri.length() == 0 || uri.charAt(0) == '/')
1172: return false;
1173:
1174: for (int i = 0, len = uri.length(); i < len; ++i) {
1175: char c = uri.charAt(i);
1176:
1177: if (c == ':') {
1178: return true;
1179: } else if (c == '/') {
1180: return false;
1181: }
1182: }
1183:
1184: return false;
1185: }
1186:
1187: public static TypeInstance getArrayBaseType(ArrayType arrayType) {
1188: TypeInstance baseType = arrayType;
1189:
1190: do {
1191: baseType = ((ArrayType) baseType).getComponentType();
1192: } while (baseType instanceof ArrayType);
1193:
1194: return baseType;
1195: }
1196:
1197: public static final class Mutable {
1198: private Object _val = null;
1199:
1200: public Mutable() {
1201: }
1202:
1203: public Mutable(Object val) {
1204: _val = val;
1205: }
1206:
1207: public void set(Object val) {
1208: _val = val;
1209: }
1210:
1211: public Object get() {
1212: return _val;
1213: }
1214: }
1215:
1216: public static TypeInstance getGenericBoundsType(TypeInstance type) {
1217: if (type instanceof TypeVariable) {
1218: ReferenceType[] bounds = ((TypeVariable) type)
1219: .getDeclaration().getBounds();
1220: return bounds.length > 0 ? bounds[0] : type;
1221: }
1222:
1223: return type;
1224: }
1225:
1226: public static File getFileRelativeToSourceFile(
1227: TypeDeclaration outerClass, String relativePath,
1228: CoreAnnotationProcessorEnv env)
1229: throws FatalCompileTimeException {
1230: assert relativePath.length() > 0;
1231:
1232: //
1233: // If it starts with '/', just find the webapp-relative file.
1234: //
1235: if (relativePath.charAt(0) == '/')
1236: return getWebappRelativeFile(relativePath, true, env);
1237:
1238: //
1239: // Look for the file relative to the source directory of the given class.
1240: //
1241: File sourceFile = getSourceFile(outerClass, false);
1242: File desiredParentDir = sourceFile.getAbsoluteFile()
1243: .getParentFile();
1244: File retVal = new File(desiredParentDir, relativePath);
1245:
1246: //
1247: // If we still haven't found it, construct a webapp-relative path and look for the file relative to the webapp.
1248: //
1249: if (!retVal.exists()) {
1250: PackageDeclaration jpfPackage = outerClass.getPackage();
1251: return getWebappRelativeFile(getPathRelativeToPackage(
1252: relativePath, jpfPackage), true, env);
1253: }
1254:
1255: return retVal;
1256: }
1257:
1258: public static String getPathRelativeToPackage(String relativePath,
1259: PackageDeclaration packageDecl) {
1260: if (packageDecl != null) {
1261: String packageName = packageDecl.getQualifiedName();
1262: if (packageName.length() > 0)
1263: return '/' + packageName.replace('.', '/') + '/'
1264: + relativePath;
1265: }
1266:
1267: return '/' + relativePath;
1268: }
1269:
1270: public static File getWebappRelativeFile(String webappRelativePath,
1271: boolean lookInSourceRoots, CoreAnnotationProcessorEnv env)
1272: throws FatalCompileTimeException {
1273: //
1274: // Look for the file out in the web-addressable portion of the webapp.
1275: //
1276: assert webappRelativePath.startsWith("/") : webappRelativePath;
1277: String[] webContentRoots = getWebContentRoots(env);
1278:
1279: for (int i = 0; i < webContentRoots.length; i++) {
1280: String webContentRoot = webContentRoots[i];
1281: File retVal = new File(webContentRoot + webappRelativePath);
1282: if (retVal.exists())
1283: return retVal;
1284: }
1285:
1286: //
1287: // If appropriate, look for the file under all the source roots.
1288: //
1289: if (lookInSourceRoots) {
1290: String[] webSourceRoots = getWebSourceRoots(env);
1291:
1292: for (int i = 0; i < webSourceRoots.length; i++) {
1293: String webSourceRoot = webSourceRoots[i];
1294: File webSourceRelativeFile = new File(webSourceRoot
1295: + webappRelativePath);
1296: if (webSourceRelativeFile.exists())
1297: return webSourceRelativeFile;
1298: }
1299: }
1300:
1301: return null;
1302: }
1303:
1304: public static String[] getWebSourceRoots(
1305: CoreAnnotationProcessorEnv env)
1306: throws FatalCompileTimeException {
1307: String[] legacyOption = getOption("-Aweb.source.roots", false,
1308: env); // deprecated - TODO: deprecation message
1309: if (legacyOption != null)
1310: return legacyOption;
1311: return getOption("-sourcepath", true, env);
1312: }
1313:
1314: public static String[] getWebContentRoots(
1315: CoreAnnotationProcessorEnv env)
1316: throws FatalCompileTimeException {
1317: return getOption("-Aweb.content.root", true, env);
1318: }
1319:
1320: /**
1321: * Utility method to get the value of the "phase" option from the annotation processing environment.
1322: * @param env the annotation processing environment
1323: * @return the value of the "phase" option, or <code>null</code> if the option is unspecified
1324: * @throws FatalCompileTimeException if an error occurs getting the value of the option
1325: */
1326: public static String isReconcilePhase(CoreAnnotationProcessorEnv env)
1327: throws FatalCompileTimeException {
1328: String[] phase = getOption(
1329: "-A"
1330: + JpfLanguageConstants.ANNOTATION_PROCESSOR_OPTION_PHASE,
1331: false, env);
1332: return phase != null && phase.length > 0 ? phase[0] : null;
1333: }
1334:
1335: private static String[] getOption(String optionName,
1336: boolean required, CoreAnnotationProcessorEnv env)
1337: throws MissingOptionException {
1338: String[] cached = (String[]) env.getAttribute(optionName);
1339: if (cached != null)
1340: return cached;
1341:
1342: Map options = env.getOptions();
1343: String value = (String) options.get(optionName);
1344:
1345: if (value == null) {
1346: // TODO: there appears to be a bug in APT where both the key/value are contained in the key
1347: String aptOption = optionName + '=';
1348: for (Iterator i = options.keySet().iterator(); i.hasNext();) {
1349: String key = (String) i.next();
1350:
1351: if (key.startsWith(aptOption)) {
1352: value = key.substring(aptOption.length());
1353: break;
1354: }
1355: }
1356: }
1357:
1358: if (value == null) {
1359: if (required)
1360: throw new MissingOptionException(optionName);
1361: return null;
1362: }
1363:
1364: String[] values = value.trim().split(File.pathSeparator);
1365: for (int i = 0; i < values.length; i++) {
1366: values[i] = values[i].trim();
1367: }
1368:
1369: env.setAttribute(optionName, values);
1370: return values;
1371: }
1372: }
|