/* * Copyright 2013 Guidewire Software, Inc. */ package gw.compiler.ij.api; import gw.lang.reflect.IConstructorInfo; import gw.lang.reflect.IGenericMethodInfo; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IParameterInfo; import gw.lang.reflect.IPropertyInfo; import gw.lang.reflect.IRelativeTypeInfo; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeInfo; import gw.lang.reflect.Modifier; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.gs.IGosuEnhancement; 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.util.fingerprint.FP64; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; public class TypeFingerprint { private static int nullSafeCompare(String s1, String s2) { if (s1 == null) { return s2 == null ? 0 : 1; } if (s2 == null) { return -1; } return s1.compareTo(s2); } private static final Comparator<IType> TYPE_COMPARATOR = new Comparator<IType>() { @Override public int compare(IType type1, IType type2) { return nullSafeCompare(type1.getName(), type2.getName()); } }; private static final Comparator<IGosuClass> CLASS_COMPARATOR = new Comparator<IGosuClass>() { @Override public int compare(IGosuClass class1, IGosuClass class2) { return nullSafeCompare(class1.getName(), class2.getName()); } }; private static final Comparator<IMethodInfo> METHOD_COMPARATOR = new Comparator<IMethodInfo>() { public int compare(IMethodInfo method1, IMethodInfo method2) { int res = nullSafeCompare(method1.getName(), method2.getName()); if (res != 0) { return res; } res = compareParameters(method1.getParameters(), method2.getParameters()); if (res != 0) { return res; } res = nullSafeCompare(method1.getReturnType().getName(), method2.getReturnType().getName()); return res; } }; private static final Comparator<IJavaClassMethod> METHOD_INFO_COMPARATOR = new Comparator<IJavaClassMethod>() { public int compare(IJavaClassMethod method1, IJavaClassMethod method2) { int res = nullSafeCompare(method1.getName(), method2.getName()); if (res != 0) { return res; } res = compareParameters(method1.getParameterTypes(), method2.getParameterTypes()); if (res != 0) { return res; } res = nullSafeCompare(method1.getReturnType().getName(), method2.getReturnType().getName()); return res; } }; private static final Comparator<IConstructorInfo> CONSTRUCTOR_COMPARATOR = new Comparator<IConstructorInfo>() { public int compare(IConstructorInfo constructor1, IConstructorInfo constructor2) { int res = nullSafeCompare(constructor1.getName(), constructor2.getName()); if (res != 0) { return res; } res = compareParameters(constructor1.getParameters(), constructor2.getParameters()); return res; } }; private static final Comparator<IJavaClassConstructor> CONSTRUCTOR_INFO_COMPARATOR = new Comparator<IJavaClassConstructor>() { public int compare(IJavaClassConstructor constructor1, IJavaClassConstructor constructor2) { return compareParameters(constructor1.getParameterTypes(), constructor2.getParameterTypes()); } }; private static final Comparator<IPropertyInfo> PROPERTY_COMPARATOR = new Comparator<IPropertyInfo>() { public int compare(IPropertyInfo property1, IPropertyInfo property2) { int res = nullSafeCompare(property1.getName(), property2.getName()); if (res != 0) { return res; } res = nullSafeCompare(property1.getFeatureType().getName(), property2.getFeatureType().getName()); return res; } }; private static final Comparator<IJavaClassField> PROPERTY_INFO_COMPARATOR = new Comparator<IJavaClassField>() { public int compare(IJavaClassField property1, IJavaClassField property2) { int res = nullSafeCompare(property1.getName(), property2.getName()); if (res != 0) { return res; } res = nullSafeCompare(property1.getType().getName(), property2.getType().getName()); return res; } }; private static int compareParameters(IParameterInfo[] params1, IParameterInfo[] params2) { int res = Integer.compare(params1.length, params2.length); if (res != 0) { return res; } for (int i = 0; i < params1.length; i++) { res = nullSafeCompare(params1[i].getName(), params2[i].getName()); if (res != 0) { return res; } res = nullSafeCompare(params1[i].getFeatureType().getName(), params2[i].getFeatureType().getName()); if (res != 0) { return res; } } return 0; } private static int compareParameters(IJavaClassInfo[] params1, IJavaClassInfo[] params2) { int res = Integer.compare(params1.length, params2.length); if (res != 0) { return res; } for (int i = 0; i < params1.length; i++) { res = nullSafeCompare(params1[i].getName(), params2[i].getName()); if (res != 0) { return res; } } return 0; } public static FP64 get(IType type) { final FP64 fp = new FP64(); extend(fp, type); return fp; } public static void extend(FP64 fp, IType type) { // the package final String namespace = type.getNamespace(); if (namespace != null) { fp.extend(namespace); // we have to support java classes in the "default" package e.g., our own Launcher class } // the type name fp.extend(type.getName()); // the class modifiers final int modifiers = Modifier.setPublic(type.getModifiers(), false); fp.extend(modifiers); // the superclass declaration, if any final IType superType = type.getSupertype(); if (superType != null) { fp.extend(superType.getName()); } // the list of interfaces List<IType> ifaces = Arrays.asList(type.getInterfaces()); for (IType interfaceType : sort(ifaces, TYPE_COMPARATOR)) { fp.extend(interfaceType.getName()); } // generic type variables final IGenericTypeVariable[] typeVariables = type.getGenericTypeVariables(); if (typeVariables != null) { for (IGenericTypeVariable genericTypeVariable : typeVariables) { // the bounding type of the generic type variable fp.extend(genericTypeVariable.getBoundingType().getName()); } } // the enhanced type for enhancements if (type instanceof IGosuEnhancement) { final IType enhancedType = ((IGosuEnhancement) type).getEnhancedType(); if (enhancedType != null) { fp.extend(enhancedType.getName()); } } // non-private methods final ITypeInfo typeInfo = type.getTypeInfo(); final List<? extends IMethodInfo> methods = typeInfo instanceof IRelativeTypeInfo ? ((IRelativeTypeInfo) typeInfo).getMethods(type) : typeInfo.getMethods(); for (IMethodInfo method : sort(methods, METHOD_COMPARATOR)) { if (!method.isPrivate()) { // the method name if (method.getName() != null) { fp.extend(method.getName()); } // the return type fp.extend(method.getReturnType().getName()); // parameter types for (IParameterInfo parameter : method.getParameters()) { // method parameter type fp.extend(parameter.getFeatureType().getName()); } if (method instanceof IGenericMethodInfo) { for (IGenericTypeVariable typeVariable : ((IGenericMethodInfo) method).getTypeVariables()) { // the bounding type of the generic type variable fp.extend(typeVariable.getBoundingType().getName()); } } // modifiers fp.extend(Boolean.toString(method.isPublic())); fp.extend(Boolean.toString(method.isPrivate())); fp.extend(Boolean.toString(method.isProtected())); fp.extend(Boolean.toString(method.isInternal())); fp.extend(Boolean.toString(method.isFinal())); fp.extend(Boolean.toString(method.isStatic())); fp.extend(Boolean.toString(method.isHidden())); } } // non-private constructors final List<? extends IConstructorInfo> constructors = typeInfo instanceof IRelativeTypeInfo ? ((IRelativeTypeInfo) typeInfo).getConstructors(type) : typeInfo.getConstructors(); for (IConstructorInfo constructor : sort(constructors, CONSTRUCTOR_COMPARATOR)) { if (!constructor.isPrivate()) { for (IParameterInfo parameter : constructor.getParameters()) { // constructor parameter type fp.extend(parameter.getFeatureType().getName()); } // modifiers fp.extend(Boolean.toString(constructor.isPublic())); fp.extend(Boolean.toString(constructor.isPrivate())); fp.extend(Boolean.toString(constructor.isProtected())); fp.extend(Boolean.toString(constructor.isInternal())); fp.extend(Boolean.toString(constructor.isFinal())); fp.extend(Boolean.toString(constructor.isStatic())); // fp.extend(Boolean.toString(constructor.isHidden())); } } // non-private properties final List<? extends IPropertyInfo> properties = typeInfo instanceof IRelativeTypeInfo ? ((IRelativeTypeInfo) typeInfo).getProperties(type) : typeInfo.getProperties(); for (IPropertyInfo property : sort(properties, PROPERTY_COMPARATOR)) { if (!property.isPrivate()) { // the property name if (property.getName() != null) { fp.extend(property.getName()); } // the property type IType featureType = null; try { featureType = property.getFeatureType(); } catch (Throwable t) { //Ignore } if (featureType != null) { fp.extend(featureType.getName()); } // readability fp.extend(Boolean.toString(property.isReadable())); fp.extend(Boolean.toString(property.isWritable())); // modifiers fp.extend(Boolean.toString(property.isPublic())); fp.extend(Boolean.toString(property.isPrivate())); fp.extend(Boolean.toString(property.isProtected())); fp.extend(Boolean.toString(property.isInternal())); fp.extend(Boolean.toString(property.isFinal())); fp.extend(Boolean.toString(property.isStatic())); fp.extend(Boolean.toString(property.isHidden())); } } // non-private inner classes if (type instanceof IGosuClass) { final List<? extends IGosuClass> innerClasses = ((IGosuClass) type).getInnerClasses(); for (IGosuClass innerClass : sort(innerClasses, CLASS_COMPARATOR)) { if (!Modifier.isPrivate(innerClass.getModifiers()) && !innerClass.isAnonymous()) { fp.extend(TypeFingerprint.get(innerClass).getRawFingerprint()); } } } } public static void extend(FP64 fp, IJavaClassInfo type) { // the package final String namespace = type.getNamespace(); if (namespace != null) { fp.extend(namespace); // we have to support java classes in the "default" package e.g., our own Launcher class } // the type name fp.extend(type.getName()); // the class modifiers final int modifiers = Modifier.setPublic(type.getModifiers(), false); fp.extend(modifiers); // the superclass declaration, if any final IJavaClassInfo superType = type.getSuperclass(); if (superType != null) { fp.extend(superType.getName()); } // the list of interfaces List<IType> types = new ArrayList<IType>(type.getInterfaces().length); for (IType iType : types) { types.add(iType); } for (IType interfaceType : sort(types, TYPE_COMPARATOR)) { fp.extend(interfaceType.getName()); } // generic type variables final IJavaClassTypeVariable[] typeVariables = type.getTypeParameters(); if (typeVariables != null) { for (IJavaClassTypeVariable genericTypeVariable : typeVariables) { // the bounding type of the generic type variable IJavaClassType[] bounds = genericTypeVariable.getBounds(); for (IJavaClassType bound : bounds) { fp.extend(bound.getName()); } } } // the enhanced type for enhancements if (type instanceof IGosuEnhancement) { final IType enhancedType = ((IGosuEnhancement) type).getEnhancedType(); if (enhancedType != null) { fp.extend(enhancedType.getName()); } } // non-private methods final List<IJavaClassMethod> methods = Arrays.asList(type.getDeclaredMethods()); for (IJavaClassMethod method : sort(methods, METHOD_INFO_COMPARATOR)) { if (!Modifier.isPrivate(method.getModifiers())) { // the method name fp.extend(method.getName()); // the return type fp.extend(method.getReturnType().getName()); // parameter types for (IJavaClassInfo parameterType : method.getParameterTypes()) { // method parameter type fp.extend(parameterType.getName()); } if (method instanceof IGenericMethodInfo) { for (IGenericTypeVariable typeVariable : ((IGenericMethodInfo) method).getTypeVariables()) { // the bounding type of the generic type variable fp.extend(typeVariable.getBoundingType().getName()); } } // modifiers fp.extend(method.getModifiers()); } } // non-private constructors final List<IJavaClassConstructor> constructors = Arrays.asList(type.getDeclaredConstructors()); for (IJavaClassConstructor constructor : sort(constructors, CONSTRUCTOR_INFO_COMPARATOR)) { if (!Modifier.isPrivate(constructor.getModifiers())) { for (IJavaClassInfo parameterType : constructor.getParameterTypes()) { // constructor parameter type fp.extend(parameterType.getName()); } // modifiers fp.extend(constructor.getModifiers()); } } // non-private properties final List<IJavaClassField> properties = Arrays.asList(type.getDeclaredFields()); for (IJavaClassField property : sort(properties, PROPERTY_INFO_COMPARATOR)) { if (!Modifier.isPrivate(property.getModifiers())) { // the property name fp.extend(property.getName()); // the property type fp.extend(property.getType().getName()); // modifiers fp.extend(property.getModifiers()); } } // non-private inner classes if (type instanceof IGosuClass) { final List<? extends IGosuClass> innerClasses = ((IGosuClass) type).getInnerClasses(); for (IGosuClass innerClass : sort(innerClasses, CLASS_COMPARATOR)) { if (!Modifier.isPrivate(innerClass.getModifiers()) && !innerClass.isAnonymous()) { fp.extend(TypeFingerprint.get(innerClass).getRawFingerprint()); } } } } private static <T> List<T> sort(List<T> src, Comparator<? super T> comparator) { List<T> dst = new ArrayList(src); Collections.sort(dst, comparator); return dst; } }