/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.java2dart.util; import org.apache.commons.lang3.StringUtils; import org.eclipse.core.runtime.Assert; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.IPackageBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.TypeDeclaration; /** * Helper for JDT integration. */ public class JavaUtils { /** * The constant to return from {@link #getFullyQualifiedName(ITypeBinding, boolean)} when given * {@link ITypeBinding} is <code>null</code>, i.e. no type binding information found, for example * because of compilation errors. */ public static String NO_TYPE_BINDING_NAME = "__WBP_NO_TYPE_BINDING"; /** * The constant to return from {@link #getMethodSignature(IMethodBinding)} when given * {@link IMethodBinding} is <code>null</code>, i.e. no method binding information found, for * example because of compilation errors. */ public static String NO_METHOD_BINDING_SIGNATURE = "__WBP_NO_METHOD_BINDING"; /** * Returns the fully qualified name of given {@link ITypeBinding}, or * {@link #NO_TYPE_BINDING_NAME} if <code>null</code> binding given. * * @param binding the binding representing the type. * @param runtime flag <code>true</code> if we need name for class loading, <code>false</code> if * we need name for source generation. * @return the fully qualified name of given {@link ITypeBinding}, or * {@link #NO_TYPE_BINDING_NAME} . */ public static String getFullyQualifiedName(ITypeBinding binding, boolean runtime) { return getFullyQualifiedName(binding, runtime, false); } /** * @param binding the {@link ITypeBinding} to analyze. * @param runtime flag <code>true</code> if we need name for class loading, <code>false</code> if * we need name for source generation. * @param withGenerics flag <code>true</code> if generics type arguments should be appended. * @return the fully qualified name of given {@link ITypeBinding}, or * {@link #NO_TYPE_BINDING_NAME} . */ public static String getFullyQualifiedName(ITypeBinding binding, boolean runtime, boolean withGenerics) { // check if no binding if (binding == null) { return NO_TYPE_BINDING_NAME; } // check for primitive type if (binding.isPrimitive()) { return binding.getName(); } // array if (binding.isArray()) { StringBuilder sb = new StringBuilder(); // append element type qualified name ITypeBinding elementType = binding.getElementType(); String elementTypeQualifiedName = getFullyQualifiedName(elementType, runtime); sb.append(elementTypeQualifiedName); // append dimensions for (int i = 0; i < binding.getDimensions(); i++) { sb.append("[]"); } // done return sb.toString(); } // object { String scope; ITypeBinding declaringType = binding.getDeclaringClass(); if (declaringType == null) { IPackageBinding packageBinding = binding.getPackage(); if (packageBinding == null || packageBinding.isUnnamed()) { scope = ""; } else { scope = packageBinding.getName() + "."; } } else if (binding.isTypeVariable()) { return binding.getName(); } else { // use '$', because we use this class name for loading class scope = getFullyQualifiedName(declaringType, runtime); if (runtime) { scope += "$"; } else { scope += "."; } } // prepare "simple" name, without scope String jdtName = binding.getName(); String name = StringUtils.substringBefore(jdtName, "<"); if (withGenerics) { ITypeBinding[] typeArguments = binding.getTypeArguments(); if (typeArguments.length != 0) { StringBuilder sb = new StringBuilder(name); sb.append("<"); for (ITypeBinding typeArgument : typeArguments) { if (sb.charAt(sb.length() - 1) != '<') { sb.append(","); } String typeArgumentName = getFullyQualifiedName(typeArgument, runtime, withGenerics); sb.append(typeArgumentName); } sb.append(">"); name = sb.toString(); } } // qualified name is scope plus "simple" name return scope + name; } } /** * @return the JDT signature of described method. */ public static String getJdtMethodSignature(String className, String methodName, String parameterTypes[]) { StringBuilder parametersSignature = new StringBuilder(); for (String parameterType : parameterTypes) { parametersSignature.append(getJdtTypeName(parameterType)); } return getJdtTypeName(className) + "." + methodName + "(" + parametersSignature + ")"; } /** * @return the JDT method or field signature without return type. */ public static String getJdtSignature(IBinding binding) { if (binding != null) { String signature = binding.getKey(); return JavaUtils.getJdtSignature(signature); } return null; } /** * @return the JDT method or field signature without return type. */ public static String getJdtSignature(String signature) { int closeParenIndex = signature.indexOf(')'); if (closeParenIndex != -1) { // field if (!signature.contains("(")) { return signature.substring(0, closeParenIndex); } // method or parameter int parameterIndex = signature.indexOf('#'); // method if (parameterIndex == -1) { return signature.substring(0, closeParenIndex + 1); } // method parameter return signature.substring(0, closeParenIndex + 1) + signature.substring(parameterIndex); } return signature; } /** * @return the JDT signature type name for given "human" type name. */ public static String getJdtTypeName(String name) { if ("boolean".equals(name)) { return "Z"; } if ("byte".equals(name)) { return "B"; } if ("char".equals(name)) { return "C"; } if ("double".equals(name)) { return "D"; } if ("float".equals(name)) { return "F"; } if ("int".equals(name)) { return "I"; } if ("long".equals(name)) { return "J"; } if ("short".equals(name)) { return "S"; } if ("void".equals(name)) { return "V"; } return "L" + StringUtils.replace(name, ".", "/") + ";"; } /** * @return signature for given {@link IMethodBinding} with base-declaration types. */ public static String getMethodDeclarationSignature(IMethodBinding methodBinding) { if (methodBinding == null) { return NO_METHOD_BINDING_SIGNATURE; } IMethodBinding overriddenMethod = Bindings.findOverriddenMethod(methodBinding, true); if (overriddenMethod != null) { methodBinding = overriddenMethod; } return getMethodSignature(methodBinding.getMethodDeclaration(), true); } /** * @return signature for given {@link IMethodBinding} with generic type names. */ public static String getMethodGenericSignature(IMethodBinding methodBinding) { return getMethodSignature(methodBinding.getMethodDeclaration(), false); } public static IBinding getOriginalBinding(IBinding binding) { if (binding instanceof IMethodBinding) { IMethodBinding methodBinding = (IMethodBinding) binding; methodBinding = methodBinding.getMethodDeclaration(); while (true) { IMethodBinding overriddenMethod = Bindings.findOverriddenMethod(methodBinding, true); if (overriddenMethod == null) { break; } methodBinding = overriddenMethod; } return methodBinding; } if (binding instanceof IVariableBinding) { IVariableBinding varBinding = (IVariableBinding) binding; return varBinding.getVariableDeclaration(); } if (binding instanceof ITypeBinding) { ITypeBinding typeBinding = (ITypeBinding) binding; return typeBinding.getTypeDeclaration(); } return binding; } public static String getQualifiedName(ITypeBinding binding) { String name = binding.getQualifiedName(); if (name.contains("<")) { name = StringUtils.substringBefore(name, "<"); } return name; } /** * @return the JDT signature with changed name. */ public static String getRenamedJdtSignature(String signature, String newName) { // parameter { int parameterIndex = signature.indexOf('#'); if (parameterIndex != -1) { return signature.substring(0, parameterIndex + 1) + newName; } } // field or method int dotIndex = signature.indexOf('.'); Assert.isLegal(dotIndex != -1, "Cannot find '.' in " + signature); // method int openParenIndex = signature.indexOf('('); if (openParenIndex != -1) { return signature.substring(0, dotIndex + 1) + newName + signature.substring(openParenIndex); } // field return signature.substring(0, dotIndex + 1) + newName; } public static boolean isMethod(Object nodeBinding, String className, String methodName) { if (nodeBinding instanceof IMethodBinding) { IMethodBinding binding = (IMethodBinding) nodeBinding; return binding.getName().equals(methodName) && isMethodInClass(binding, className); } return false; } public static boolean isMethodDeclaredInClass(Object bindingObject, String reqClassName) { if (bindingObject instanceof IMethodBinding) { IMethodBinding binding = (IMethodBinding) bindingObject; binding = (IMethodBinding) getOriginalBinding(binding); return getQualifiedName(binding.getDeclaringClass()).equals(reqClassName); } return false; } /** * @return <code>true</code> if given {@link IMethodBinding} is method defined in the class with * required name. */ public static boolean isMethodInClass(Object bindingObject, String reqClassName) { if (bindingObject instanceof IMethodBinding) { IMethodBinding binding = (IMethodBinding) bindingObject; return isSubtype(binding.getDeclaringClass(), reqClassName); } return false; } /** * Checks if the given {@link BodyDeclaration} is declared as "package private". */ public static boolean isPackagePrivate(BodyDeclaration node) { return !isPublic(node) && !isProtected(node) && !isPrivate(node); } /** * Checks if the given {@link BodyDeclaration} is declared as "private". */ public static boolean isPrivate(BodyDeclaration node) { return Modifier.isPrivate(node.getModifiers()); } /** * Checks if the given {@link BodyDeclaration} is declared as "protected". */ public static boolean isProtected(BodyDeclaration node) { return Modifier.isProtected(node.getModifiers()); } /** * Checks if the given {@link BodyDeclaration} is declared as "public" or is implicitly public * because if declared in an interface. */ public static boolean isPublic(BodyDeclaration node) { if (Modifier.isPublic(node.getModifiers())) { return true; } if (node.getParent() instanceof TypeDeclaration) { TypeDeclaration typeDeclaration = (TypeDeclaration) node.getParent(); return typeDeclaration.isInterface(); } return false; } public static boolean isStatic(IBinding binding) { return Modifier.isStatic(binding.getModifiers()); } public static boolean isStaticFieldBinding(Object binding) { if (binding instanceof IVariableBinding) { IVariableBinding fieldBinding = (IVariableBinding) binding; return fieldBinding.isField() && isStatic(fieldBinding); } return false; } public static boolean isSubtype(ITypeBinding binding, ITypeBinding superBinding) { if (binding != null) { if (binding == superBinding) { return true; } for (ITypeBinding intf : binding.getInterfaces()) { if (isSubtype(intf, superBinding)) { return true; } } if (isSubtype(binding.getSuperclass(), superBinding)) { return true; } } return false; } public static boolean isSubtype(ITypeBinding binding, String reqClassName) { if (binding != null) { if (getQualifiedName(binding).equals(reqClassName)) { return true; } for (ITypeBinding intf : binding.getInterfaces()) { if (isSubtype(intf, reqClassName)) { return true; } } if (isSubtype(binding.getSuperclass(), reqClassName)) { return true; } } return false; } public static boolean isTypeNamed(Object binding, String reqName) { if (binding instanceof ITypeBinding) { ITypeBinding typeBinding = (ITypeBinding) binding; return reqName.equals(getQualifiedName(typeBinding)); } return false; } /** * @param methodBinding the method binding. * @param declaration set <code>true</code> if need type variables replaced with a base types. * @return signature for given {@link IMethodBinding}. */ private static String getMethodSignature(IMethodBinding methodBinding, boolean declaration) { // check if no binding if (methodBinding == null) { return NO_METHOD_BINDING_SIGNATURE; } // signature StringBuilder buffer = new StringBuilder(); // name if (methodBinding.isConstructor()) { buffer.append("<init>"); } else { buffer.append(methodBinding.getName()); } // parameters buffer.append('('); { ITypeBinding[] parameterTypes = methodBinding.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { ITypeBinding parameterType = parameterTypes[i]; if (i != 0) { buffer.append(','); } if (declaration && parameterType.isTypeVariable()) { ITypeBinding variableBinding = getTypeVariableBound(parameterType); if (variableBinding == null) { buffer.append("java.lang.Object"); } else { buffer.append(getFullyQualifiedName(variableBinding, false)); } } else { buffer.append(getFullyQualifiedName(parameterType, false)); } } } buffer.append(')'); // return result return buffer.toString(); } /** * @param typeBinding the {@link ITypeBinding} of type variable. * @return the declared type bounds, may be <code>null</code> if not specified. */ private static ITypeBinding getTypeVariableBound(ITypeBinding typeBinding) { Assert.isLegal(typeBinding.isTypeVariable()); ITypeBinding[] typeBounds = typeBinding.getTypeBounds(); if (typeBounds.length != 0) { return typeBounds[0]; } else { return null; } } }