/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser.java.classinfo;
import gw.internal.gosu.parser.AsmClassJavaClassInfo;
import gw.internal.gosu.parser.TypeUsesMap;
import gw.internal.gosu.parser.java.IJavaASTNode;
import gw.internal.gosu.parser.java.JavaASTConstants;
import gw.internal.gosu.parser.java.JavaLexer;
import gw.internal.gosu.parser.java.JavaParser;
import gw.internal.gosu.parser.java.LeafASTNode;
import gw.internal.gosu.parser.java.SourceTypeFormatException;
import gw.internal.gosu.parser.java.TreeBuilder;
import gw.internal.gosu.parser.java.TypeASTNode;
import gw.lang.GosuShop;
import gw.lang.javadoc.IClassDocNode;
import gw.lang.parser.TypeVarToTypeMap;
import gw.lang.reflect.EnumValuePlaceholder;
import gw.lang.reflect.IAnnotationInfo;
import gw.lang.reflect.IDefaultTypeLoader;
import gw.lang.reflect.IEnumValue;
import gw.lang.reflect.IScriptabilityModifier;
import gw.lang.reflect.IType;
import gw.lang.reflect.ImplicitPropertyUtil;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.ISourceFileHandle;
import gw.lang.reflect.java.AbstractJavaClassInfo;
import gw.lang.reflect.java.ErrorJavaClassInfo;
import gw.lang.reflect.java.IJavaClassConstructor;
import gw.lang.reflect.java.IJavaClassField;
import gw.lang.reflect.java.IJavaClassInfo;
import gw.lang.reflect.java.IJavaClassMethod;
import gw.lang.reflect.java.IJavaClassType;
import gw.lang.reflect.java.IJavaClassTypeVariable;
import gw.lang.reflect.java.IJavaMethodDescriptor;
import gw.lang.reflect.java.IJavaPropertyDescriptor;
import gw.lang.reflect.java.ITypeInfoResolver;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IModule;
import gw.internal.ext.org.antlr.runtime.ANTLRStringStream;
import gw.internal.ext.org.antlr.runtime.CharStream;
import gw.internal.ext.org.antlr.runtime.RecognitionException;
import gw.internal.ext.org.antlr.runtime.TokenRewriteStream;
import gw.util.GosuObjectUtil;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public abstract class JavaSourceType extends AbstractJavaClassInfo implements IJavaClassType, ITypeInfoResolver {
public static final int IGNORE_NONE = 0;
public static final int IGNORE_INTERFACES = 1;
public static final int IGNORE_SUPERCLASS = 2;
private static Map<String, Class> PRIMITIVES = new HashMap<String, Class>();
static {
PRIMITIVES.put(int.class.getName(), int.class);
PRIMITIVES.put(byte.class.getName(), byte.class);
PRIMITIVES.put(char.class.getName(), char.class);
PRIMITIVES.put(short.class.getName(), short.class);
PRIMITIVES.put(long.class.getName(), long.class);
PRIMITIVES.put(float.class.getName(), float.class);
PRIMITIVES.put(double.class.getName(), double.class);
PRIMITIVES.put(boolean.class.getName(), boolean.class);
PRIMITIVES.put(void.class.getName(), void.class);
}
private static final Object CACHE_MISS = new Object() { public String toString() {return "cache miss";}};
protected IModule _gosuModule;
protected IJavaASTNode _typeNode;
protected IJavaASTNode _bodyNode;
protected int _typeNodeIndex;
protected String _fullyQualifiedName;
protected String _namespace;
protected String _simpleName;
protected List<String> _importList;
protected JavaSourceType _enclosingClass;
protected IJavaClassField[] _fields;
protected IJavaClassMethod[] _methods;
protected JavaSourceModifierList _modifiersList;
protected IJavaClassInfo[] _innerClasses;
protected IJavaClassConstructor[] _constructors;
protected IJavaClassInfo[] _interfaces;
protected IJavaClassType _genericSuperClass;
protected IJavaClassTypeVariable[] _typeParameters;
protected IJavaClassType[] _genericInterfaces;
private IJavaClassField[] _allFields;
private Object[] _enumConstants;
private IJavaPropertyDescriptor[] _propertyDescriptors;
private IJavaClassInfo _superClass;
private IJavaMethodDescriptor[] _methodDescriptors;
protected Map<String, Object> _cache;
private ISourceFileHandle _fileHandle;
private List<String> _staticImportList;
public static IJavaClassInfo createTopLevel(ISourceFileHandle fileHandle, IModule gosuModule) {
CharStream cs = new ANTLRStringStream(fileHandle.getSource().getSource());
JavaLexer lexer = new JavaLexer(cs);
TokenRewriteStream tokens = new TokenRewriteStream(lexer);
JavaParser parser = new JavaParser(tokens);
TreeBuilder treeBuilder = new TreeBuilder();
parser.setTreeBuilder(treeBuilder);
try {
parser.compilationUnit();
} catch (RecognitionException e) {
// ignore parse issues
}
IJavaASTNode tree = treeBuilder.getTree();
JavaSourceType result = null;
if (tree.getChildOfType(JavaASTConstants.normalClassDeclaration) != null) {
result = new JavaSourceClass(fileHandle, tree, gosuModule);
} else if (tree.getChildOfType(JavaASTConstants.normalInterfaceDeclaration) != null) {
result = new JavaSourceInterface(fileHandle, tree, gosuModule);
} else if (tree.getChildOfType(JavaASTConstants.enumDeclaration) != null) {
result = new JavaSourceEnum(fileHandle, tree, gosuModule);
} else if (tree.getChildOfType(JavaASTConstants.annotationTypeDeclaration) != null) {
result = new JavaSourceAnnotation(fileHandle, tree, gosuModule);
}
if (result != null && result.isValid()) {
return result;
} else {
return new JavaSourceUnresolvedClass(fileHandle, gosuModule);
}
}
private static JavaSourceType createInner(IJavaASTNode tree, JavaSourceType containingClass) {
if (tree.isOfType(JavaASTConstants.normalClassDeclaration)) {
return new JavaSourceClass(tree, containingClass);
} else if (tree.isOfType(JavaASTConstants.normalInterfaceDeclaration)) {
return new JavaSourceInterface(tree, containingClass);
} else if (tree.isOfType(JavaASTConstants.enumDeclaration)) {
return new JavaSourceEnum(tree, containingClass);
} else if (tree.isOfType(JavaASTConstants.annotationTypeDeclaration)) {
return new JavaSourceAnnotation(tree, containingClass);
} else {
throw new RuntimeException("unsupported node type");
}
}
/**
* For top level classes.
*/
protected JavaSourceType(ISourceFileHandle fileHandle, IJavaASTNode node, String declarationType, int typeNodeType, String bodyType, IModule gosuModule) {
_fileHandle = fileHandle;
_namespace = fileHandle.getNamespace();
_simpleName = fileHandle.getRelativeName();
_gosuModule = gosuModule;
makeImportList(node);
_typeNode = node.getChildOfTypes(declarationType);
_typeNodeIndex = _typeNode.getChildOfTypesIndex( typeNodeType );
_bodyNode = _typeNode.getChildOfTypes(bodyType);
_cache = new HashMap<String, Object>();
}
/**
* For inner classes.
*/
protected JavaSourceType(IJavaASTNode node, JavaSourceType enclosingClass, int typeNodeType, String bodyType) {
_typeNode = node;
_enclosingClass = enclosingClass;
_gosuModule = enclosingClass.getModule();
_bodyNode = _typeNode.getChildOfType(bodyType);
_typeNodeIndex = _typeNode.getChildOfTypesIndex(typeNodeType);
_cache = new HashMap<String, Object>();
_namespace = enclosingClass.getNamespace();
_simpleName = computeSimpleName();
}
private String computeSimpleName() {
try {
if (_typeNodeIndex != -1) {
IJavaASTNode child = _typeNode.getChild(_typeNodeIndex + 1);
if (checkNode(child, JavaParser.IDENTIFIER)) {
return child.getText();
}
}
} catch(IndexOutOfBoundsException e) {
// no node for classname field
}
throw new SourceTypeFormatException("no class name");
}
private static IJavaClassType resolveParameterizedArrayType(ITypeInfoResolver typeResolver, TypeASTNode typeASTNode, String typeName) {
if (!typeName.endsWith("[]")) {
return resolveParameterizedType(typeResolver, typeASTNode, typeName);
}
IJavaClassType type = resolveParameterizedArrayType(typeResolver, typeASTNode, typeName.substring(0, typeName.length() - 2));
return new JavaSourceArrayType(type);
}
public static IJavaClassType createType(ITypeInfoResolver typeResolver, IJavaASTNode typeNode) {
if (typeNode instanceof TypeASTNode) {
TypeASTNode typeASTNode = (TypeASTNode) typeNode;
String typeName = typeASTNode.getTypeName();
if (typeASTNode.isParameterizedArrayType()) {
return resolveParameterizedArrayType(typeResolver, typeASTNode, typeName);
} else if (typeASTNode.isParameterized()) {
return resolveParameterizedType(typeResolver, typeASTNode, typeName);
} else {
return createType(typeResolver, typeName, IGNORE_NONE);
}
} else if (isTypeArgument(typeNode)) {
if (isWildcardType(typeNode)) {
IJavaASTNode childOfType = typeNode.getChildOfType(JavaASTConstants.type);
if (childOfType == null) {
return new JavaWildcardType(NULL_TYPE);
} else {
if (isSuper(typeNode)) {
return new JavaWildcardType(JavaTypes.OBJECT().getBackingClassInfo());
} else {
return new JavaWildcardType(createType(typeResolver, childOfType));
}
}
} else {
return createType(typeResolver, typeNode.getChildOfType(JavaASTConstants.type));
}
} else { // 'void' and the like go through here
return createType(typeResolver, typeNode.getText(), IGNORE_NONE);
}
}
private static boolean isSuper(IJavaASTNode typeNode) {
return typeNode.getChildOfType(JavaParser.SUPER) != null;
}
private static boolean isTypeArgument(IJavaASTNode typeNode) {
return typeNode.isOfType(JavaASTConstants.typeArgument);
}
private static boolean isWildcardType(IJavaASTNode typeNode) {
return typeNode.getChildOfType(JavaParser.QUES) != null;
}
private static IJavaClassType resolveParameterizedType(ITypeInfoResolver typeResolver, TypeASTNode typeNode, String typeName) {
List<IJavaASTNode> typeArgumentNodes = typeNode.getTypeArguments();
IJavaClassType[] typeParameters = new IJavaClassType[typeArgumentNodes.size()];
IJavaClassType concreteType = createType(typeResolver, typeName, IGNORE_NONE);
if (concreteType == null || concreteType instanceof ErrorJavaClassInfo) {
return ERROR_TYPE;
}
IJavaClassTypeVariable[] parameters = ((IJavaClassInfo) concreteType).getTypeParameters();
if (parameters.length != typeParameters.length) {
throw new RuntimeException("Type parameters screwup.");
}
for (int i = 0; i < typeParameters.length; i++) {
typeParameters[i] = createType(typeResolver, typeArgumentNodes.get(i));
if (typeParameters[i] instanceof JavaWildcardType && ((JavaWildcardType)typeParameters[i]).getUpperBound() == NULL_TYPE) {
((JavaWildcardType)typeParameters[i]).setBound(parameters[i].getBounds()[0]);
}
}
return new JavaParameterizedType(typeParameters, concreteType);
}
public static IJavaClassType createType(ITypeInfoResolver typeResolver, String typeName, int ignoreFlags) {
if (typeName.endsWith("[]")) {
String typeNameNoArray = typeName.substring(0, typeName.length() - 2);
IJavaClassType componentType = createType(typeResolver, typeNameNoArray, ignoreFlags);
if (componentType instanceof IJavaClassInfo) {
return new JavaArrayClassInfo((IJavaClassInfo) componentType);
} else {
return new JavaSourceArrayType(componentType);
}
} else {
IJavaClassType type = typeResolver.resolveType(typeName, ignoreFlags);
return type == null ? ERROR_TYPE : type;
}
}
protected void makeImportList(IJavaASTNode node) {
List<IJavaASTNode> importDecls = node.getChildrenOfTypes(JavaASTConstants.importDeclaration);
List<String> importList = new ArrayList<String>(importDecls.size());
List<String> staticImportList = new ArrayList<String>(importDecls.size());
importList.add("java.lang.*");
for (IJavaASTNode importDecl : importDecls) {
StringBuilder importText = new StringBuilder();
List<IJavaASTNode> children = importDecl.getChildren();
boolean bStatic = false;
for (int i = 1; i < children.size(); i++) {
IJavaASTNode leaf = children.get(i);
String token = leaf.getText();
if ("static".equals(token)) {
bStatic = true;
continue;
}
importText.append(token);
}
if (bStatic) {
int iDotStar = importText.lastIndexOf(".*");
if (iDotStar > 0) {
importText.delete(iDotStar, importText.length());
}
staticImportList.add( importText.toString() );
}
importList.add(importText.toString());
}
Collections.sort(importList, new Comparator<String>() {
public int compare(String s1, String s2) {
if (s1.endsWith("*")) {
return +1;
}
if (s2.endsWith("*")) {
return -1;
}
return 0;
}
});
_importList = importList;
_staticImportList = staticImportList;
}
public IModule getModule() {
return _gosuModule;
}
@Override
public String getNameSignature() {
return GosuShop.toSignature(getName());
}
@Override
public String getRelativeName() {
return getName().substring(getNamespace().length() + 1);
}
@Override
public String getDisplayName() {
return getSimpleName();
}
@Override
public String getSimpleName() {
return _simpleName;
}
@Override
public boolean isArray() {
return false;
}
private boolean checkNode(IJavaASTNode node, int tokenType) {
return node != null && node.isLeaf() && ((LeafASTNode) node).getTokenType() == tokenType;
}
public String getName() {
if (_fullyQualifiedName == null) {
if (_enclosingClass == null) {
_fullyQualifiedName = _namespace + "." + getSimpleName();
} else {
_fullyQualifiedName = _enclosingClass.getName() + "." + getSimpleName();
}
}
return _fullyQualifiedName;
}
@Override
public IJavaClassInfo[] getInterfaces() {
if (_interfaces == null) {
TypeSystem.lock();
try {
if (_interfaces == null) {
IJavaASTNode typeList = _typeNode.getChildOfType(JavaASTConstants.typeList);
if (typeList == null) {
_interfaces = IJavaClassType.EMPTY_ARRAY;
return _interfaces;
}
List<IJavaASTNode> children = typeList.getChildren();
IJavaClassInfo[] interfaces = new IJavaClassInfo[children.size()];
for (int i = 0; i < interfaces.length; i++) {
String typeName = ((TypeASTNode) children.get(i)).getTypeName();
IJavaClassInfo classInfo = (IJavaClassInfo) createType(this, typeName, IGNORE_SUPERCLASS | IGNORE_INTERFACES);
interfaces[i] = classInfo;
}
_interfaces = interfaces;
}
} finally {
TypeSystem.unlock();
}
}
return _interfaces;
}
@Override
public IJavaClassType[] getGenericInterfaces() {
TypeSystem.lock();
try {
if (_genericInterfaces == null) {
IJavaASTNode typeList = _typeNode.getChildOfType(JavaASTConstants.typeList);
if (typeList == null) {
_genericInterfaces = IJavaClassType.EMPTY_ARRAY;
return _genericInterfaces;
}
List<IJavaASTNode> children = typeList.getChildren();
IJavaClassType[] genericInterfaces = new IJavaClassType[children.size()];
for (int i = 0; i < genericInterfaces.length; i++) {
genericInterfaces[i] = createType(this, children.get(i));
}
_genericInterfaces = genericInterfaces;
}
return _genericInterfaces;
} finally {
TypeSystem.unlock();
}
}
@Override
public IJavaClassInfo getSuperclass() {
if (_superClass == null) {
if (isInterface()) {
_superClass = NULL_TYPE;
} else if (isEnum()) {
_superClass = JavaTypes.ENUM().getBackingClassInfo();
} else {
IJavaASTNode superTypeNode = _typeNode.getChildOfType(JavaASTConstants.type);
_superClass = superTypeNode != null
? (IJavaClassInfo) createType(this, ((TypeASTNode) superTypeNode).getTypeName(), IGNORE_SUPERCLASS | IGNORE_INTERFACES)
: JavaTypes.OBJECT().getBackingClassInfo();
}
if (hasCyclicInheritance(_superClass)) {
_superClass = NULL_TYPE;
}
}
return _superClass == NULL_TYPE ? null : _superClass;
}
private boolean hasCyclicInheritance(IJavaClassInfo superClass) {
Set<IJavaClassInfo> visited = new HashSet<IJavaClassInfo>();
visited.add(this);
while (superClass != null && !visited.contains(superClass)) {
visited.add(superClass);
superClass = superClass.getSuperclass();
}
return superClass != null;
}
@Override
public IJavaClassType getGenericSuperclass() {
if (_genericSuperClass == null) {
if (isInterface()) {
_genericSuperClass = NULL_TYPE;
} else if (isEnum()) {
_genericSuperClass = JavaTypes.ENUM().getBackingClassInfo();
} else {
IJavaASTNode superTypeNode = _typeNode.getChildOfType(JavaASTConstants.type);
_genericSuperClass = superTypeNode != null ? createType(this, superTypeNode) : NULL_TYPE;
}
}
return _genericSuperClass == NULL_TYPE ? null : _genericSuperClass;
}
public void initMethodsAndConstructors() {
List<IJavaASTNode> methodNodes = _bodyNode.getChildrenOfTypes(getMethodDeclNodeType());
List<IJavaClassMethod> methods = new ArrayList<IJavaClassMethod>();
List<IJavaClassConstructor> constructors = new ArrayList<IJavaClassConstructor>();
for (int i = 0; i < methodNodes.size(); i++) {
JavaSourceMethod method = JavaSourceMethod.create(methodNodes.get(i), this);
if (method != null) {
if (method.isConstructor()) {
constructors.add((IJavaClassConstructor) method);
} else {
methods.add(method);
}
}
}
if (isEnum()) {
methods.add(new SyntheticJavaMethod(this, this, this, "valueOf",
Modifier.STATIC | Modifier.PUBLIC, new IJavaClassInfo[]{
TypeSystem.getJavaClassInfo(String.class)
}, new IJavaClassInfo[0]));
methods.add(new SyntheticJavaMethod(this, this.getArrayType(), this.getArrayType(), "values",
Modifier.STATIC | Modifier.PUBLIC, new IJavaClassInfo[0], new IJavaClassInfo[0]));
}
if (isClass() && constructors.size() == 0) {
constructors.add(new JavaSourceDefaultConstructor(this));
}
_methods = methods.toArray(new IJavaClassMethod[methods.size()]);
_constructors = constructors.toArray(new IJavaClassConstructor[constructors.size()]);
}
private String getMethodDeclNodeType() {
if (isClass() ||isEnum()) {
return JavaASTConstants.methodDeclaration;
} else if (isInterface()) {
return JavaASTConstants.interfaceMethodDeclaration;
} else if (isAnnotation()) {
return JavaASTConstants.annotationMethodDeclaration;
} else {
throw new RuntimeException("What the heck is this thing.");
}
}
public IJavaClassMethod[] getDeclaredMethods() {
if (_methods == null) {
initMethodsAndConstructors();
}
return _methods;
}
public IJavaClassConstructor[] getDeclaredConstructors() {
if (_constructors == null) {
initMethodsAndConstructors();
}
return _constructors;
}
public IJavaClassConstructor getConstructor( IJavaClassInfo... paramTypes ) throws NoSuchMethodException {
outer:
for (IJavaClassConstructor ctor : getDeclaredConstructors()) {
IJavaClassInfo[] methodParamTypes = ctor.getParameterTypes();
if (paramTypes.length != methodParamTypes.length) {
continue;
}
for (int i = 0; i < paramTypes.length; i++) {
if (!paramTypes[i].equals(methodParamTypes[i])) {
continue outer;
}
}
return ctor;
}
throw new NoSuchMethodException();
}
public IJavaClassField[] getDeclaredFields() {
if (_fields == null) {
List<IJavaASTNode> fieldNodes = _bodyNode.getChildrenOfTypes(
JavaASTConstants.fieldDeclaration,
JavaASTConstants.interfaceFieldDeclaration,
JavaASTConstants.enumConstant
);
_fields = new JavaSourceField[fieldNodes.size()];
for (int i = 0; i < _fields.length; i++) {
_fields[i] = JavaSourceField.create(fieldNodes.get(i), this);
}
}
return _fields;
}
public IJavaClassField[] getFields() {
if (_allFields == null) {
List<IJavaClassField> fields = new ArrayList<IJavaClassField>();
IJavaClassField[] declaredFields = getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
IJavaClassField field = declaredFields[i];
if (Modifier.isPublic(field.getModifiers())) {
fields.add(field);
}
}
IJavaClassInfo superclass = getSuperclass();
if (superclass != null) {
fields.addAll(Arrays.asList(superclass.getFields()));
}
_allFields = fields.toArray(new IJavaClassField[fields.size()]);
}
return _allFields;
}
@Override
public Object[] getEnumConstants() {
if (_enumConstants == null) {
List<IEnumValue> enums = new ArrayList<IEnumValue>();
IJavaClassField[] fields = getFields();
for (IJavaClassField field : fields) {
if (field.isEnumConstant()) {
enums.add(new EnumValuePlaceholder(field.getName()));
}
}
_enumConstants = enums.toArray(new IEnumValue[enums.size()]);
}
return _enumConstants;
}
@Override
public IJavaPropertyDescriptor[] getPropertyDescriptors() {
if (_propertyDescriptors == null) {
_propertyDescriptors = initPropertyDescriptors();
}
return _propertyDescriptors;
}
protected IJavaPropertyDescriptor[] initPropertyDescriptors() {
Map<String, IJavaClassMethod> getters = new HashMap<String, IJavaClassMethod>();
Map<String, IJavaClassMethod> setters = new HashMap<String, IJavaClassMethod>();
List<IJavaClassMethod> methods = new ArrayList<IJavaClassMethod>();
methods.addAll(Arrays.asList(getDeclaredMethods()));
for (IJavaClassMethod method : methods) {
ImplicitPropertyUtil.ImplicitPropertyInfo info = JavaSourceUtil.getImplicitProperty(method);
if (info != null) {
if (info.isGetter() && !getters.containsKey(info.getName())) {
getters.put(info.getName(), method);
} else if (info.isSetter() && !setters.containsKey(info.getName())) {
setters.put(info.getName(), method);
}
}
}
List<IJavaPropertyDescriptor> propertyDescriptors = new ArrayList<IJavaPropertyDescriptor>();
for (Map.Entry<String, IJavaClassMethod> entry : getters.entrySet()) {
String propName = entry.getKey();
IJavaClassMethod setter = setters.get(propName);
IJavaClassMethod getter = entry.getValue();
IJavaClassType getterType = getter == null ? null : getter.getGenericReturnType();
if (setter != null) {
setters.remove(propName);
if (getterType != null &&
!setter.getGenericParameterTypes()[0].equals(getterType) &&
!GosuObjectUtil.equals(setter.getGenericParameterTypes()[0].getConcreteType(), getterType)) {
setter = null;
}
}
if (getterType != null) {
if( setter == null ) {
setter = AsmClassJavaClassInfo.maybeFindSetterInSuper( getter, getSuperclass() );
}
propertyDescriptors.add(new JavaSourcePropertyDescriptor(
propName, (IJavaClassInfo) getterType.getConcreteType(), getter, setter));
}
else {
setter = setters.get(propName);
if( setter != null ) {
getter = AsmClassJavaClassInfo.maybeFindGetterInSuper( setter, getSuperclass() );
if( getter != null ) {
setters.remove( propName );
propertyDescriptors.add( new JavaSourcePropertyDescriptor(
propName, (IJavaClassInfo)getterType.getConcreteType(), getter, setter ) );
}
}
}
}
for (Map.Entry<String, IJavaClassMethod> entry : setters.entrySet()) {
String propName = entry.getKey();
IJavaClassMethod setter = entry.getValue();
IJavaClassType setterType = setter.getGenericReturnType();
propertyDescriptors.add(new JavaSourcePropertyDescriptor(propName, (IJavaClassInfo) setterType.getConcreteType(), null, setter));
}
return propertyDescriptors.toArray(new IJavaPropertyDescriptor[propertyDescriptors.size()]);
}
@Override
public IType getJavaType() {
return TypeSystem.get(this);
}
public IJavaClassTypeVariable[] getTypeParameters() {
if (_typeParameters == null) {
IJavaASTNode typeParamsNode = _typeNode.getChildOfType(JavaASTConstants.typeParameters);
if (typeParamsNode != null) {
List<IJavaASTNode> typeParamNodes = typeParamsNode.getChildrenOfTypes(JavaASTConstants.typeParameter);
_typeParameters = new IJavaClassTypeVariable[typeParamNodes.size()];
for (int i = 0; i < _typeParameters.length; i++) {
_typeParameters[i] = JavaSourceTypeVariable.create(this, typeParamNodes.get(i));
}
} else {
_typeParameters = JavaSourceTypeVariable.EMPTY;
}
}
return _typeParameters;
}
@Override
public IClassDocNode createClassDocNode() {
return null;
}
@Override
public boolean hasCustomBeanInfo() {
return false;
}
@Override
public boolean isVisibleViaFeatureDescriptor(IScriptabilityModifier constraint) {
return true;
}
@Override
public boolean isHiddenViaFeatureDescriptor() {
return false;
}
@Override
public IJavaClassInfo getComponentType() {
return null;
}
@Override
public int getModifiers() {
return getModifierList().getModifiers();
}
public IModifierList getModifierList() {
if (_modifiersList == null) {
_modifiersList = new JavaSourceModifierList(this, _typeNode.getChildOfType(JavaASTConstants.modifiers));
}
return _modifiersList;
}
public IType getEnclosingType() {
return _enclosingClass == null ? null : TypeSystem.get(_enclosingClass);
}
public String getNamespace() {
return _namespace;
}
@Override
public IJavaClassInfo getArrayType() {
return new JavaArrayClassInfo(this);
}
@Override
public IJavaClassInfo[] getDeclaredClasses() {
if (_innerClasses == null) {
List<IJavaASTNode> innerNodes = _bodyNode.getChildrenOfTypes(
JavaASTConstants.normalClassDeclaration,
JavaASTConstants.normalInterfaceDeclaration,
JavaASTConstants.annotationTypeDeclaration,
JavaASTConstants.enumDeclaration
);
List<JavaSourceType> innerClasses = new ArrayList<JavaSourceType>(innerNodes.size());
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < innerNodes.size(); i++) {
JavaSourceType inner = null;
try {
inner = JavaSourceType.createInner(innerNodes.get(i), this);
} catch (SourceTypeFormatException e) {
// malformed class
}
if (inner != null && inner.isValid()) {
innerClasses.add(inner);
}
}
_innerClasses = innerClasses.toArray(new JavaSourceType[innerClasses.size()]);
}
return _innerClasses;
}
@Override
public Class getBackingClass() {
return null;
}
public IJavaClassInfo getInnerClass(String relativeName) {
for (IJavaClassInfo innerClass : getDeclaredClasses()) {
if (innerClass.getSimpleName().equals(relativeName)) {
return innerClass;
}
}
return null;
}
private boolean isValid() {
return _typeNode != null && _bodyNode != null;
}
@Override
public IType getActualType(TypeVarToTypeMap typeMap) {
return getActualType(typeMap, false);
}
@Override
public IType getActualType(TypeVarToTypeMap typeMap, boolean bKeepTypeVars) {
return TypeSystem.getActualType(getJavaType(), typeMap, bKeepTypeVars);
}
@Override
public IJavaClassType getConcreteType() {
return this;
}
public boolean isClass() {
return this instanceof JavaSourceClass;
}
public boolean isInterface() {
return this instanceof JavaSourceInterface;
}
@Override
public Object newInstance() throws InstantiationException, IllegalAccessException {
return null;
}
@Override
public IJavaClassMethod getMethod(String methodName, IJavaClassInfo... paramTypes) throws NoSuchMethodException {
outer:
for (IJavaClassMethod method : getDeclaredMethods()) {
if (!method.getName().equals(methodName)) {
continue;
}
IJavaClassInfo[] methodParamTypes = method.getParameterTypes();
if (paramTypes.length != methodParamTypes.length) {
continue;
}
for (int i = 0; i < paramTypes.length; i++) {
if (!paramTypes[i].equals(methodParamTypes[i])) {
continue outer;
}
}
return method;
}
throw new NoSuchMethodException();
}
public IJavaClassMethod getDeclaredMethod(String methodName, IJavaClassInfo... paramTypes) throws NoSuchMethodException {
return getMethod(methodName, paramTypes);
}
@Override
public IJavaMethodDescriptor[] getMethodDescriptors() {
if (_methodDescriptors == null) {
IJavaClassMethod[] declaredMethods = getDeclaredMethods();
_methodDescriptors = new IJavaMethodDescriptor[declaredMethods.length];
for (int i = 0; i < declaredMethods.length; i++) {
_methodDescriptors[i] = new JavaSourceMethodDescriptor(declaredMethods[i]);
}
}
return _methodDescriptors;
}
public boolean isEnum() {
return this instanceof JavaSourceEnum;
}
@Override
public boolean isPrimitive() {
return false;
}
public boolean isAnnotation() {
return this instanceof JavaSourceAnnotation;
}
public boolean isPublic() {
return getModifierList().hasModifier(Modifier.PUBLIC);
}
public boolean isProtected() {
return getModifierList().hasModifier(Modifier.PROTECTED);
}
public boolean isPrivate() {
return getModifierList().hasModifier(Modifier.PRIVATE);
}
public boolean isInternal() {
return getModifierList().hasModifier(Modifier.INTERNAL) ||
(!getModifierList().hasModifier(Modifier.PUBLIC) &&
!getModifierList().hasModifier(Modifier.PROTECTED) &&
!getModifierList().hasModifier(Modifier.PRIVATE));
}
public IJavaClassType resolveType(String relativeName, int ignoreFlags) {
return resolveType(relativeName, this, ignoreFlags);
}
public IJavaClassType resolveType(String relativeName, IJavaClassInfo whosAskin, int ignoreFlags) {
Object cachedOrCyclicType = getCachedDetectCyclicType(relativeName);
if( cachedOrCyclicType != null ) {
return cachedOrCyclicType == CACHE_MISS ? null : (IJavaClassType)cachedOrCyclicType;
}
IJavaClassType type = null;
try {
// Primitives
if (PRIMITIVES.containsKey(relativeName)) {
return type = JavaTypes.getJreType(PRIMITIVES.get(relativeName)).getBackingClassInfo();
}
// Qualified or Semi-qualified Type e.g., Outer.Inner
int iDot = relativeName.indexOf(".");
if (iDot == 0) {
return type = null;
} else if (iDot >= 0) {
return type = resolveQualifiedOrSemiQualifiedType(relativeName, whosAskin, iDot, ignoreFlags);
}
// Identity
if (relativeName.equals(getSimpleName())) {
return type = this;
}
// Direct Inner Classes (no recursion)
IJavaClassType innerClass = JavaSourceUtil.resolveInnerClass(this, relativeName, whosAskin);
if (innerClass != null) {
return type = innerClass;
}
// Imported Class
IJavaClassType importedType = resolveImport(relativeName);
if (importedType != null) {
return type = importedType;
}
// type variable
for (IJavaClassTypeVariable typeParameter : getTypeParameters()) {
if (relativeName.equals(typeParameter.getName())) {
return type = typeParameter;
}
}
// Enclosing Class
IJavaClassInfo enclosingClass = getEnclosingClass();
if (enclosingClass != null) {
IJavaClassType outerClsCtx = enclosingClass.resolveType(relativeName, whosAskin, IGNORE_NONE);
if (outerClsCtx != null) {
return type = outerClsCtx;
}
}
// Class in Same Package
IJavaClassType neighborClass = resolveClassInSamePackage(relativeName);
if (neighborClass != null) {
return type = neighborClass;
}
// Super Ancestry
if ((ignoreFlags & IGNORE_SUPERCLASS) == 0) {
IJavaClassInfo superClass = getSuperclass();
if (superClass != null) {
IJavaClassType outerClsCtx = superClass.resolveType(relativeName, whosAskin, ignoreFlags);
if (outerClsCtx != null) {
return type = outerClsCtx;
}
}
}
// Interface Ancestry
if ((ignoreFlags & IGNORE_INTERFACES) == 0) {
for (IJavaClassInfo ifaceType : getInterfaces()) {
IJavaClassType innerClassCtx = ifaceType.resolveType(relativeName, whosAskin, ignoreFlags);
if (innerClassCtx != null) {
return type = innerClassCtx;
}
}
}
// Not found
return type = null;
}
finally {
if( type == null ) {
_cache.put( relativeName, CACHE_MISS );
}
else {
_cache.put( relativeName, type );
}
}
}
private Object getCachedDetectCyclicType( String relativeName ) {
// Short-circuit type cycles
Object type = _cache.get( relativeName );
if( type != null ) {
return type;
}
// _cache.put( relativeName, CACHE_MISS );
return null;
}
private IJavaClassType resolveQualifiedOrSemiQualifiedType(String typeName, IJavaClassInfo whosAskin, int iDot, int ignoreFlags) {
// typeName is either:
// 1) A fully qualified name e.g., com.abc.Foo
// 2) A semi-qualified inner class name e.g., Foo.Inner
// 3) A fully qualified inner class name e.g., com.abc.Foo.Inner
String rootName = typeName.substring( 0, iDot );
// First try to resolve a relative root type e.g., the Foo in Foo.Inner (for case #2)
IJavaClassType rootType = resolveType( rootName, whosAskin, ignoreFlags);
if (rootType == null) {
// Now try to resolve a root qualified type, with or without an inner class (for cases #1 and #3)
rootType = resolveRootQualifiedType(typeName);
if (rootType != null) {
if (rootType.getName().replace('$', '.').equals(typeName)) {
// Case #1, no inner class
return rootType;
}
} else {
return null;
}
}
// Case #2 or #3
return resolveTrailingInnerClass(rootType, typeName, whosAskin);
}
private IJavaClassType resolveRootQualifiedType(String qname) {
while (true) {
IJavaClassType rootType = JavaSourceUtil.getClassInfo(qname, _gosuModule);
if (rootType != null) {
return rootType;
}
int iLastDot = qname.lastIndexOf('.');
if (iLastDot > 0) {
qname = qname.substring(0, iLastDot);
} else {
return null;
}
}
}
private IJavaClassType resolveTrailingInnerClass(IJavaClassType rootType, String fullNameIncludingRoot, IJavaClassInfo whosAskin) {
String rootName;
if (fullNameIncludingRoot.startsWith(rootType.getName())) {
rootName = rootType.getName();
} else {
rootName = rootType.getSimpleName().replace('$', '.');
}
String innerSuffix = fullNameIncludingRoot.substring(rootName.length() + 1);
String[] innerNames = innerSuffix.split("\\.");
for (String innerName : innerNames) {
IJavaClassType innerClass = JavaSourceUtil.resolveInnerClass((IJavaClassInfo) rootType.getConcreteType(), innerName, whosAskin);
if (innerClass == null) {
return null;
}
rootType = innerClass;
}
return rootType;
}
private IJavaClassType resolveClassInSamePackage(String relativeName) {
String packageName = getNamespace();
if (packageName.length() > 0) {
relativeName = packageName + '.' + relativeName;
}
return JavaSourceUtil.getClassInfo(relativeName, _gosuModule);
}
public IJavaClassType resolveImport(String relativeName) {
if( _importList == null ) {
return null;
}
for (String importText : _importList) {
int iStar = importText.lastIndexOf("*");
if (iStar > 0) {
IJavaClassType type = JavaSourceUtil.getClassInfo(importText.substring(0, iStar) + relativeName, _gosuModule);
if (type != null) {
return type;
}
} else if (importText.endsWith('.' + relativeName)) {
IJavaClassType type = JavaSourceUtil.getClassInfo(importText, _gosuModule);
if (type != null) {
return type;
}
}
}
return null;
}
@Override
public IJavaClassInfo getEnclosingClass() {
return _enclosingClass;
}
public String toString() {
return getName();
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getModifierList().isAnnotationPresent(annotationClass);
}
@Override
public IAnnotationInfo getAnnotation(Class annotationClass) {
return getModifierList().getAnnotation(annotationClass);
}
@Override
public IAnnotationInfo[] getDeclaredAnnotations() {
return getModifierList().getAnnotations();
}
public TypeUsesMap getTypeUsesMap() {
TypeUsesMap typeUsesMap = new TypeUsesMap();
typeUsesMap.addToTypeUses( getNamespace() + ".*" );
if (_importList != null) {
for (String s : _importList) {
typeUsesMap.addToTypeUses(s);
}
}
return typeUsesMap;
}
public List<String> getImportList() {
return _importList;
}
public List<String> getStaticImports() {
return _staticImportList;
}
@Override
public ISourceFileHandle getSourceFileHandle() {
if (_fileHandle == null) {
IDefaultTypeLoader loader = _enclosingClass.getModule().getTypeLoaders(IDefaultTypeLoader.class).get(0);
_fileHandle = loader.getSouceFileHandle(getName());
}
return _fileHandle;
}
}