/* Copyright (C) 2004 - 2008 Versant Inc. http://www.db4o.com This file is part of the sharpen open source java to c# translator. sharpen is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation and as clarified by db4objects' GPL interpretation policy, available at http://www.db4o.com/about/company/legalpolicies/gplinterpretation/ Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street, Suite 350, San Mateo, CA 94403, USA. sharpen is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * Portions of this file were taken from Eclipse: * /org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/Bindings.java * and are subject to the CPL. * * Original copyright notice on file follows: * /******************************************************************************* * * Copyright (c) 2000, orporation and others. * * All rights reserved. This program and the accompanying materials * * are made available under the terms of the Common Public License v1.0 * * which accompanies this distribution, and is available at * * http://www.eclipse.org/legal/cpl-v10.html * * * * Contributors: * * IBM Corporation - initial API and implementation * * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for * * bug "inline method - doesn't handle implicit cast" (see * * https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941). * ******************************************************************************* */ package sharpen.core.framework; import static sharpen.core.framework.Environments.my; import org.eclipse.jdt.core.dom.*; import sharpen.core.Configuration; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; public class BindingUtils { /** * Finds the method in the given <code>type</code> that is overrideen by * the specified <code>method<code> . Returns <code>null</code> if no * such method exits. * * @param type * The type to search the method in * @param method * The specified method that would override the result * @return the method binding representing the method oevrriding the * specified <code>method<code> */ public static IMethodBinding findOverriddenMethodInType(ITypeBinding type, IMethodBinding method) { if (type.getName().equals("Object") && method.getName().equals("clone")) return null; for (Object o : type.getDeclaredMethods()) { IMethodBinding existing = (IMethodBinding) o; if (isSubsignature(method, existing)) return existing; } return null; } public static IMethodBinding findOverriddenMethodInTypeOrSuperclasses(ITypeBinding type, IMethodBinding method) { IMethodBinding found = findOverriddenMethodInType(type, method); if (null != found) { return found; } ITypeBinding superClass = type.getSuperclass(); if (null != superClass) { return findOverriddenMethodInTypeOrSuperclasses(superClass, method); } return null; } /** * Finds a method in the hierarchy of <code>type</code> that is * overridden by </code>binding</code>. Returns <code>null</code> if no * such method exists. First the super class is examined and than the * implemented interfaces. * * @param type * The type to search the method in * @param binding * The method that overrides * @return the method binding overridden the method */ public static IMethodBinding findOverriddenMethodInHierarchy(ITypeBinding type, IMethodBinding binding) { return findOverriddenMethodInHierarchy(type, binding, true); } public static IMethodBinding findOverriddenMethodInHierarchy(ITypeBinding type, IMethodBinding binding, boolean considerInterfaces) { final ITypeBinding superClass = type.getSuperclass(); if (superClass != null) { final IMethodBinding superClassMethod = findOverriddenMethodInHierarchy(superClass, binding); if (superClassMethod != null) return superClassMethod; } final IMethodBinding method = findOverriddenMethodInType(type, binding); if (method != null) return method; if (considerInterfaces) { final ITypeBinding[] interfaces = type.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { final IMethodBinding interfaceMethod = findOverriddenMethodInHierarchy(interfaces[i], binding); if (interfaceMethod != null) return interfaceMethod; } } return null; } /** * Finds the method that is defines the given method. The returned * method might not be visible. * * @param method * The method to find * @param typeResolver * TODO * @return the method binding representing the method */ public static IMethodBinding findMethodDefininition(IMethodBinding method, AST typeResolver) { if (null == method) { return null; } IMethodBinding definition = null; ITypeBinding type = method.getDeclaringClass(); ITypeBinding[] interfaces = type.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { IMethodBinding res = findOverriddenMethodInHierarchy(interfaces[i], method); if (res != null) { definition = res; // methods from interfaces are // always public and // therefore visible break; } } if (type.getSuperclass() != null) { IMethodBinding res = findOverriddenMethodInHierarchy(type.getSuperclass(), method); if (res != null && !Modifier.isPrivate(res.getModifiers())) { definition = res; } } else if (type.isInterface() && null != typeResolver) { IMethodBinding res = findOverriddenMethodInHierarchy( typeResolver.resolveWellKnownType("java.lang.Object"), method); if (res != null) { definition = res; } } IMethodBinding def = findMethodDefininition(definition, typeResolver); if (def != null) { return def; } return definition; } public static boolean isVisibleInHierarchy(IMethodBinding member, IPackageBinding pack) { int otherflags = member.getModifiers(); ITypeBinding declaringType = member.getDeclaringClass(); if (Modifier.isPublic(otherflags) || Modifier.isProtected(otherflags) || (declaringType != null && declaringType.isInterface())) { return true; } else if (Modifier.isPrivate(otherflags)) { return false; } return pack == declaringType.getPackage(); } public static String qualifiedName(IMethodBinding binding) { return qualifiedName(binding.getDeclaringClass()) + "." + binding.getName(); } public static String qualifiedSignature(IMethodBinding binding) { StringBuffer buf = new StringBuffer(); buf.append(qualifiedName(binding.getDeclaringClass())).append(".").append(binding.getName()) .append("("); ITypeBinding[] parameterTypes = binding.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { if (i != 0) buf.append(","); buf.append(qualifiedName(parameterTypes[i])); } buf.append(")"); return buf.toString(); } public static String typeMappingKey(final ITypeBinding type) { ITypeBinding[] typeArguments = type.getTypeArguments(); if (typeArguments.length == 0) typeArguments = type.getTypeParameters(); if (typeArguments.length > 0) { return qualifiedName(type) + "<" + repeat(',', typeArguments.length - 1) + ">"; } return qualifiedName(type); } private static String repeat(char c, int count) { StringBuilder builder = new StringBuilder(count); for (int i = 0; i < count; ++i) { builder.append(c); } return builder.toString(); } public static String qualifiedName(final ITypeBinding declaringClass) { String qn = declaringClass.getTypeDeclaration().getQualifiedName(); if (qn.length() > 0) return qn; else return declaringClass.getQualifiedName(); } public static String qualifiedName(IVariableBinding binding) { ITypeBinding declaringClass = binding.getDeclaringClass(); if (null == declaringClass) { return binding.getName(); } return qualifiedName(declaringClass) + "." + binding.getName(); } public static boolean isStatic(IMethodBinding binding) { return Modifier.isStatic(binding.getModifiers()); } public static boolean isStatic(IVariableBinding binding) { return Modifier.isStatic(binding.getModifiers()); } public static boolean isStatic(MethodInvocation invocation) { return isStatic(invocation.resolveMethodBinding()); } /** * * http://javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/eclipse/jdt/internal/corext/dom/Bindings.java.html * * Checks if the two bindings are equals. Also works across binding * environments. * * @param b1 * first binding treated as <code>this</code>. So it must * not be <code>null</code> * @param b2 * the second binding. * @return boolean */ public static boolean equals(IBinding b1, IBinding b2) { return b1.isEqualTo(b2); } /** * * http://javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/eclipse/jdt/internal/corext/dom/Bindings.java.html * * Checks if the two arrays of bindings have the same length and their * elements are equal. Uses * <code>Bindings.equals(IBinding, IBinding)</code> to compare. * * @param b1 * the first array of bindings. Must not be * <code>null</code>. * @param b2 * the second array of bindings. * @return boolean */ public static boolean equals(IBinding[] b1, IBinding[] b2) { if (b1 == b2) return true; if (b2 == null) return false; if (b1.length != b2.length) return false; for (int i = 0; i < b1.length; i++) { if (!equals(b1[i], b2[i])) return false; } return true; } /** * * http://javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/eclipse/jdt/internal/corext/dom/Bindings.java.html * */ private static boolean containsTypeVariables(ITypeBinding type) { if (type.isTypeVariable()) return true; if (type.isArray()) return containsTypeVariables(type.getElementType()); if (type.isCapture()) return containsTypeVariables(type.getWildcard()); if (type.isParameterizedType()) return containsTypeVariables(type.getTypeArguments()); if (type.isTypeVariable()) return containsTypeVariables(type.getTypeBounds()); if (type.isWildcardType() && type.getBound() != null) return containsTypeVariables(type.getBound()); return false; } /** * * http://javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/eclipse/jdt/internal/corext/dom/Bindings.java.html * */ private static boolean containsTypeVariables(ITypeBinding[] types) { for (int i = 0; i < types.length; i++) if (containsTypeVariables(types[i])) return true; return false; } /** * * http://javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/eclipse/jdt/internal/corext/dom/Bindings.java.html * */ private static Set getTypeBoundsForSubsignature(ITypeBinding typeParameter) { ITypeBinding[] typeBounds = typeParameter.getTypeBounds(); int count = typeBounds.length; if (count == 0) return Collections.EMPTY_SET; Set result = new HashSet(typeBounds.length); for (int i = 0; i < typeBounds.length; i++) { ITypeBinding bound = typeBounds[i]; if ("java.lang.Object".equals(typeBounds[0].getQualifiedName())) //$NON-NLS-1$ continue; else if (containsTypeVariables(bound)) result.add(bound.getErasure()); // try to // achieve // effect of // "rename type variables" else if (bound.isRawType()) result.add(bound.getTypeDeclaration()); else result.add(bound); } return result; } /** * * http://javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/eclipse/jdt/internal/corext/dom/Bindings.java.html * * @param overriding * overriding method (m1) * @param overridden * overridden method (m2) * @return <code>true</code> iff the method <code>m1</code> is a * subsignature of the method <code>m2</code>. This is one of * the requirements for m1 to override m2. Accessibility and * return types are not taken into account. Note that * subsignature is <em>not</em> symmetric! */ public static boolean isSubsignature(IMethodBinding overriding, IMethodBinding overridden) { // TODO: use IMethodBinding#isSubsignature(..) once it is tested // and fixed (only erasure of m1's parameter types, considering // type variable counts, doing type variable substitution if (!overriding.getName().equals(overridden.getName())) return false; ITypeBinding[] m1Params = overriding.getParameterTypes(); ITypeBinding[] m2Params = overridden.getParameterTypes(); if (m1Params.length != m2Params.length) return false; ITypeBinding[] m1TypeParams = overriding.getTypeParameters(); ITypeBinding[] m2TypeParams = overridden.getTypeParameters(); if (m1TypeParams.length != m2TypeParams.length && m1TypeParams.length != 0) // non-generic m1 // can override // a generic m2 return false; // m1TypeParameters.length == (m2TypeParameters.length || 0) if (m2TypeParams.length != 0) { // Note: this branch does not 100% adhere to the spec // and may report some false positives. // Full compliance would require major duplication of // compiler code. // Compare type parameter bounds: for (int i = 0; i < m1TypeParams.length; i++) { // loop over m1TypeParams, which is either // empty, or equally long as m2TypeParams Set m1Bounds = getTypeBoundsForSubsignature(m1TypeParams[i]); Set m2Bounds = getTypeBoundsForSubsignature(m2TypeParams[i]); if (!m1Bounds.equals(m2Bounds)) return false; } // Compare parameter types: if (equals(m2Params, m1Params)) return true; for (int i = 0; i < m1Params.length; i++) { ITypeBinding m1Param = m1Params[i]; if (containsTypeVariables(m1Param)) m1Param = m1Param.getErasure(); // try // to // achieve // effect // of // "rename type variables" else if (m1Param.isRawType()) m1Param = m1Param.getTypeDeclaration(); if (!(equals(m1Param, m2Params[i].getErasure()))) // can // erase // m2 return false; } return true; } else { // m1TypeParams.length == m2TypeParams.length == 0 if (equals(m1Params, m2Params)) return true; for (int i = 0; i < m1Params.length; i++) { ITypeBinding m1Param = m1Params[i]; if (m1Param.isRawType()) m1Param = m1Param.getTypeDeclaration(); if (!(equals(m1Param, m2Params[i].getErasure()))) // can // erase // m2 return false; } return true; } } public static boolean findInterfaceInClassHierarchy(ITypeBinding type, ITypeBinding binding) { while (binding != null) { if (binding.isInterface()) break; Set<ITypeBinding> ifaces = new LinkedHashSet<ITypeBinding>(); collectInterfaces(ifaces, binding); if (ifaces.contains(type)) return true; binding = binding.getSuperclass(); } return false; } public static boolean isValidCSInterface(ITypeBinding type) { if (type.getDeclaredFields().length != 0) return false; for (ITypeBinding ntype : type.getDeclaredTypes()) { if (!isExtractedNestedType(ntype)) return false; } return true; } private static boolean isExtractedNestedType(ITypeBinding type) { return my(Configuration.class).typeHasMapping(BindingUtils.typeMappingKey(type)); } private static boolean isNestedInInterface(ITypeBinding type) { while ((type = type.getDeclaringClass()) != null) { if (type.isInterface()) return true; } return false; } public static IMethodBinding getBaseMethod(IMethodBinding methodBinding, boolean overrideOnly, boolean allowStatic) { if (!allowStatic && Modifier.isStatic(methodBinding.getModifiers())) return null; ITypeBinding superclass = methodBinding.getDeclaringClass().getSuperclass(); if (null != superclass) { IMethodBinding result = BindingUtils.findOverriddenMethodInHierarchy(superclass, methodBinding); if (null != result) return result; } if (Modifier.isStatic(methodBinding.getModifiers())) return null; Set<ITypeBinding> baseInterfaces = new LinkedHashSet<ITypeBinding>(); collectInterfaces(baseInterfaces, methodBinding.getDeclaringClass()); for (final ITypeBinding iface : baseInterfaces) { if (!isNestedInInterface(iface) || !isValidCSInterface(iface)) { if (overrideOnly) continue; } // Base interface generated as a class IMethodBinding result = BindingUtils.findOverriddenMethodInType(iface, methodBinding); if (result != null) return result; } return null; } public static Set<ITypeBinding> getAllInterfaces(ITypeBinding binding) { Set<ITypeBinding> set = new LinkedHashSet<ITypeBinding>(); collectInterfaces(set, binding); return Collections.unmodifiableSet(set); } private static void collectInterfaces(Set<ITypeBinding> interfaceList, ITypeBinding binding) { ITypeBinding[] interfaces = binding.getInterfaces(); for (int i = 0; i < interfaces.length; ++i) { ITypeBinding interfaceBinding = interfaces[i]; if (interfaceList.contains(interfaceBinding)) { continue; } collectInterfaces(interfaceList, interfaceBinding); interfaceList.add(interfaceBinding); } } }