/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser.java.classinfo; import gw.config.CommonServices; import gw.internal.gosu.parser.DefaultTypeLoader; import gw.lang.reflect.IDefaultTypeLoader; import gw.lang.reflect.ITypeRefFactory; import gw.lang.reflect.ImplicitPropertyUtil; import gw.lang.reflect.Modifier; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.java.IJavaClassInfo; import gw.lang.reflect.java.IJavaClassMethod; import gw.lang.reflect.java.IJavaClassType; import gw.lang.reflect.java.asm.AsmClass; import gw.lang.reflect.module.IModule; public class JavaSourceUtil { public static IJavaClassInfo getClassInfo( AsmClass cls, IModule module ) { if( isProxy( cls ) ) { return getJavaClassInfo( cls, module ); } else { if( !CommonServices.getPlatformHelper().isInIDE() ) { // Don't try to load from source unless we have to, this saves a load of time esp. for case // where we're loading an inner java class where replacing the '$' below with '.' we bascially // put the type system through a load of unnecessary work. IJavaClassInfo classInfo = getJavaClassInfo( cls, module ); if( classInfo != null ) { return classInfo; } } return getClassInfo( cls.getName().replace( '$', '.' ), module ); } } private static IJavaClassInfo getJavaClassInfo( AsmClass asmClass, IModule module ) { for( IModule m : module.getModuleTraversalList() ) { TypeSystem.pushModule( m ); try { DefaultTypeLoader defaultTypeLoader = (DefaultTypeLoader)m.getModuleTypeLoader().getDefaultTypeLoader(); if( defaultTypeLoader != null ) { IJavaClassInfo javaClassInfo = defaultTypeLoader.getJavaClassInfo( asmClass, module ); if( javaClassInfo != null ) { return javaClassInfo; } } } finally { TypeSystem.popModule( m ); } } return null; } public static IJavaClassInfo getClassInfo(Class aClass, IModule gosuModule) { DefaultTypeLoader loader = (DefaultTypeLoader) gosuModule.getModuleTypeLoader().getDefaultTypeLoader(); if (isProxy(aClass)) { return loader.getJavaClassInfo( aClass, gosuModule ); } else if (aClass.isArray()) { IJavaClassInfo classInfo = getClassInfo(aClass.getComponentType(), gosuModule); IModule module = classInfo.getModule(); return loader.getJavaClassInfo(aClass, module); } else { if( !CommonServices.getPlatformHelper().isInIDE() ) { // Don't try to load from source unless we have to, this saves a load of time esp. for case // where we're loading an inner java class where replacing the '$' below with '.' we bascially // put the type system through a load of unnecessary work. IJavaClassInfo javaClassInfo = loader.getJavaClassInfo( aClass, gosuModule ); if (javaClassInfo != null) { return javaClassInfo; } } return getClassInfo(aClass.getName().replace('$', '.'), gosuModule); } } private static boolean isProxy(AsmClass aClass) { String name = aClass.getName(); return name.endsWith(ITypeRefFactory.USER_PROXY_SUFFIX) || name.endsWith(ITypeRefFactory.SYSTEM_PROXY_SUFFIX); } private static boolean isProxy(Class aClass) { String name = aClass.getName(); return name.endsWith(ITypeRefFactory.USER_PROXY_SUFFIX) || name.endsWith(ITypeRefFactory.SYSTEM_PROXY_SUFFIX); } public static IJavaClassInfo getClassInfo(String qualifiedName, IModule gosuModule) { for (IModule module : gosuModule.getModuleTraversalList()) { IDefaultTypeLoader loader = module.getModuleTypeLoader().getDefaultTypeLoader(); if (loader != null) { IJavaClassInfo javaClassInfo = loader.getJavaClassInfo(qualifiedName); if (javaClassInfo != null) { return javaClassInfo; } } } return null; } public static ImplicitPropertyUtil.ImplicitPropertyInfo getImplicitProperty(IJavaClassMethod method) { IJavaClassType returnType = method.getGenericReturnType(); if (returnType != null) { String returnTypeName = returnType.getName(); int argCount = method.getParameterTypes().length; String name = method.getName(); if (argCount == 0 && !returnTypeName.equals("void")) { if (name.startsWith(ImplicitPropertyUtil.GET) && (name.length() > ImplicitPropertyUtil.GET.length())) { return new ImplicitPropertyUtil.ImplicitPropertyInfo(false, true, ImplicitPropertyUtil.capitalizeFirstChar(name.substring(3))); } else if (name.startsWith(ImplicitPropertyUtil.IS) && (name.length() > ImplicitPropertyUtil.IS.length()) && (returnTypeName.equals("boolean") || returnTypeName.equals("java.lang.Boolean"))) { return new ImplicitPropertyUtil.ImplicitPropertyInfo(false, true, ImplicitPropertyUtil.capitalizeFirstChar(name.substring(2))); } } else if (argCount == 1) { if (returnTypeName.equals("void") && name.startsWith(ImplicitPropertyUtil.SET) && (name.length() > ImplicitPropertyUtil.SET.length())) { return new ImplicitPropertyUtil.ImplicitPropertyInfo(true, false, ImplicitPropertyUtil.capitalizeFirstChar(name.substring(3))); } } } return null; } public static IJavaClassType resolveInnerClass(IJavaClassInfo rootType, String innerName, IJavaClassInfo whosAskin) { for (IJavaClassInfo innerCls : rootType.getDeclaredClasses()) { if (innerName.equals(innerCls.getSimpleName()) && isVisible(rootType, innerCls, whosAskin)) { return innerCls; } } return null; } private static boolean isVisible(IJavaClassInfo rootType, IJavaClassInfo innerClass, IJavaClassInfo whosAskin) { if ((whosAskin == rootType) || Modifier.isPublic(innerClass.getModifiers()) || (isEnclosed(rootType, whosAskin))) { return true; } if( innerClass.isProtected() && isDescendant(rootType, whosAskin) ) { return true; } if (whosAskin.getNamespace().equals(rootType.getNamespace()) && innerClass.isInternal()) { return true; } return false; } public static boolean isEnclosed(IJavaClassInfo enclosingClass, IJavaClassInfo nestedClass) { IJavaClassInfo enclosingType = nestedClass.getEnclosingClass(); if (enclosingType == null) { return false; } if (enclosingType == enclosingClass) { return true; } return isEnclosed(enclosingClass, enclosingType); } public static boolean isDescendant(IJavaClassInfo ancestorClassInfo, IJavaClassInfo descendantClassInfo) { IJavaClassInfo superclass = descendantClassInfo.getSuperclass(); if (superclass != null) { if (superclass == ancestorClassInfo) { return true; } if (isDescendant(ancestorClassInfo, superclass)) { return true; } } for (IJavaClassInfo iface : descendantClassInfo.getInterfaces()) { if (iface == ancestorClassInfo) { return true; } if (isDescendant(ancestorClassInfo, iface)) { return true; } } return false; } }