/* * Copyright 2013 Guidewire Software, Inc. */ package gw.lang.reflect; import gw.lang.parser.ScriptabilityModifiers; import gw.lang.parser.TypeVarToTypeMap; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.java.JavaTypes; import gw.util.GosuCollectionUtil; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.IdentityHashMap; public abstract class BaseFeatureInfo implements IAttributedFeatureInfo { private transient IType _intrType; private transient IFeatureInfo _container; private volatile List<IAnnotationInfo> _deprecated; transient private volatile List<IAnnotationInfo> _annotations; transient private volatile Boolean _internalAPI; static <T> List<T> compactAndLockList(List<T> list) { if (list == null || list.isEmpty()) { return Collections.emptyList(); } if (list.size() == 1) { return Collections.singletonList(list.get(0)); } if (list instanceof ArrayList) { ((ArrayList<T>)list).trimToSize(); } return Collections.unmodifiableList(list); } protected Collection<BaseFeatureInfo> getSuperAnnotatedElements() { List<BaseFeatureInfo> infos = new ArrayList<BaseFeatureInfo>(); addAnnotationSuperElement( infos, getOwnersType().getSupertype() ); if( !(this instanceof IConstructorInfo) ) { IType[] interfaces = getOwnersType().getInterfaces(); if( interfaces != null ) { for( IType anInterface : interfaces ) { if (!TypeSystem.isDeleted(anInterface)) { addAnnotationSuperElement( infos, anInterface ); } } } } return infos; } private void addAnnotationSuperElement( List<BaseFeatureInfo> infos, IType type ) { if( type != null && !(type instanceof IErrorType) ) { IFeatureInfo featureInfo; if( this instanceof IConstructorInfo ) { featureInfo = type.getTypeInfo().getConstructor( getParamTypes( ((IConstructorInfo)this).getParameters() ) ); } else if( this instanceof IMethodInfo) { featureInfo = type.getTypeInfo().getMethod( getDisplayName(), getParamTypes( ((IMethodInfo)this).getParameters() ) ); } else if( this instanceof IPropertyInfo) { featureInfo = type.getTypeInfo().getProperty( getName() ); } else { assert this instanceof ITypeInfo; featureInfo = type.getTypeInfo(); } if( featureInfo != null ) { if( featureInfo instanceof BaseFeatureInfo ) { BaseFeatureInfo baseFeatureInfo = (BaseFeatureInfo)featureInfo; infos.add( baseFeatureInfo ); } } } } public static IType[] getParamTypes( IParameterInfo[] parameters ) { List<IType> retValue = new ArrayList<IType>(); if( parameters != null ) { for( IParameterInfo parameterInfo : parameters ) { retValue.add( parameterInfo.getFeatureType() ); } } return retValue.toArray( new IType[retValue.size()] ); } public BaseFeatureInfo( IFeatureInfo container ) { _container = container; } public BaseFeatureInfo( IType intrType ) { _intrType = intrType; } public IFeatureInfo getContainer() { return _container; } public String getDisplayName() { return getName(); } public String getDescription() { return getName(); } public IType getOwnersType() { if (_intrType != null) { return _intrType; } else { IFeatureInfo container = getContainer(); return container != null ? container.getOwnersType() : null; } } /** * Returns the list of annotations <b>exactly matching</b> the annotation passed in. If the annotation is a sub type * of the type passed in, this will not return those annotations. * <p/> * This is equivilent to calling getAnnotations().get(type). * * @param type the type to look for */ public List<IAnnotationInfo> getAnnotationsOfType( IType type ) { return ANNOTATION_HELPER.getAnnotationsOfType(type, getAnnotations()); } public boolean hasAnnotation( IType type ) { return ANNOTATION_HELPER.hasAnnotation(type, getAnnotations()); } @Override public IAnnotationInfo getAnnotation( IType type ) { return ANNOTATION_HELPER.getAnnotation(type, getAnnotations(), type.getDisplayName()); } @Override public boolean hasDeclaredAnnotation( IType type ) { return ANNOTATION_HELPER.hasAnnotation( type, getDeclaredAnnotations() ); } public List<IAnnotationInfo> getAnnotations() { if( _annotations == null ) { TypeSystem.lock(); try { if( _annotations == null ) { List<IAnnotationInfo> annotations = new ArrayList<IAnnotationInfo>(); addAnnotations( annotations, this, new IdentityHashMap()); _annotations = GosuCollectionUtil.compactAndLockList(annotations); } } finally { TypeSystem.unlock(); } } return _annotations; } private void addAnnotations( List<IAnnotationInfo> annotations, BaseFeatureInfo fi, Map visitedFeatures ) { if( !visitedFeatures.containsKey( fi ) ) { for( IAnnotationInfo annotationInfo : fi.getDeclaredAnnotations() ) { if( fi == this || (ANNOTATION_HELPER.isInherited( annotationInfo.getType() ) && ANNOTATION_HELPER.shouldAddInheritedAnnotation( this, annotations, annotationInfo ))) { if (annotationInfo == null) { throw new NullPointerException("Null annotation found on " + fi + " on " + fi.getOwnersType()); } annotations.add( annotationInfo ); } } visitedFeatures.put( fi, null ); for( BaseFeatureInfo baseFeatureInfo : fi.getSuperAnnotatedElements() ) { addAnnotations( annotations, baseFeatureInfo, visitedFeatures ); } } } public boolean isVisible( IScriptabilityModifier constraint ) { return !isInternalAPI(); } public boolean isScriptable() { return isVisible( ScriptabilityModifiers.SCRIPTABLE ); } public boolean isHidden() { try { return isInternalAPI(); } catch (Exception e) { return false; } } public boolean isInternalAPI() { if( _internalAPI == null ) { TypeSystem.lock(); try { if( _internalAPI == null ) { _internalAPI = !getAnnotationsOfType(JavaTypes.INTERNAL_API()).isEmpty(); } assert _internalAPI != null; } finally { TypeSystem.unlock(); } } return _internalAPI; } public boolean isAbstract() { return false; } public boolean isFinal() { return false; } public boolean isDeprecated() { return !getDeprecatedAnnotation().isEmpty(); } public String getDeprecatedReason() { List<IAnnotationInfo> annotation = getDeprecatedAnnotation(); if (annotation.isEmpty()) { return null; } else { return (String) annotation.get(0).getFieldValue("value"); } } private List<IAnnotationInfo> getDeprecatedAnnotation() { if (_deprecated == null) { TypeSystem.lock(); try { if (_deprecated == null) { IType container = _intrType == null ? getContainer().getOwnersType() : _intrType; if( container instanceof ITypeRef ) { _deprecated = getAnnotationsOfType( TypeSystem.getByFullName("gw.lang.Deprecated", container.getTypeLoader().getModule().getExecutionEnvironment().getGlobalModule())); } else { _deprecated = Collections.emptyList(); } } } finally { TypeSystem.unlock(); } } return _deprecated; } public boolean isPrivate() { return false; } public boolean isInternal() { return false; } public boolean isProtected() { return false; } public boolean isPublic() { return true; } public IType getActualTypeInContainer( IFeatureInfo container, IType type ) { IType ownerType = container.getOwnersType(); if( ownerType.isParameterizedType() ) { TypeVarToTypeMap actualParamByVarName = TypeSystem.mapTypeByVarName( ownerType, ownerType, true ); if( container instanceof IGenericMethodInfo ) { for( IGenericTypeVariable tv : ((IGenericMethodInfo)container).getTypeVariables() ) { if( actualParamByVarName.isEmpty() ) { actualParamByVarName = new TypeVarToTypeMap(); } actualParamByVarName.put( tv.getTypeVariableDefinition().getType(), tv.getTypeVariableDefinition().getType() ); } type = TypeSystem.getActualType( type, actualParamByVarName, true ); } } return type; } public String toString() { return getName(); } }