/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.lang.annotation;
import gw.lang.GosuShop;
import gw.lang.reflect.IAnnotationInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public enum UsageModifier {
/**
* Use None to specify this annotation cannot exist on a class
*/
None,
/**
* Use None to specify this annotation can only appear once on a class
*/
One,
/**
* Use None to specify this annotation can appear many times on a class
*/
Many;
public static UsageModifier getUsageModifier( UsageTarget targetType, IType annotationType )
{
UsageModifier modifier = null;
//First look for gosu-style usage annotations
ArrayList<IAnnotationInfo> usageInfos = getExplicitUsageAnnotations(annotationType);
if( usageInfos != null && usageInfos.size() > 0 )
{
return getUsageModifier( targetType, modifier, usageInfos );
}
// if it's a java annotation with no explicit annotation usage, translate the java element type information
else if( annotationType instanceof IJavaType &&
JavaTypes.ANNOTATION().isAssignableFrom( annotationType ) )
{
return translateJavaElementTypeToUsageModifier( targetType, annotationType );
}
else
{
// By default, gosu annotations can appear multiple times
return UsageModifier.Many;
}
}
private static ArrayList<IAnnotationInfo> getExplicitUsageAnnotations(IType type)
{
ArrayList<IAnnotationInfo> lst = new ArrayList<IAnnotationInfo>();
List<IAnnotationInfo> usageAnnotations = type.getTypeInfo().getAnnotationsOfType( JavaTypes.ANNOTATION_USAGE() );
if( usageAnnotations != null )
{
lst.addAll(usageAnnotations);
}
List<IAnnotationInfo> usagesAnnotations = type.getTypeInfo().getAnnotationsOfType( JavaTypes.ANNOTATION_USAGES() );
if( usagesAnnotations != null )
{
for( IAnnotationInfo iAnnotationInfo : usagesAnnotations )
{
IAnnotationInfo[] values = (IAnnotationInfo[]) GosuShop.getAnnotationFieldValueAsArray(iAnnotationInfo, "value");
lst.addAll( Arrays.asList(values) );
}
}
return lst;
}
private static UsageModifier getUsageModifier( UsageTarget targetType, UsageModifier modifier, ArrayList<IAnnotationInfo> annotationInfos )
{
//If there are usages, then we must examine each one to find the one that most specifically applies
for( IAnnotationInfo usage : annotationInfos )
{
String target = (String) usage.getFieldValue("target");
String usageModifier = (String) usage.getFieldValue("usageModifier");
// If usage applies to all, and we haven't had a more specific match yet, get the modifier for it
if( target.equals(UsageTarget.AllTarget.name()) && modifier == null )
{
modifier = UsageModifier.valueOf(usageModifier);
}
// If usage applies to the given target, it always overrides whatever else we've seen
if( target.equals(targetType.name()) )
{
modifier = UsageModifier.valueOf(usageModifier);
}
}
// if no usage matched, then that implies that the usage is None.
if( modifier == null )
{
modifier = UsageModifier.None;
}
return modifier;
}
private static UsageModifier translateJavaElementTypeToUsageModifier( UsageTarget targetType, IType annotationType )
{
IAnnotationInfo targetAnnotation = ((IJavaType)annotationType).getBackingClassInfo().getAnnotation(Target.class);
if( targetAnnotation == null )
{
return UsageModifier.One; // If there are no targets, it can be used everywhere
}
else
{
Object v = targetAnnotation.getFieldValue("value");
String[] value;
if (v == null) {
value = null;
} else if (v.getClass().isArray()) {
value = (String[]) v;
} else {
value = new String[] {(String) v};
}
if (value == null || value.length == 0) {
return UsageModifier.One; // If there are no targets, it can be used everywhere
}
// otherwise, look for a target that matches our own UsageTarget
for( String elementTypeConst : value )
{
if( elementTypeConst.equals( ElementType.CONSTRUCTOR.name() ) && targetType == UsageTarget.ConstructorTarget ||
elementTypeConst.equals( ElementType.FIELD.name() ) && targetType == UsageTarget.PropertyTarget ||
elementTypeConst.equals( ElementType.ANNOTATION_TYPE.name() ) && targetType == UsageTarget.TypeTarget ||
elementTypeConst.equals( ElementType.TYPE.name() ) && targetType == UsageTarget.TypeTarget ||
elementTypeConst.equals( ElementType.METHOD.name() ) && (targetType == UsageTarget.MethodTarget || targetType == UsageTarget.PropertyTarget) ||
elementTypeConst.equals( ElementType.PARAMETER.name() ) && targetType == UsageTarget.ParameterTarget )
{
return UsageModifier.One;
}
}
return UsageModifier.None;
}
}
}