/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.internal.gosu.parser.java.classinfo.JavaSourceUtil; import gw.lang.GosuShop; import gw.lang.javadoc.IClassDocNode; import gw.lang.reflect.BeanInfoUtil; import gw.lang.reflect.IAnnotationInfo; import gw.lang.reflect.IScriptabilityModifier; import gw.lang.reflect.IType; import gw.lang.reflect.Modifier; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.gs.IGosuObject; import gw.lang.reflect.gs.ISourceFileHandle; import gw.lang.reflect.java.AbstractJavaClassInfo; import gw.lang.reflect.java.IClassJavaClassInfo; 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.module.IModule; import gw.util.concurrent.LockingLazyVar; import gw.util.concurrent.LocklessLazyVar; import java.beans.BeanDescriptor; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.List; public class ClassJavaClassInfo extends TypeJavaClassType implements IClassJavaClassInfo { private Class<?> _class; private transient LockingLazyVar<GenericBeanInfo> _beanInfo = new LockingLazyVar<GenericBeanInfo>() { @Override protected GenericBeanInfo init() { return NewIntrospector.getBeanInfo(_class); } }; private IJavaClassMethod[] _declaredMethods; private IJavaClassInfo[] _interfaces; private IJavaClassInfo _superclass; private IJavaClassTypeVariable[] _typeVariables; private IJavaClassField[] _declaredFields; private IJavaClassConstructor[] _declaredConstructors; private IAnnotationInfo[] _declaredAnnotations; private IJavaPropertyDescriptor[] _propertyDescriptors; private IJavaMethodDescriptor[] _methodDescriptors; private IJavaClassField[] _fields; private IJavaClassType[] _genericInterfaces; private IJavaClassInfo[] _declaredClasses; private ISourceFileHandle _fileHandle; private LocklessLazyVar<IType> _enclosingClass = new LocklessLazyVar<IType>() { protected IType init() { Class enclosingClass = _class.getEnclosingClass(); return enclosingClass == null ? null : TypeSystem.get(enclosingClass, _module); } }; public ClassJavaClassInfo(Class cls, IModule module) { super(cls, module); if (cls == null) { throw new IllegalArgumentException("Class cannot be null."); } _class = cls; _module = module; } @Override public boolean isAnnotation() { return _class.isAnnotation(); } @Override public boolean isInterface() { return _class.isInterface(); } @Override public IJavaClassType getConcreteType() { return this; } @Override public String getName() { return _class.getName(); } public String getNameSignature() { if (_class.isArray()) { return _class.getName(); } else { return GosuShop.toSignature(_class.getName()); } } @Override public IJavaClassMethod getMethod(String methodName, IJavaClassInfo... paramTypes) throws NoSuchMethodException { Class[] javaParamTypes = new Class[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { IJavaClassInfo paramType = paramTypes[i]; if (paramType == null) { javaParamTypes[i] = null; } else { javaParamTypes[i] = paramType.getBackingClass(); if (javaParamTypes[i] == null) { throw new IllegalStateException("Class info for " + getName() + " is concrete, but class info for method parameter " + paramType.getName() + " is not (it's a " + paramType.getClass() + "), so can't get method by signature"); } } } return new MethodJavaClassMethod(_class.getMethod(methodName, javaParamTypes), _module); } public IJavaClassMethod getDeclaredMethod(String methodName, IJavaClassInfo... paramTypes) throws NoSuchMethodException { Class[] javaParamTypes = new Class[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { IJavaClassInfo paramType = paramTypes[i]; Class backingClass = paramType.getBackingClass(); javaParamTypes[i] = backingClass; if( backingClass == null ) { throw new IllegalStateException("Class info for " + getName() + " is concrete, but class info for method parameter " + paramType.getName() + " is not (it's a " + paramType.getClass() + "), so can't get method by signature"); } } return new MethodJavaClassMethod(_class.getMethod(methodName, javaParamTypes), _module); } @Override public IJavaClassMethod[] getDeclaredMethods() { if (_declaredMethods == null) { Method[] rawMethods = NewIntrospector.getDeclaredMethods(_class); IJavaClassMethod[] methods = new IJavaClassMethod[rawMethods.length]; for (int i = 0; i < rawMethods.length; i++) { methods[i] = new MethodJavaClassMethod(rawMethods[i], _module); } _declaredMethods = methods; } return _declaredMethods; } @Override public Object newInstance() throws InstantiationException, IllegalAccessException { return _class.newInstance(); } @Override public Object[] getEnumConstants() { return _class.getEnumConstants(); } @Override public IType getJavaType() { return TypeSystem.get(_class, _module); } @Override public IJavaClassInfo[] getInterfaces() { if (_interfaces == null) { Class[] rawInterfaces = _class.getInterfaces(); IJavaClassInfo[] interfaces = new IJavaClassInfo[rawInterfaces.length]; for (int i = 0; i < rawInterfaces.length; i++) { interfaces[i] = JavaSourceUtil.getClassInfo(rawInterfaces[i], _module); } _interfaces = interfaces; } return _interfaces; } @Override public IJavaClassInfo getSuperclass() { if (_superclass == null) { _superclass = _class.getSuperclass() == null ? NULL_TYPE : JavaSourceUtil.getClassInfo(_class.getSuperclass(), _module); } return _superclass == NULL_TYPE ? null : _superclass; } public IJavaClassTypeVariable[] getTypeParameters() { if (_typeVariables == null) { TypeVariable[] rawTypeVariables = _class.getTypeParameters(); IJavaClassTypeVariable[] typeVariables = new IJavaClassTypeVariable[rawTypeVariables.length]; for (int i = 0; i < rawTypeVariables.length; i++) { typeVariables[i] = new TypeVariableJavaTypeVariable(rawTypeVariables[i], _module); } _typeVariables = typeVariables; } return _typeVariables; } @Override public IJavaClassField[] getDeclaredFields() { if (_declaredFields == null) { Field[] rawFields = _class.getDeclaredFields(); IJavaClassField[] fields = new IJavaClassField[rawFields.length]; for (int i = 0; i < rawFields.length; i++) { fields[i] = new FieldJavaClassField(rawFields[i], _module); } _declaredFields = fields; } return _declaredFields; } @Override public IJavaClassConstructor[] getDeclaredConstructors() { if (_declaredConstructors == null) { Constructor<?>[] rawCtors = _class.getDeclaredConstructors(); List<IJavaClassConstructor> ctors = new ArrayList<IJavaClassConstructor>(rawCtors.length); for (Constructor<?> rawCtor : rawCtors) { if (!rawCtor.isSynthetic()) { ctors.add(new ConstructorJavaClassConstructor(rawCtor, _module)); } } _declaredConstructors = ctors.toArray(new IJavaClassConstructor[ctors.size()]); } return _declaredConstructors; } public IJavaClassConstructor getConstructor(IJavaClassInfo... paramTypes) throws NoSuchMethodException { Class[] javaParamTypes = new Class[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { IJavaClassInfo paramType = paramTypes[i]; if (paramType instanceof ClassJavaClassInfo) { javaParamTypes[i] = ((ClassJavaClassInfo) paramType)._class; } else { throw new IllegalStateException("Class info for " + getName() + " is concrete, but class info for method parameter " + paramType.getName() + " is not (it's a " + paramType.getClass() + "), so can't get method by signature"); } } return new ConstructorJavaClassConstructor(_class.getConstructor( javaParamTypes ), _module); } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { return _class.isAnnotationPresent(annotationClass); } @Override public IAnnotationInfo getAnnotation(Class annotationClass) { Annotation annotation = _class.getAnnotation(annotationClass); for (IAnnotationInfo annotationInfo : getDeclaredAnnotations()) { if (((ClassAnnotationInfo)annotationInfo).getInstance() == annotation) { return annotationInfo; } } return null; } @Override public IAnnotationInfo[] getDeclaredAnnotations() { if (_declaredAnnotations == null) { Annotation[] annotations = _class.getDeclaredAnnotations(); IAnnotationInfo[] declaredAnnotations = new IAnnotationInfo[annotations.length]; for (int i = 0; i < declaredAnnotations.length; i++) { declaredAnnotations[i] = new ClassAnnotationInfo(annotations[i], this); } _declaredAnnotations = declaredAnnotations; } return _declaredAnnotations; } @Override public IClassDocNode createClassDocNode() { return GosuShop.getJavaDocFactory().create(_class); } @Override public IJavaPropertyDescriptor[] getPropertyDescriptors() { if (_propertyDescriptors == null) { PropertyDescriptor[] rawPropDesc = _beanInfo.get().getPropertyDescriptors(); IJavaPropertyDescriptor[] propDesc = new IJavaPropertyDescriptor[rawPropDesc.length]; for (int i = 0; i < rawPropDesc.length; i++) { propDesc[i] = new PropertyDescriptorJavaPropertyDescriptor(rawPropDesc[i], _module); } _propertyDescriptors = propDesc; } return _propertyDescriptors; } @Override public IJavaMethodDescriptor[] getMethodDescriptors() { if (_methodDescriptors == null) { GWMethodDescriptor[] rawMDs = _beanInfo.get().getGWMethodDescriptors(); IJavaMethodDescriptor[] mds = new IJavaMethodDescriptor[rawMDs.length]; for (int i = 0; i < rawMDs.length; i++) { mds[i] = new MethodDescriptorJavaMethodDescriptor(rawMDs[i], _module); } _methodDescriptors = mds; } return _methodDescriptors; } @Override public boolean hasCustomBeanInfo() { return !(_beanInfo.get() instanceof GenericBeanInfo); } @Override public String getRelativeName() { BeanDescriptor bd = _beanInfo.get().getBeanDescriptor(); return bd != null ? bd.getName() : getJavaType().getRelativeName(); } @Override public String getDisplayName() { BeanDescriptor bd = _beanInfo.get().getBeanDescriptor(); return bd != null ? bd.getDisplayName() : getJavaType().getRelativeName(); } @Override public String getSimpleName() { return _class.getSimpleName(); } @Override public boolean isVisibleViaFeatureDescriptor(IScriptabilityModifier constraint) { return _beanInfo.get().getBeanDescriptor() == null || BeanInfoUtil.isVisible(_beanInfo.get().getBeanDescriptor(), constraint); } @Override public boolean isHiddenViaFeatureDescriptor() { return _beanInfo.get().getBeanDescriptor() != null && _beanInfo.get().getBeanDescriptor().isHidden(); } @Override public IJavaClassField[] getFields() { if (_fields == null) { Field[] rawFields = _class.getFields(); IJavaClassField[] fields = new IJavaClassField[rawFields.length]; for (int i = 0; i < rawFields.length; i++) { fields[i] = new FieldJavaClassField(rawFields[i], _module); } _fields = fields; } return _fields; } public Class getJavaClass() { return _class; } @Override public IJavaClassInfo getComponentType() { return JavaSourceUtil.getClassInfo(_class.getComponentType(), _module); } @Override public boolean isArray() { return _class.isArray(); } @Override public boolean isEnum() { return _class.isEnum(); } @Override public int getModifiers() { return _class.getModifiers(); } @Override public boolean isPrimitive() { return _class.isPrimitive(); } @Override public IJavaClassInfo getEnclosingClass() { Class enclosingClass = _class.getEnclosingClass(); if (enclosingClass != null) { return TypeSystem.getJavaClassInfo(enclosingClass, _module); } return null; } @Override public IType getEnclosingType() { return _enclosingClass.get(); } @Override public String getNamespace() { Class cls = _class; Package aPackage = cls.getPackage(); while (aPackage == null && cls.isArray()) { cls = cls.getComponentType(); aPackage = cls.getPackage(); } return aPackage == null ? null : aPackage.getName(); } @Override public IJavaClassType[] getGenericInterfaces() { if (_genericInterfaces == null) { Type[] rawIfaces = _class.getGenericInterfaces(); IJavaClassType[] ifaces = new IJavaClassType[rawIfaces.length]; for (int i = 0; i < rawIfaces.length; i++) { ifaces[i] = TypeJavaClassType.createType(rawIfaces[i], _module); } _genericInterfaces = ifaces; } return _genericInterfaces; } @Override public IJavaClassType getGenericSuperclass() { return TypeJavaClassType.createType(_class.getGenericSuperclass(), _module); } @Override public IJavaClassInfo getArrayType() { DefaultTypeLoader defaultTypeLoader = (DefaultTypeLoader)_module.getModuleTypeLoader().getDefaultTypeLoader(); return defaultTypeLoader.getJavaClassInfo( Array.newInstance(_class, 0).getClass(), _module ); } @Override public IJavaClassInfo[] getDeclaredClasses() { if (_declaredClasses == null) { Class[] rawClasses = _class.getDeclaredClasses(); ArrayList<IJavaClassInfo> declaredClasses = new ArrayList<IJavaClassInfo>(rawClasses.length); for (int i = 0; i < rawClasses.length; i++) { if (!rawClasses[i].isAnonymousClass()) { DefaultTypeLoader defaultTypeLoader = (DefaultTypeLoader)_module.getModuleTypeLoader().getDefaultTypeLoader(); IJavaClassInfo declaredClassInfo = defaultTypeLoader.getJavaClassInfo( rawClasses[i], _module ); declaredClasses.add( declaredClassInfo ); } } _declaredClasses = declaredClasses.toArray(new IJavaClassInfo[declaredClasses.size()]); } return _declaredClasses; } @Override public boolean isAssignableFrom(IJavaClassInfo aClass) { return AbstractJavaClassInfo.isAssignableFrom(this, aClass); } @Override public boolean isPublic() { return Modifier.isPublic(_class.getModifiers()); } @Override public boolean isProtected() { return Modifier.isProtected(_class.getModifiers()); } @Override public boolean isInternal() { return !isPublic() && !isProtected() && !isPrivate(); } @Override public boolean isPrivate() { return Modifier.isPrivate(_class.getModifiers()); } @Override public boolean equals(Object obj) { return AbstractJavaClassInfo.equals(this, obj); } @Override public int hashCode() { return AbstractJavaClassInfo.hashCode(this); } public String toString() { return _class.toString(); } @Override public Class getBackingClass() { return _class; } @Override public ISourceFileHandle getSourceFileHandle() { return _fileHandle; } @Override public IModule getModule() { return _module; } public boolean isTypeGosuClassInstance() { return IGosuObject.class.isAssignableFrom(_class) && TypeSystem.getByFullNameIfValid(_class.getName().replace('$', '.')) instanceof IGosuClass; } @Override public IJavaClassType resolveType(String relativeName, int ignoreFlags) { return null; } @Override public IJavaClassType resolveType(String relativeName, IJavaClassInfo whosAskin, int ignoreFlags) { Class backingClass = getBackingClass(); for (Class innerClass : backingClass.getDeclaredClasses()) { if (innerClass.getName().equals(getName() + "$" + relativeName)) { return JavaSourceUtil.getClassInfo(innerClass, getJavaType().getTypeLoader().getModule()); } } return null; } @Override public IJavaClassType resolveImport(String relativeName) { return null; } public void setSourceFileHandle(ISourceFileHandle fileHandle) { _fileHandle = fileHandle; } }