JdbcMapper/beehive-netui-compiler/src/main/java/org/apache/beehive/netui/compiler/CompilerUtils.java

1332 lines
49 KiB
Java

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Header:$
*/
package org.apache.beehive.netui.compiler;
import org.apache.beehive.netui.compiler.typesystem.declaration.*;
import org.apache.beehive.netui.compiler.typesystem.env.CoreAnnotationProcessorEnv;
import org.apache.beehive.netui.compiler.typesystem.env.Filer;
import org.apache.beehive.netui.compiler.typesystem.env.Messager;
import org.apache.beehive.netui.compiler.typesystem.type.ArrayType;
import org.apache.beehive.netui.compiler.typesystem.type.ClassType;
import org.apache.beehive.netui.compiler.typesystem.type.DeclaredType;
import org.apache.beehive.netui.compiler.typesystem.type.InterfaceType;
import org.apache.beehive.netui.compiler.typesystem.type.ReferenceType;
import org.apache.beehive.netui.compiler.typesystem.type.TypeInstance;
import org.apache.beehive.netui.compiler.typesystem.type.TypeVariable;
import org.apache.beehive.netui.compiler.typesystem.util.SourcePosition;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
public class CompilerUtils
implements JpfLanguageConstants
{
private static final String ERROR_STRING = "<error>";
private static final TypeDeclaration ERROR_TYPE_DECLARATION = new ErrorTypeDeclaration();
public static boolean isJpfAnnotation( AnnotationInstance annotation, String unqualifiedName )
{
String annotationName = getDeclaration( annotation.getAnnotationType() ).getQualifiedName();
return annotationName.equals( ANNOTATION_QUALIFIER + unqualifiedName );
}
public static AnnotationInstance getAnnotation( ClassDeclaration decl, String unqualifiedName, boolean inherited )
{
if ( ! inherited ) return getAnnotation( decl, unqualifiedName );
do
{
AnnotationInstance ann = getAnnotation( decl, unqualifiedName );
if ( ann != null ) return ann;
ClassType superType = decl.getSuperclass();
TypeDeclaration superTypeDecl = getDeclaration( superType );
decl = superTypeDecl instanceof ClassDeclaration ? ( ClassDeclaration ) superTypeDecl : null;
} while ( decl != null );
return null;
}
public static AnnotationInstance getAnnotation( Declaration element, String unqualifiedName )
{
return getAnnotationFullyQualified( element, ANNOTATION_QUALIFIER + unqualifiedName );
}
public static AnnotationInstance getAnnotationFullyQualified( Declaration element, String fullyQualifiedName )
{
AnnotationInstance[] annotations = element.getAnnotationInstances();
for ( int ii = 0; ii < annotations.length; ii++ )
{
AnnotationInstance i = annotations[ii];
String iName = getDeclaration( i.getAnnotationType() ).getQualifiedName();
if ( fullyQualifiedName.equals( iName ) ) return i;
}
return null;
}
public static AnnotationValue getAnnotationValue( Declaration element, String annotationName, String valueName )
{
AnnotationInstance ann = getAnnotation( element, annotationName );
return ann != null ? getAnnotationValue( ann, valueName, true ) : null;
}
/**
* If the given annotation exists, assert that the given member is not null</code>, and return it; otherwise,
* if the given annotation does not exist, return null</code>.
*/
private static AnnotationValue assertAnnotationValue( Declaration element, String annotationName, String valueName,
boolean defaultIsNull )
{
AnnotationInstance ann = getAnnotation( element, annotationName );
if ( ann == null )
{
return null;
}
else
{
return getAnnotationValue( ann, valueName, defaultIsNull );
}
}
public static String getStringValue( Declaration element, String annotationName, String memberName,
boolean defaultIsNull )
{
return ( String ) getValue( element, annotationName, memberName, defaultIsNull );
}
public static AnnotationValue getAnnotationValue( AnnotationInstance annotation, String memberName,
boolean defaultIsNull )
{
Map valuesPresent = annotation.getElementValues();
for ( Iterator ii = valuesPresent.entrySet().iterator(); ii.hasNext(); )
{
Map.Entry i = ( Map.Entry ) ii.next();
if ( memberName.equals( ( ( AnnotationTypeElementDeclaration ) i.getKey() ).getSimpleName() ) )
{
return ( AnnotationValue ) i.getValue();
}
}
//
// We didn't find it. If necessary, look for the default value.
//
if ( defaultIsNull ) return null;
AnnotationTypeDeclaration typeDecl = annotation.getAnnotationType().getAnnotationTypeDeclaration();
if ( typeDecl == null ) return null; // type declaration is null in the case of error type
AnnotationTypeElementDeclaration[] members = typeDecl.getAnnotationMembers();
for ( int i = 0; i < members.length; i++ )
{
AnnotationTypeElementDeclaration member = members[i];
if ( memberName.equals( member.getSimpleName() ) ) return member.getDefaultValue();
}
return null;
}
public static List getStringArrayValue( Declaration element, String annotationName, String memberName,
boolean defaultIsNull )
{
AnnotationValue value = assertAnnotationValue( element, annotationName, memberName, defaultIsNull );
if ( value == null ) return null;
ArrayList ret = new ArrayList();
getValues( value, ret, false );
return ret;
}
public static Boolean getBooleanValue( Declaration element, String annotationName, String memberName,
boolean defaultIsNull )
{
AnnotationValue value = assertAnnotationValue( element, annotationName, memberName, defaultIsNull );
return value != null ? ( Boolean ) value.getValue() : null;
}
public static String getString( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
return value != null ? ( String ) value.getValue() : ( defaultIsNull ? null : "" );
}
public static TypeInstance getTypeInstance( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
if ( value == null ) return null;
Object typeInstance = value.getValue();
if ( isErrorString( typeInstance ) ) return new ErrorTypeInstance();
assert typeInstance instanceof TypeInstance
: "expected TypeInstance, got " + typeInstance + "(value=" + typeInstance + ")";
return ( TypeInstance ) typeInstance;
}
public static String getEnumFieldName( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
return value != null ? getEnumFieldName( value ) : null;
}
public static List getStringArray( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
if ( value == null ) return null;
ArrayList ret = new ArrayList();
getValues( value, ret, false );
return ret;
}
public static DeclaredType getDeclaredType( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
return ( DeclaredType ) getReferenceType( annotation, memberName, defaultIsNull );
}
public static ReferenceType getReferenceType( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
// If the type is the string "<error>", it means that there is already an error related to the type itself.
if ( value != null && isErrorString( value.getValue() ) ) return null;
return value != null ? ( ReferenceType ) value.getValue() : null;
}
public static Integer getInteger( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
if ( value == null ) return defaultIsNull ? null : new Integer( 0 );
Object result = value.getValue();
// If the value isn't of type Integer, silently return 0 -- it's a compile error in user code that will
// be caught by javac.
return result instanceof Integer ? (Integer) result : new Integer(0);
}
public static boolean isErrorString( Object str )
{
return ERROR_STRING.equals( str );
}
public static Long getLong( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
if ( value == null ) return defaultIsNull ? null : new Long( 0 );
Object result = value.getValue();
// If the value isn't of type Long, silently return 0 -- it's a compile error in user code that will
// be caught by javac.
return result instanceof Long ? (Long) result : new Long(0);
}
public static Float getFloat( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
if ( value == null ) return defaultIsNull ? null : new Float( 0 );
Object result = value.getValue();
// If the value isn't of type Float, silently return 0 -- it's a compile error in user code that will
// be caught by javac.
return result instanceof Float ? (Float) result : new Float(0);
}
public static Double getDouble( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
if ( value == null ) return defaultIsNull ? null : new Double( 0 );
Object result = value.getValue();
// If the value isn't of type Double, silently return 0 -- it's a compile error in user code that will
// be caught by javac.
return result instanceof Double ? (Double) result : new Double(0);
}
public static Boolean getBoolean( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
if ( value == null ) return defaultIsNull ? null : Boolean.FALSE;
Object result = value.getValue();
// If the value isn't of type Boolean, silently return false -- it's a compile error in user code that will
// be caught by javac.
return result instanceof Boolean ? (Boolean) result : Boolean.FALSE;
}
public static Object getValue( Declaration element, String annotationName, String memberName, boolean defaultIsNull )
{
AnnotationValue value = assertAnnotationValue( element, annotationName, memberName, defaultIsNull );
return value != null ? value.getValue() : null;
}
public static List getAnnotationArrayValue( Declaration element, String annotationName,
String memberName, boolean defaultIsNull )
{
AnnotationValue value = assertAnnotationValue( element, annotationName, memberName, defaultIsNull );
if ( value == null ) return null;
ArrayList ret = new ArrayList();
getValues( value, ret, true );
return ret;
}
public static List getAnnotationArray( AnnotationInstance annotation, String memberName,
boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
return getAnnotationArray( value );
}
public static List getAnnotationArray( AnnotationValue value )
{
if ( value == null ) return null;
ArrayList ret = new ArrayList();
getValues( value, ret, true );
return ret;
}
private static void getValues( AnnotationValue arrayValue, List translatedValues, boolean weedOutErrorType )
{
List values = ( List ) arrayValue.getValue();
for ( Iterator ii = values.iterator(); ii.hasNext(); )
{
Object i = ii.next();
Object value = i instanceof AnnotationValue ? ( ( AnnotationValue ) i ).getValue() : i;
if ( ! weedOutErrorType || ! isErrorString( value ) ) translatedValues.add( value );
}
}
public static String getQualifiedName( AnnotationInstance annotation )
{
return getDeclaration( annotation.getAnnotationType() ).getQualifiedName();
}
public static String getSimpleName( AnnotationInstance annotation )
{
return getDeclaration( annotation.getAnnotationType() ).getSimpleName();
}
public static AnnotationInstance getAnnotation( AnnotationInstance annotation, String memberName, boolean defaultIsNull )
{
AnnotationValue value = getAnnotationValue( annotation, memberName, defaultIsNull );
return value != null ? ( AnnotationInstance ) value.getValue() : null;
}
public static MethodDeclaration getClassMethod( TypeDeclaration jclass, String methodName, String desiredAnnotation )
{
return getClassMethod( jclass, methodName, desiredAnnotation, false );
}
private static MethodDeclaration getClassMethod( TypeDeclaration type, String methodName, String desiredAnnotation,
boolean onlyPublicOrProtected )
{
if ( ! ( type instanceof ClassDeclaration ) ) return null;
ClassDeclaration jclass = ( ClassDeclaration ) type;
MethodDeclaration[] methods = jclass.getMethods();
for ( int i = 0; i < methods.length; i++ )
{
MethodDeclaration method = methods[i];
if ( ! onlyPublicOrProtected || method.hasModifier( Modifier.PROTECTED )
|| method.hasModifier( Modifier.PUBLIC ) )
{
if ( methodName.equals( method.getSimpleName() )
&& ( desiredAnnotation == null || getAnnotation( method, desiredAnnotation ) != null ) )
{
return method;
}
}
}
ClassType superclass = jclass.getSuperclass();
if ( superclass != null )
{
return getClassMethod( getDeclaration( superclass ), methodName, desiredAnnotation, true );
}
return null;
}
public static FieldDeclaration getClassField( TypeDeclaration jclass, String fieldName, String desiredAnnotation )
{
return getClassField( jclass, fieldName, desiredAnnotation, false );
}
private static FieldDeclaration getClassField( TypeDeclaration type, String fieldName, String desiredAnnotation,
boolean onlyPublicOrProtected )
{
if ( ! ( type instanceof ClassDeclaration ) ) return null;
ClassDeclaration jclass = ( ClassDeclaration ) type;
FieldDeclaration[] fields = jclass.getFields();
for ( int i = 0; i < fields.length; i++ )
{
FieldDeclaration field = fields[i];
if ( ! onlyPublicOrProtected || field.hasModifier( Modifier.PROTECTED )
|| field.hasModifier( Modifier.PUBLIC ) )
{
if ( fieldName.equals( field.getSimpleName() )
&& ( desiredAnnotation == null || getAnnotation( field, desiredAnnotation ) != null ) )
{
return field;
}
}
}
ClassType superclass = jclass.getSuperclass();
if ( superclass != null )
{
return getClassField( getDeclaration( superclass ), fieldName, desiredAnnotation, true );
}
return null;
}
public static MethodDeclaration[] getClassMethods( TypeDeclaration jclass, String desiredAnnotation )
{
Collection results = new ArrayList();
getClassMethods( jclass, desiredAnnotation, false, results );
return ( MethodDeclaration[] ) results.toArray( new MethodDeclaration[ results.size() ] );
}
private static void getClassMethods( TypeDeclaration type, String desiredAnnotation, boolean onlyPublicOrPrivate,
Collection results )
{
if ( ! ( type instanceof ClassDeclaration ) ) return;
ClassDeclaration jclass = ( ClassDeclaration ) type;
MethodDeclaration[] methods = jclass.getMethods();
for ( int i = 0; i < methods.length; i++ )
{
MethodDeclaration method = methods[i];
if ( ! onlyPublicOrPrivate || method.hasModifier( Modifier.PROTECTED )
|| method.hasModifier( Modifier.PUBLIC ) )
{
if ( desiredAnnotation == null || getAnnotation( method, desiredAnnotation ) != null )
{
boolean isDuplicate = false;
//
// Make sure we're not adding a duplicate method -- one that was already overridden.
//
if ( onlyPublicOrPrivate )
{
ParameterDeclaration[] methodParams = method.getParameters();
for ( Iterator j = results.iterator(); j.hasNext() && !isDuplicate; )
{
MethodDeclaration existingMethod = ( MethodDeclaration ) j.next();
if ( existingMethod.getSimpleName().equals( method.getSimpleName() ) )
{
ParameterDeclaration[] existingMethodParams = existingMethod.getParameters();
if ( existingMethodParams.length == methodParams.length )
{
isDuplicate = true;
for ( int k = 0; k < existingMethodParams.length; ++k )
{
ParameterDeclaration existingMethodParam = existingMethodParams[ k ];
ParameterDeclaration methodParam = methodParams[ k ];
if ( ! existingMethodParam.getType().equals( methodParam.getType() ) )
{
isDuplicate = false;
break;
}
}
}
}
}
}
if ( ! isDuplicate ) results.add( method );
}
}
}
ClassType superclass = jclass.getSuperclass();
if ( superclass != null && ! getDeclaration( superclass ).getQualifiedName().startsWith( "java.lang." ) )
{
getClassMethods( getDeclaration( superclass ), desiredAnnotation, true, results );
}
}
public static Collection getClassFields( TypeDeclaration jclass )
{
Collection results = new ArrayList();
getClassFields( jclass, false, results );
return results;
}
private static void getClassFields( TypeDeclaration type, boolean onlyPublicOrPrivate,
Collection results )
{
if ( ! ( type instanceof ClassDeclaration ) ) return;
ClassDeclaration jclass = ( ClassDeclaration ) type;
FieldDeclaration[] fields = jclass.getFields();
for ( int i = 0; i < fields.length; i++ )
{
FieldDeclaration field = fields[i];
if ( ! onlyPublicOrPrivate || field.hasModifier( Modifier.PROTECTED ) || field.hasModifier( Modifier.PUBLIC ) )
{
results.add( field );
}
}
ClassType superclass = jclass.getSuperclass();
if ( superclass != null ) getClassFields( getDeclaration( superclass ), true, results );
}
public static Collection getClassNestedTypes( TypeDeclaration jclass )
{
Collection results = new ArrayList();
getClassNestedTypes( jclass, false, results );
return results;
}
private static void getClassNestedTypes( TypeDeclaration type, boolean onlyPublicOrPrivate,
Collection results )
{
if ( ! ( type instanceof ClassDeclaration ) ) return;
ClassDeclaration jclass = ( ClassDeclaration ) type;
TypeDeclaration[] nestedTypes = jclass.getNestedTypes();
for ( int i = 0; i < nestedTypes.length; i++ )
{
TypeDeclaration nestedType = nestedTypes[i];
if ( ! onlyPublicOrPrivate || nestedType.hasModifier( Modifier.PROTECTED )
|| nestedType.hasModifier( Modifier.PUBLIC ) )
{
results.add( nestedType );
}
}
ClassType superclass = jclass.getSuperclass();
if ( superclass != null ) getClassNestedTypes( getDeclaration( superclass ), true, results );
}
/**
* Get a Class.forName-able string for the given type signature.
*/
public static String getFormClassName( TypeDeclaration jclass, CoreAnnotationProcessorEnv env )
{
if ( isAssignableFrom( STRUTS_FORM_CLASS_NAME, jclass, env ) )
{
return getLoadableName( jclass );
}
else if ( isAssignableFrom( BEA_XMLOBJECT_CLASS_NAME, jclass, env ) )
{
return XML_FORM_CLASS_NAME;
}
else if ( isAssignableFrom( APACHE_XMLOBJECT_CLASS_NAME, jclass, env ) )
{
return XML_FORM_CLASS_NAME;
}
else
{
return ANY_FORM_CLASS_NAME;
}
}
public static String getFormClassName( DeclaredType jclass, CoreAnnotationProcessorEnv env )
{
return getFormClassName( getDeclaration( jclass ), env );
}
public static boolean isAbsoluteURL( String path )
{
try
{
return new URI( path ).getScheme() != null;
}
catch ( URISyntaxException e )
{
return false;
}
}
public static boolean isAssignableFrom( String className, TypeInstance type, CoreAnnotationProcessorEnv env )
{
if ( ! ( type instanceof DeclaredType ) ) return false;
return isAssignableFrom( className, getDeclaration( ( DeclaredType ) type ), env );
}
public static boolean isAssignableFrom( TypeDeclaration base, TypeDeclaration typeDecl )
{
if ( base != null && typeDecl != null )
{
if ( typesAreEqual( typeDecl, base ) ) return true;
if ( typeDecl instanceof ClassDeclaration )
{
ClassType superclass = ( ( ClassDeclaration ) typeDecl ).getSuperclass();
if ( superclass != null && isAssignableFrom( base, getDeclaration( superclass ) ) ) return true;
}
InterfaceType[] superInterfaces = typeDecl.getSuperinterfaces();
for ( int i = 0; i < superInterfaces.length; i++ )
{
InterfaceType superInterface = superInterfaces[i];
if ( isAssignableFrom( base, getDeclaration( superInterface ) ) ) return true;
}
}
return false;
}
public static boolean isAssignableFrom( TypeInstance base, TypeDeclaration cl )
{
if ( ! ( base instanceof DeclaredType ) ) return false;
return isAssignableFrom( getDeclaration( ( DeclaredType ) base ), cl );
}
public static boolean isAssignableFrom( TypeDeclaration base, TypeInstance cl )
{
if ( ! ( cl instanceof DeclaredType ) ) return false;
return isAssignableFrom( base, getDeclaration( ( DeclaredType ) cl ) );
}
public static boolean isAssignableFrom( String className, TypeDeclaration cl, CoreAnnotationProcessorEnv env )
{
TypeDeclaration base = env.getTypeDeclaration( className );
return isAssignableFrom( base, cl );
}
public static boolean isOfClass( TypeInstance type, String className, CoreAnnotationProcessorEnv env )
{
if ( ! ( type instanceof DeclaredType ) ) return false;
return typesAreEqual( getDeclaration( ( DeclaredType ) type ), env.getTypeDeclaration( className ) );
}
public static boolean typesAreEqual( TypeDeclaration t1, TypeDeclaration t2 )
{
assert t1 != null;
if ( t2 == null ) return false;
return t1.getQualifiedName().equals( t2.getQualifiedName() );
}
public static TypeDeclaration getOuterClass( MemberDeclaration classMember )
{
return classMember instanceof ClassDeclaration
? ( ClassDeclaration ) classMember
: classMember.getDeclaringType();
}
public static TypeDeclaration getOutermostClass( MemberDeclaration classMember )
{
TypeDeclaration containingClass;
while ( ( containingClass = classMember.getDeclaringType() ) != null )
{
classMember = containingClass;
}
assert classMember instanceof ClassDeclaration : classMember.getClass().getName();
return ( ClassDeclaration ) classMember;
}
public static boolean hasDefaultConstructor( TypeDeclaration jclass )
{
if ( ! ( jclass instanceof ClassDeclaration ) ) return false;
ConstructorDeclaration[] constructors = ( ( ClassDeclaration ) jclass ).getConstructors();
for ( int i = 0; i < constructors.length; i++ )
{
ConstructorDeclaration ctor = constructors[i];
if ( ctor.getParameters().length == 0 ) return true;
}
return false;
}
private static Declaration findElement( Collection elements, String elementName )
{
for ( Iterator ii = elements.iterator(); ii.hasNext(); )
{
Object element = ii.next();
Declaration decl = ( Declaration ) element;
if ( decl.getSimpleName().equals( elementName ) ) return decl;
}
return null;
}
public static FieldDeclaration findField( TypeDeclaration jclass, String fieldName )
{
return ( FieldDeclaration ) findElement( getClassFields( jclass ), fieldName );
}
public static ClassDeclaration findInnerClass( TypeDeclaration jclass, String innerClassName )
{
return ( ClassDeclaration ) findElement( getClassNestedTypes( jclass ), innerClassName );
}
public static String getEnumFieldName( AnnotationValue enumMember )
{
if ( enumMember == null || enumMember.getValue() == null )
return "";
else
return enumMember.getValue().toString();
}
/**
* Get the qualified name of the given class, with '$' used to separate inner classes; the returned string can be
* used with Class.forName().
*/
public static String getLoadableName( TypeDeclaration jclass )
{
TypeDeclaration containingClass = jclass.getDeclaringType();
if ( containingClass == null )
{
return jclass.getQualifiedName();
}
else
{
return getLoadableName( containingClass ) + '$' + jclass.getSimpleName();
}
}
public static String getLoadableName( DeclaredType jclass )
{
return getLoadableName( getDeclaration( jclass ) );
}
public static File getSourceFile( TypeDeclaration decl, boolean mustBeNonNull )
{
decl = getOutermostClass( decl );
SourcePosition position = decl.getPosition();
if ( mustBeNonNull ) assert position != null : "no source file for " + decl.toString();
return position != null ? position.file() : null;
}
public static class ExtendedCoreAnnotationProcessorEnv
implements CoreAnnotationProcessorEnv
{
private CoreAnnotationProcessorEnv _env;
private boolean _useEqualsToCompareAnnotations;
private HashMap _attributes;
public ExtendedCoreAnnotationProcessorEnv( CoreAnnotationProcessorEnv env,
boolean useEqualsToCompareAnnotations )
{
_env = env;
_useEqualsToCompareAnnotations = useEqualsToCompareAnnotations;
}
public boolean useEqualsToCompareAnnotations()
{
return _useEqualsToCompareAnnotations;
}
public Map getOptions()
{
return _env.getOptions();
}
public Messager getMessager()
{
return _env.getMessager();
}
public Filer getFiler()
{
return _env.getFiler();
}
public TypeDeclaration[] getSpecifiedTypeDeclarations()
{
return _env.getSpecifiedTypeDeclarations();
}
public TypeDeclaration getTypeDeclaration( String s )
{
return _env.getTypeDeclaration( s );
}
public Declaration[] getDeclarationsAnnotatedWith( AnnotationTypeDeclaration annotationTypeDeclaration )
{
return _env.getDeclarationsAnnotatedWith( annotationTypeDeclaration );
}
public void setAttribute( String propertyName, Object value )
{
if ( _attributes == null ) _attributes = new HashMap();
_attributes.put( propertyName, value );
}
public Object getAttribute( String propertyName )
{
return _attributes != null ? _attributes.get( propertyName ) : null;
}
}
public static boolean annotationsAreEqual( AnnotationInstance a1, AnnotationInstance a2, boolean allowExactDuplicates,
CoreAnnotationProcessorEnv env )
{
assert a1 != null;
if ( a2 == null ) return false;
//
// TODO: This entire method is a workaround for a bug in APT where an annotation may not equal itelf.
// If this behavior changes, we want to rely on equals(), not this deep comparison, which is more expensive
// and wrong if the two annotations 'look' exactly the same.
//
if ( ! allowExactDuplicates
&& env instanceof ExtendedCoreAnnotationProcessorEnv
&& ( ( ExtendedCoreAnnotationProcessorEnv ) env ).useEqualsToCompareAnnotations() )
{
return a1.equals( a2 );
}
Map vals1 = a1.getElementValues();
Map vals2 = a2.getElementValues();
if ( vals1.size() != vals2.size() ) return false;
Iterator ents1 = vals1.entrySet().iterator();
Iterator ents2 = vals2.entrySet().iterator();
while ( ents1.hasNext() )
{
Map.Entry entry1 = ( Map.Entry ) ents1.next();
Map.Entry entry2 = ( Map.Entry ) ents2.next();
if ( ! ( ( AnnotationTypeElementDeclaration ) entry1.getKey() ).getSimpleName().equals( ( ( AnnotationTypeElementDeclaration ) entry2.getKey() ).getSimpleName() ) ) return false;
Object val1 = ( ( AnnotationValue ) entry1.getValue() ).getValue();
Object val2 = ( ( AnnotationValue ) entry2.getValue() ).getValue();
if ( val1 instanceof Collection )
{
if ( ! ( val2 instanceof Collection ) ) return false;
Collection list1 = ( Collection ) val1;
Collection list2 = ( Collection ) val2;
if ( list1.size() != list2.size() ) return false;
Iterator j1 = list1.iterator();
Iterator j2 = list2.iterator();
while ( j1.hasNext() )
{
Object o1 = ( ( AnnotationValue ) j1.next() ).getValue();
Object o2 = ( ( AnnotationValue ) j2.next() ).getValue();
if ( o1 instanceof AnnotationInstance )
{
if ( ! ( o2 instanceof AnnotationInstance ) ) return false;
if ( ! annotationsAreEqual( ( AnnotationInstance ) o1, ( AnnotationInstance ) o2, allowExactDuplicates, env ) ) return false;
}
else
{
if ( ! o1.equals( o2 ) ) return false;
}
}
}
else if ( val1 instanceof AnnotationInstance )
{
if ( ! ( val2 instanceof AnnotationInstance ) ) return false;
if ( ! annotationsAreEqual( ( AnnotationInstance ) val1, ( AnnotationInstance ) val2, allowExactDuplicates, env ) ) return false;
}
else if ( ! val1.equals( val2 ) )
{
return false;
}
}
return true;
}
public static class BeanPropertyDescriptor
{
private String _propertyName;
private String _type;
public BeanPropertyDescriptor( String propertyName, String type )
{
_propertyName = propertyName;
_type = type;
}
public String getPropertyName()
{
return _propertyName;
}
public String getType()
{
return _type;
}
}
public static class BeanPropertyDeclaration
extends BeanPropertyDescriptor
{
private MethodDeclaration _getter;
public BeanPropertyDeclaration( String propertyName, String type, MethodDeclaration getter )
{
super( propertyName, type );
_getter = getter;
}
public MethodDeclaration getGetter()
{
return _getter;
}
}
public static BeanPropertyDeclaration getBeanProperty( MethodDeclaration method )
{
if ( method.hasModifier( Modifier.PUBLIC ) && ! method.hasModifier( Modifier.STATIC ) )
{
String returnType = method.getReturnType().toString();
if ( ! returnType.equals( "void" ) && method.getParameters().length == 0 )
{
String methodName = method.getSimpleName();
String propertyName = null;
if ( methodName.startsWith( GETTER_PREFIX ) && methodName.length() > GETTER_PREFIX.length() )
{
propertyName = methodName.substring( GETTER_PREFIX.length() );
}
else if ( methodName.startsWith( BOOLEAN_GETTER_PREFIX ) && returnType.equals( "boolean" )
&& methodName.length() > BOOLEAN_GETTER_PREFIX.length() )
{
propertyName = methodName.substring( BOOLEAN_GETTER_PREFIX.length() );
}
if ( propertyName != null )
{
//
// If the first two letters are uppercase, we don't change the first character to lowercase.
// This is so that something like getURI has a property name of 'URI' (see JavaBeans spec).
//
if ( propertyName.length() == 1 )
{
propertyName = propertyName.toLowerCase();
}
else if ( ! Character.isUpperCase( propertyName.charAt( 1 ) ) )
{
propertyName = Character.toLowerCase( propertyName.charAt( 0 ) ) + propertyName.substring( 1 );
}
return new BeanPropertyDeclaration( propertyName, returnType, method );
}
}
}
return null;
}
public static Collection getBeanProperties( ClassDeclaration type, boolean getInheritedProperties )
{
MethodDeclaration[] methods = getInheritedProperties ? getClassMethods( type, null ) : type.getMethods();
ArrayList ret = new ArrayList();
for ( int i = 0; i < methods.length; i++ )
{
MethodDeclaration method = methods[i];
if ( method.hasModifier( Modifier.PUBLIC ) )
{
BeanPropertyDeclaration bpd = getBeanProperty( method );
if ( bpd != null ) ret.add( bpd );
}
}
return ret;
}
public static boolean isPageFlowClass( ClassDeclaration jclass, CoreAnnotationProcessorEnv env )
{
return getAnnotation( jclass, CONTROLLER_TAG_NAME ) != null && isAssignableFrom( JPF_BASE_CLASS, jclass, env );
}
public static String removeFileExtension( String uri )
{
int lastDot = uri.lastIndexOf( '.' );
return uri.substring( 0, lastDot );
}
public static TypeDeclaration inferTypeFromPath( String webappRelativePath, CoreAnnotationProcessorEnv env )
{
assert webappRelativePath.startsWith( "/" ) : webappRelativePath;
String className = removeFileExtension( webappRelativePath.substring( 1 ) );
return env.getTypeDeclaration( className.replace( '/', '.' ) );
}
/**
* Infers the Struts module path from the given controller class.
*/
public static String inferModulePathFromType(TypeDeclaration type) {
PackageDeclaration pkg = type.getPackage();
return pkg != null ? '/' + pkg.getQualifiedName().replace('.', '/') : "/";
}
public static TypeDeclaration getDeclaration( DeclaredType type )
{
TypeDeclaration decl = type != null ? type.getDeclaration() : null;
return decl != null ? decl : ERROR_TYPE_DECLARATION;
}
private static class ErrorTypeInstance
implements TypeInstance
{
public String toString()
{
return ERROR_STRING;
}
}
private static class ErrorTypeDeclaration
implements TypeDeclaration
{
private static final InterfaceType[] SUPERINTERFACES = new InterfaceType[0];
private static final FieldDeclaration[] FIELDS = new FieldDeclaration[0];
private static final MethodDeclaration[] METHODS = new MethodDeclaration[0];
private static final TypeDeclaration[] NESTED_TYPES = new TypeDeclaration[0];
private static final AnnotationInstance[] ANNOTATIONS = new AnnotationInstance[0];
public PackageDeclaration getPackage()
{
throw new IllegalStateException( "not implemented " );
}
public String getQualifiedName()
{
return ERROR_STRING;
}
/*
public Collection getFormalTypeParameters()
{
return Collections.EMPTY_LIST;
}
*/
public InterfaceType[] getSuperinterfaces()
{
return SUPERINTERFACES;
}
public FieldDeclaration[] getFields()
{
return FIELDS;
}
public MethodDeclaration[] getMethods()
{
return METHODS;
}
public TypeDeclaration[] getNestedTypes()
{
return NESTED_TYPES;
}
public TypeDeclaration getDeclaringType()
{
return null;
}
public String getDocComment()
{
throw new IllegalStateException( "not implemented " );
}
public AnnotationInstance[] getAnnotationInstances()
{
return ANNOTATIONS;
}
/*
public Annotation getAnnotation( Class s )
{
throw new IllegalStateException( "not implemented " );
}
*/
public Set getModifiers()
{
return Collections.EMPTY_SET;
}
public String getSimpleName()
{
return getQualifiedName();
}
public SourcePosition getPosition()
{
throw new IllegalStateException( "not implemented " );
}
public boolean hasModifier( Modifier modifier )
{
return false;
}
/*
public void accept( DeclarationVisitor declarationVisitor )
{
throw new IllegalStateException( "not implemented " );
}
*/
}
/**
* This is the same logic that we have in the runtime, in PageFlowRequestProcessor. Can't share the code, though.
*/
public static boolean isAbsoluteURI( String uri )
{
//
// This method needs to be fast, so it can't use java.net.URI.
//
if ( uri.length() == 0 || uri.charAt( 0 ) == '/' ) return false;
for ( int i = 0, len = uri.length(); i < len; ++i )
{
char c = uri.charAt( i );
if ( c == ':' )
{
return true;
}
else if ( c == '/' )
{
return false;
}
}
return false;
}
public static TypeInstance getArrayBaseType( ArrayType arrayType )
{
TypeInstance baseType = arrayType;
do
{
baseType = ( ( ArrayType ) baseType ).getComponentType();
} while ( baseType instanceof ArrayType );
return baseType;
}
public static final class Mutable
{
private Object _val = null;
public Mutable()
{
}
public Mutable( Object val )
{
_val = val;
}
public void set( Object val )
{
_val = val;
}
public Object get()
{
return _val;
}
}
public static TypeInstance getGenericBoundsType( TypeInstance type )
{
if ( type instanceof TypeVariable )
{
ReferenceType[] bounds = ( ( TypeVariable ) type ).getDeclaration().getBounds();
return bounds.length > 0 ? bounds[0] : type;
}
return type;
}
public static File getFileRelativeToSourceFile( TypeDeclaration outerClass, String relativePath,
CoreAnnotationProcessorEnv env )
throws FatalCompileTimeException
{
assert relativePath.length() > 0;
//
// If it starts with '/', just find the webapp-relative file.
//
if ( relativePath.charAt( 0 ) == '/' ) return getWebappRelativeFile( relativePath, true, env );
//
// Look for the file relative to the source directory of the given class.
//
File sourceFile = getSourceFile( outerClass, false );
File desiredParentDir = sourceFile.getAbsoluteFile().getParentFile();
File retVal = new File( desiredParentDir, relativePath );
//
// If we still haven't found it, construct a webapp-relative path and look for the file relative to the webapp.
//
if ( ! retVal.exists() )
{
PackageDeclaration jpfPackage = outerClass.getPackage();
return getWebappRelativeFile( getPathRelativeToPackage( relativePath, jpfPackage ), true, env );
}
return retVal;
}
public static String getPathRelativeToPackage( String relativePath, PackageDeclaration packageDecl )
{
if ( packageDecl != null )
{
String packageName = packageDecl.getQualifiedName();
if ( packageName.length() > 0 ) return '/' + packageName.replace( '.', '/' ) + '/' + relativePath;
}
return '/' + relativePath;
}
public static File getWebappRelativeFile( String webappRelativePath, boolean lookInSourceRoots,
CoreAnnotationProcessorEnv env )
throws FatalCompileTimeException
{
//
// Look for the file out in the web-addressable portion of the webapp.
//
assert webappRelativePath.startsWith( "/" ) : webappRelativePath;
String[] webContentRoots = getWebContentRoots( env );
for ( int i = 0; i < webContentRoots.length; i++ )
{
String webContentRoot = webContentRoots[i];
File retVal = new File( webContentRoot + webappRelativePath );
if ( retVal.exists() ) return retVal;
}
//
// If appropriate, look for the file under all the source roots.
//
if ( lookInSourceRoots )
{
String[] webSourceRoots = getWebSourceRoots( env );
for ( int i = 0; i < webSourceRoots.length; i++ )
{
String webSourceRoot = webSourceRoots[i];
File webSourceRelativeFile = new File( webSourceRoot + webappRelativePath );
if ( webSourceRelativeFile.exists() ) return webSourceRelativeFile;
}
}
return null;
}
public static String[] getWebSourceRoots( CoreAnnotationProcessorEnv env )
throws FatalCompileTimeException
{
String[] legacyOption = getOption( "-Aweb.source.roots", false, env ); // deprecated - TODO: deprecation message
if ( legacyOption != null ) return legacyOption;
return getOption( "-sourcepath", true, env );
}
public static String[] getWebContentRoots( CoreAnnotationProcessorEnv env )
throws FatalCompileTimeException
{
return getOption( "-Aweb.content.root", true, env );
}
/**
* Utility method to get the value of the "phase" option from the annotation processing environment.
* @param env the annotation processing environment
* @return the value of the "phase" option, or <code>null</code> if the option is unspecified
* @throws FatalCompileTimeException if an error occurs getting the value of the option
*/
public static String isReconcilePhase(CoreAnnotationProcessorEnv env)
throws FatalCompileTimeException {
String[] phase = getOption("-A" + JpfLanguageConstants.ANNOTATION_PROCESSOR_OPTION_PHASE, false, env);
return phase != null && phase.length > 0 ? phase[0] : null;
}
private static String[] getOption( String optionName, boolean required, CoreAnnotationProcessorEnv env )
throws MissingOptionException
{
String[] cached = ( String[] ) env.getAttribute( optionName );
if ( cached != null ) return cached;
Map options = env.getOptions();
String value = ( String ) options.get( optionName );
if ( value == null )
{
// TODO: there appears to be a bug in APT where both the key/value are contained in the key
String aptOption = optionName + '=';
for ( Iterator i = options.keySet().iterator(); i.hasNext(); )
{
String key = ( String ) i.next();
if ( key.startsWith( aptOption ) )
{
value = key.substring( aptOption.length() );
break;
}
}
}
if ( value == null )
{
if ( required ) throw new MissingOptionException( optionName );
return null;
}
String[] values = value.trim().split( File.pathSeparator );
for ( int i = 0; i < values.length; i++ )
{
values[i] = values[i].trim();
}
env.setAttribute( optionName, values );
return values;
}
}