package com.google.dart.java2dart.util;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
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.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Copy of "org.eclipse.jdt.internal.corext.dom.Bindings" from JDT UI.
*/
public class Bindings {
public static final String ARRAY_LENGTH_FIELD_BINDING_STRING = "(array type):length";//$NON-NLS-1$
/**
* @param overridden the overridden method
* @param overridable the overriding method
* @return returns <code>true</code> if the overriding method overrrides the overridden
* @deprecated Need to review: Use {@link #isSubsignature(IMethodBinding, IMethodBinding)} if the
* two bindings are in the same hierarchy (directly overrides each other), or
* {@link #findMethodInHierarchy(ITypeBinding, String, ITypeBinding[])} else.
*/
@Deprecated
public static boolean areOverriddenMethods(IMethodBinding overridden, IMethodBinding overridable) {
if (!overridden.getName().equals(overridable.getName())) {
return false;
}
return areSubTypeCompatible(overridden, overridable);
}
/**
* Note: this method is for debugging and testing purposes only. There are tests whose
* pre-computed test results rely on the returned String's format.
*
* @param binding the binding
* @return a string representation of given binding
* @see org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider
*/
public static String asString(IBinding binding) {
if (binding instanceof IMethodBinding) {
return asString((IMethodBinding) binding);
} else if (binding instanceof ITypeBinding) {
return ((ITypeBinding) binding).getQualifiedName();
} else if (binding instanceof IVariableBinding) {
return asString((IVariableBinding) binding);
}
return binding.toString();
}
/**
* @param candidates the candidates
* @param overridable the overriding method
* @return returns <code>true></code> if the overriding method overrides a candidate
* @deprecated Need to review: Use {@link #isSubsignature(IMethodBinding, IMethodBinding)} if the
* two bindings are in the same hierarchy (directly overrides each other), or
* {@link #findMethodInHierarchy(ITypeBinding, String, ITypeBinding[])} else.
*/
@Deprecated
public static boolean containsSignatureEquivalentConstructor(IMethodBinding[] candidates,
IMethodBinding overridable) {
for (int index = 0; index < candidates.length; index++) {
if (isSignatureEquivalentConstructor(candidates[index], overridable)) {
return true;
}
}
return false;
}
/**
* Checks if the declarations of two bindings are equals. Also works across binding environments.
*
* @param b1 first binding, must not be <code>null</code>
* @param b2 second binding, must not be <code>null</code>
* @return boolean
*/
public static boolean equalDeclarations(IBinding b1, IBinding b2) {
if (b1.getKind() != b2.getKind()) {
return false;
}
return getDeclaration(b1).isEqualTo(getDeclaration(b2));
}
/**
* 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);
}
/**
* 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) {
Assert.isNotNull(b1);
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 (!Bindings.equals(b1[i], b2[i])) {
return false;
}
}
return true;
}
/**
* Finds the compilation unit where the type of the given <code>ITypeBinding</code> is defined,
* using the class path defined by the given Java project. Returns <code>null</code> if no
* compilation unit is found (e.g. type binding is from a binary type)
*
* @param typeBinding the type binding to search for
* @param project the project used as a scope
* @return the compilation unit containing the type
* @throws JavaModelException if an errors occurs in the Java model
*/
public static ICompilationUnit findCompilationUnit(ITypeBinding typeBinding, IJavaProject project)
throws JavaModelException {
IJavaElement type = typeBinding.getJavaElement();
if (type instanceof IType) {
return ((IType) type).getCompilationUnit();
} else {
return null;
}
}
/**
* Finds the field specified by <code>fieldName</code> in the type hierarchy denoted by the given
* type. Returns <code>null</code> if no such field exists. If the field is defined in more than
* one super type only the first match is returned. First the super class is examined and then the
* implemented interfaces.
*
* @param type The type to search the field in
* @param fieldName The name of the field to find
* @return the variable binding representing the field
*/
public static IVariableBinding findFieldInHierarchy(ITypeBinding type, String fieldName) {
IVariableBinding field = findFieldInType(type, fieldName);
if (field != null) {
return field;
}
ITypeBinding superClass = type.getSuperclass();
if (superClass != null) {
field = findFieldInHierarchy(superClass, fieldName);
if (field != null) {
return field;
}
}
ITypeBinding[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
field = findFieldInHierarchy(interfaces[i], fieldName);
if (field != null) {
return field;
}
}
return null;
}
/**
* Finds the field specified by <code>fieldName<code> in
* the given <code>type</code>. Returns <code>null</code> if no such field exits.
*
* @param type the type to search the field in
* @param fieldName the field name
* @return the binding representing the field or <code>null</code>
*/
public static IVariableBinding findFieldInType(ITypeBinding type, String fieldName) {
if (type.isPrimitive()) {
return null;
}
IVariableBinding[] fields = type.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
IVariableBinding field = fields[i];
if (field.getName().equals(fieldName)) {
return field;
}
}
return null;
}
/**
* Finds a method for the given <code>IMethodBinding</code>. Returns <code>null</code> if the type
* doesn't contain a corresponding method.
*
* @param method the method to find
* @param type the type to look in
* @return the corresponding IMethod or <code>null</code>
* @throws JavaModelException if an error occurs in the Java model
* @deprecated Use {@link #findMethodInHierarchy(ITypeBinding, String, String[])} or
* {@link JavaModelUtil}
*/
@Deprecated
public static IMethod findMethod(IMethodBinding method, IType type) throws JavaModelException {
method = method.getMethodDeclaration();
IMethod[] candidates = type.getMethods();
for (int i = 0; i < candidates.length; i++) {
IMethod candidate = candidates[i];
if (candidate.getElementName().equals(method.getName()) && sameParameters(method, candidate)) {
return candidate;
}
}
return null;
}
/**
* Finds the method specified by <code>methodName</code> and </code>parameters</code> in the type
* hierarchy denoted by the given type. Returns <code>null</code> if no such method exists. If the
* method is defined in more than one super type only the first match is returned. First the super
* class is examined and then the implemented interfaces.
*
* @param type The type to search the method in
* @param methodName The name of the method to find
* @param parameters The parameter types of the method to find. If <code>null</code> is passed,
* only the name is matched and parameters are ignored.
* @return the method binding representing the method
*/
public static IMethodBinding findMethodInHierarchy(ITypeBinding type, String methodName,
ITypeBinding[] parameters) {
IMethodBinding method = findMethodInType(type, methodName, parameters);
if (method != null) {
return method;
}
ITypeBinding superClass = type.getSuperclass();
if (superClass != null) {
method = findMethodInHierarchy(superClass, methodName, parameters);
if (method != null) {
return method;
}
}
ITypeBinding[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
method = findMethodInHierarchy(interfaces[i], methodName, parameters);
if (method != null) {
return method;
}
}
return null;
}
/**
* Finds the method specified by <code>methodName</code> and </code>parameters</code> in the type
* hierarchy denoted by the given type. Returns <code>null</code> if no such method exists. If the
* method is defined in more than one super type only the first match is returned. First the super
* class is examined and then the implemented interfaces.
*
* @param type the type to search the method in
* @param methodName The name of the method to find
* @param parameters The parameter types of the method to find. If <code>null</code> is passed,
* only the name is matched and parameters are ignored.
* @return the method binding representing the method
*/
public static IMethodBinding findMethodInHierarchy(ITypeBinding type, String methodName,
String[] parameters) {
IMethodBinding method = findMethodInType(type, methodName, parameters);
if (method != null) {
return method;
}
ITypeBinding superClass = type.getSuperclass();
if (superClass != null) {
method = findMethodInHierarchy(superClass, methodName, parameters);
if (method != null) {
return method;
}
}
ITypeBinding[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
method = findMethodInHierarchy(interfaces[i], methodName, parameters);
if (method != null) {
return method;
}
}
return null;
}
/**
* Finds the method specified by <code>methodName<code> and </code>parameters</code> in the given
* <code>type</code>. Returns <code>null</code> if no such method exits.
*
* @param type The type to search the method in
* @param methodName The name of the method to find
* @param parameters The parameter types of the method to find. If <code>null</code> is passed,
* only the name is matched and parameters are ignored.
* @return the method binding representing the method
*/
public static IMethodBinding findMethodInType(ITypeBinding type, String methodName,
ITypeBinding[] parameters) {
if (type.isPrimitive()) {
return null;
}
IMethodBinding[] methods = type.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (parameters == null) {
if (methodName.equals(methods[i].getName())) {
return methods[i];
}
} else {
if (isEqualMethod(methods[i], methodName, parameters)) {
return methods[i];
}
}
}
return null;
}
/**
* Finds the method specified by <code>methodName<code> and </code>parameters</code> in the given
* <code>type</code>. Returns <code>null</code> if no such method exits.
*
* @param type The type to search the method in
* @param methodName The name of the method to find
* @param parameters The parameter types of the method to find. If <code>null</code> is passed,
* only the name is matched and parameters are ignored.
* @return the method binding representing the method
*/
public static IMethodBinding findMethodInType(ITypeBinding type, String methodName,
String[] parameters) {
if (type.isPrimitive()) {
return null;
}
IMethodBinding[] methods = type.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (parameters == null) {
if (methodName.equals(methods[i].getName())) {
return methods[i];
}
} else {
if (isEqualMethod(methods[i], methodName, parameters)) {
return methods[i];
}
}
}
return null;
}
/**
* Finds the method that is overridden by the given method. The search is bottom-up, so this
* returns the nearest defining/declaring method.
*
* @param overriding overriding method
* @param testVisibility If true the result is tested on visibility. Null is returned if the
* method is not visible.
* @return the method binding representing the method
*/
public static IMethodBinding findOverriddenMethod(IMethodBinding overriding,
boolean testVisibility) {
int modifiers = overriding.getModifiers();
if (Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || overriding.isConstructor()) {
return null;
}
ITypeBinding type = overriding.getDeclaringClass();
if (type.getSuperclass() != null) {
IMethodBinding res = findOverriddenMethodInHierarchy(type.getSuperclass(), overriding);
if (res != null && !Modifier.isPrivate(res.getModifiers())) {
if (!testVisibility
|| isVisibleInHierarchy(res, overriding.getDeclaringClass().getPackage())) {
return res;
}
}
}
ITypeBinding[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
IMethodBinding res = findOverriddenMethodInHierarchy(interfaces[i], overriding);
if (res != null) {
return res; // methods from interfaces are always public and therefore visible
}
}
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. If the method is
* defined in more than one super type only the first match is returned. First the super class is
* examined and then 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) {
IMethodBinding method = findOverriddenMethodInType(type, binding);
if (method != null) {
return method;
}
ITypeBinding superClass = type.getSuperclass();
if (superClass != null) {
method = findOverriddenMethodInHierarchy(superClass, binding);
if (method != null) {
return method;
}
}
ITypeBinding[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
method = findOverriddenMethodInHierarchy(interfaces[i], binding);
if (method != null) {
return method;
}
}
return null;
}
/**
* Finds the method in the given <code>type</code> that is overridden 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 of the method that is overridden by the specified
* <code>method<code>, or <code>null</code>
*/
public static IMethodBinding findOverriddenMethodInType(ITypeBinding type, IMethodBinding method) {
IMethodBinding[] methods = type.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (isSubsignature(method, methods[i])) {
return methods[i];
}
}
return null;
}
/**
* Finds a type binding for a given fully qualified type in the hierarchy of a type. Returns
* <code>null</code> if no type binding is found.
*
* @param hierarchyType the binding representing the hierarchy
* @param fullyQualifiedTypeName the fully qualified name to search for
* @return the type binding
*/
public static ITypeBinding findTypeInHierarchy(ITypeBinding hierarchyType,
String fullyQualifiedTypeName) {
if (hierarchyType.isArray() || hierarchyType.isPrimitive()) {
return null;
}
if (fullyQualifiedTypeName.equals(hierarchyType.getQualifiedName())) {
return hierarchyType;
}
ITypeBinding superClass = hierarchyType.getSuperclass();
if (superClass != null) {
ITypeBinding res = findTypeInHierarchy(superClass, fullyQualifiedTypeName);
if (res != null) {
return res;
}
}
ITypeBinding[] superInterfaces = hierarchyType.getInterfaces();
for (int i = 0; i < superInterfaces.length; i++) {
ITypeBinding res = findTypeInHierarchy(superInterfaces[i], fullyQualifiedTypeName);
if (res != null) {
return res;
}
}
return null;
}
public static String[] getAllNameComponents(ITypeBinding type) {
List<String> result = new ArrayList<String>(5);
createName(type, true, result);
return result.toArray(new String[result.size()]);
}
/**
* Returns all super types (classes and interfaces) for the given type.
*
* @param type The type to get the supertypes of.
* @return all super types (excluding <code>type</code>)
*/
public static ITypeBinding[] getAllSuperTypes(ITypeBinding type) {
Set<ITypeBinding> result = new HashSet<ITypeBinding>();
collectSuperTypes(type, result);
result.remove(type);
return result.toArray(new ITypeBinding[result.size()]);
}
/**
* Returns the binding of the variable written in an Assignment.
*
* @param assignment The assignment
* @return The binding or <code>null</code> if no bindings are available.
*/
public static IVariableBinding getAssignedVariable(Assignment assignment) {
Expression leftHand = assignment.getLeftHandSide();
switch (leftHand.getNodeType()) {
case ASTNode.SIMPLE_NAME:
return (IVariableBinding) ((SimpleName) leftHand).resolveBinding();
case ASTNode.QUALIFIED_NAME:
return (IVariableBinding) ((QualifiedName) leftHand).getName().resolveBinding();
case ASTNode.FIELD_ACCESS:
return ((FieldAccess) leftHand).resolveFieldBinding();
case ASTNode.SUPER_FIELD_ACCESS:
return ((SuperFieldAccess) leftHand).resolveFieldBinding();
default:
return null;
}
}
/**
* Returns the type binding of the node's enclosing type declaration.
*
* @param node an AST node
* @return the type binding of the node's parent type declaration, or <code>null</code>
*/
public static ITypeBinding getBindingOfParentType(ASTNode node) {
while (node != null) {
if (node instanceof AbstractTypeDeclaration) {
return ((AbstractTypeDeclaration) node).resolveBinding();
} else if (node instanceof AnonymousClassDeclaration) {
return ((AnonymousClassDeclaration) node).resolveBinding();
}
node = node.getParent();
}
return null;
}
/**
* Returns the type binding of the node's type context or null if the node is inside an
* annotation, type parameter, super type declaration, or Javadoc of a top level type. The result
* of this method is equal to the result of {@link #getBindingOfParentType(ASTNode)} for nodes in
* the type's body.
*
* @param node an AST node
* @return the type binding of the node's parent type context, or <code>null</code>
*/
public static ITypeBinding getBindingOfParentTypeContext(ASTNode node) {
StructuralPropertyDescriptor lastLocation = null;
while (node != null) {
if (node instanceof AbstractTypeDeclaration) {
AbstractTypeDeclaration decl = (AbstractTypeDeclaration) node;
if (lastLocation == decl.getBodyDeclarationsProperty()
|| lastLocation == decl.getJavadocProperty()) {
return decl.resolveBinding();
} else if (decl instanceof EnumDeclaration
&& lastLocation == EnumDeclaration.ENUM_CONSTANTS_PROPERTY) {
return decl.resolveBinding();
}
} else if (node instanceof AnonymousClassDeclaration) {
return ((AnonymousClassDeclaration) node).resolveBinding();
}
lastLocation = node.getLocationInParent();
node = node.getParent();
}
return null;
}
/**
* Returns the boxed type binding according to JLS3 5.1.7, or the original binding if the given
* type is not a primitive type.
*
* @param type a type binding
* @param ast an AST to resolve the boxed type
* @return the boxed type, or the original type if no boxed type found
*/
public static ITypeBinding getBoxedTypeBinding(ITypeBinding type, AST ast) {
if (!type.isPrimitive()) {
return type;
}
String boxedTypeName = getBoxedTypeName(type.getName());
if (boxedTypeName == null) {
return type;
}
ITypeBinding boxed = ast.resolveWellKnownType(boxedTypeName);
if (boxed == null) {
return type;
}
return boxed;
}
public static IBinding getDeclaration(IBinding binding) {
if (binding == null) {
return null;
}
switch (binding.getKind()) {
case IBinding.TYPE:
return ((ITypeBinding) binding).getTypeDeclaration();
case IBinding.VARIABLE:
return ((IVariableBinding) binding).getVariableDeclaration();
case IBinding.METHOD:
return ((IMethodBinding) binding).getMethodDeclaration();
}
return binding;
}
/**
* Returns the fully qualified name of the specified type binding.
* <p>
* If the binding resolves to a generic type, the fully qualified name of the raw type is
* returned.
*
* @param type the type binding to get its fully qualified name
* @return the fully qualified name
*/
public static String getFullyQualifiedName(ITypeBinding type) {
String name = type.getQualifiedName();
final int index = name.indexOf('<');
if (index > 0) {
name = name.substring(0, index);
}
return name;
}
// public static String getImportName(IBinding binding) {
// ITypeBinding declaring = null;
// switch (binding.getKind()) {
// case IBinding.TYPE:
// return getRawQualifiedName((ITypeBinding) binding);
// case IBinding.PACKAGE:
// return binding.getName() + ".*"; //$NON-NLS-1$
// case IBinding.METHOD:
// declaring = ((IMethodBinding) binding).getDeclaringClass();
// break;
// case IBinding.VARIABLE:
// declaring = ((IVariableBinding) binding).getDeclaringClass();
// if (declaring == null) {
// return binding.getName(); // array.length
// }
//
// break;
// default:
// return binding.getName();
// }
// return JavaModelUtil.concatenateName(getRawQualifiedName(declaring), binding.getName());
// }
public static String[] getNameComponents(ITypeBinding type) {
List<String> result = new ArrayList<String>(5);
createName(type, false, result);
return result.toArray(new String[result.size()]);
}
public static String getRawName(ITypeBinding binding) {
String name = binding.getName();
if (binding.isParameterizedType() || binding.isGenericType()) {
int idx = name.indexOf('<');
if (idx != -1) {
return name.substring(0, idx);
}
}
return name;
}
public static String getRawQualifiedName(ITypeBinding binding) {
final String EMPTY = ""; //$NON-NLS-1$
if (binding.isAnonymous() || binding.isLocal()) {
return EMPTY;
}
if (binding.isPrimitive() || binding.isNullType() || binding.isTypeVariable()) {
return binding.getName();
}
if (binding.isArray()) {
String elementTypeQualifiedName = getRawQualifiedName(binding.getElementType());
if (elementTypeQualifiedName.length() != 0) {
StringBuffer stringBuffer = new StringBuffer(elementTypeQualifiedName);
stringBuffer.append('[').append(']');
return stringBuffer.toString();
} else {
return EMPTY;
}
}
if (binding.isMember()) {
String outerName = getRawQualifiedName(binding.getDeclaringClass());
if (outerName.length() > 0) {
StringBuffer buffer = new StringBuffer();
buffer.append(outerName);
buffer.append('.');
buffer.append(getRawName(binding));
return buffer.toString();
} else {
return EMPTY;
}
} else if (binding.isTopLevel()) {
IPackageBinding packageBinding = binding.getPackage();
StringBuffer buffer = new StringBuffer();
if (packageBinding != null && packageBinding.getName().length() > 0) {
buffer.append(packageBinding.getName()).append('.');
}
buffer.append(getRawName(binding));
return buffer.toString();
}
return EMPTY;
}
public static ITypeBinding getTopLevelType(ITypeBinding type) {
ITypeBinding parent = type.getDeclaringClass();
while (parent != null) {
type = parent;
parent = type.getDeclaringClass();
}
return type;
}
public static String getTypeQualifiedName(ITypeBinding type) {
List<String> result = new ArrayList<String>(5);
createName(type, false, result);
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < result.size(); i++) {
if (i > 0) {
buffer.append('.');
}
buffer.append(result.get(i));
}
return buffer.toString();
}
/**
* Returns the unboxed type binding according to JLS3 5.1.7, or the original binding if the given
* type is not a boxed type.
*
* @param type a type binding
* @param ast an AST to resolve the unboxed type
* @return the unboxed type, or the original type if no unboxed type found
*/
public static ITypeBinding getUnboxedTypeBinding(ITypeBinding type, AST ast) {
if (!type.isClass()) {
return type;
}
String unboxedTypeName = getUnboxedTypeName(type.getQualifiedName());
if (unboxedTypeName == null) {
return type;
}
ITypeBinding unboxed = ast.resolveWellKnownType(unboxedTypeName);
if (unboxed == null) {
return type;
}
return unboxed;
}
public static int hashCode(IBinding binding) {
Assert.isNotNull(binding);
String key = binding.getKey();
if (key == null) {
return binding.hashCode();
}
return key.hashCode();
}
/**
* Tests if the given node is a declaration, not a instance of a generic type, method or field.
* Declarations can be found in AST with CompilationUnit.findDeclaringNode
*
* @param binding binding to test
* @return returns <code>true</code> if the binding is a declaration binding
*/
public static boolean isDeclarationBinding(IBinding binding) {
switch (binding.getKind()) {
case IBinding.TYPE:
return ((ITypeBinding) binding).getTypeDeclaration() == binding;
case IBinding.VARIABLE:
return ((IVariableBinding) binding).getVariableDeclaration() == binding;
case IBinding.METHOD:
return ((IMethodBinding) binding).getMethodDeclaration() == binding;
}
return true;
}
/**
* Tests whether the two methods are erasure-equivalent.
*
* @param method the first method
* @param methodName the name of the second method
* @param parameters the parameters of the second parameters
* @return return <code>true</code> if the two bindings are equal
* @deprecated use {@link #isSubsignature(IMethodBinding, IMethodBinding)}
*/
//TODO: rename to isErasureEquivalentMethod and change to two IMethodBinding parameters
@Deprecated
public static boolean isEqualMethod(IMethodBinding method, String methodName,
ITypeBinding[] parameters) {
if (!method.getName().equals(methodName)) {
return false;
}
ITypeBinding[] methodParameters = method.getParameterTypes();
if (methodParameters.length != parameters.length) {
return false;
}
for (int i = 0; i < parameters.length; i++) {
if (!equals(methodParameters[i].getErasure(), parameters[i].getErasure())) {
return false;
}
}
//Can't use this fix, since some clients assume that this method tests erasure equivalence:
// if (method.getTypeParameters().length == 0) {
// //a method without type parameters cannot be overridden by one that declares type parameters -> can be exact here
// for (int i= 0; i < parameters.length; i++) {
// if ( ! (equals(methodParameters[i], parameters[i])
// || equals(methodParameters[i].getErasure(), parameters[i]))) // subsignature
// return false;
// }
// } else {
// //this will find all overridden methods, but may generate false positives in some cases:
// for (int i= 0; i < parameters.length; i++) {
// if (!equals(methodParameters[i].getErasure(), parameters[i].getErasure()))
// return false;
// }
// }
return true;
}
/**
* Checks whether a method with the given name and parameter types is a subsignature of the given
* method binding.
*
* @param method a method
* @param methodName method name to match
* @param parameters the parameter types of the method to find. If <code>null</code> is passed,
* only the name is matched and parameters are ignored.
* @return <code>true</code> iff the method m1 (with name <code>methodName</code> and method
* parameters <code>parameters</code>) is a subsignature of the method <code>m2</code>.
* Accessibility and return types are not taken into account.
*/
public static boolean isEqualMethod(IMethodBinding method, String methodName, String[] parameters) {
if (!method.getName().equals(methodName)) {
return false;
}
ITypeBinding[] methodParameters = method.getParameterTypes();
if (methodParameters.length != parameters.length) {
return false;
}
String first, second;
int index;
for (int i = 0; i < parameters.length; i++) {
first = parameters[i];
index = first.indexOf('<');
if (index > 0) {
first = first.substring(0, index);
}
second = methodParameters[i].getErasure().getQualifiedName();
index = second.indexOf('<');
if (index > 0) {
second = second.substring(0, index);
}
if (!first.equals(second)) {
return false;
}
}
return true;
}
/**
* Checks whether the passed type binding is a runtime exception.
*
* @param thrownException the type binding
* @return <code>true</code> if the passed type binding is a runtime exception; otherwise
* <code>false</code> is returned
*/
public static boolean isRuntimeException(ITypeBinding thrownException) {
if (thrownException == null || thrownException.isPrimitive() || thrownException.isArray()) {
return false;
}
return findTypeInHierarchy(thrownException, "java.lang.RuntimeException") != null; //$NON-NLS-1$
}
/**
* @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) {
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<ITypeBinding> m1Bounds = getTypeBoundsForSubsignature(m1TypeParams[i]);
Set<ITypeBinding> 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];
ITypeBinding m2Param = m2Params[i];
if (containsTypeVariables(m1Param) || m1Param.isRawType()) {
m1Param = m1Param.getErasure(); // try to achieve effect of "rename type variables"
}
if (!(equals(m1Param, m2Param) || equals(m1Param, m2Param.getErasure()))) {
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];
ITypeBinding m2Param = m2Params[i];
if (m1Param.isRawType()) {
m1Param = m1Param.getTypeDeclaration();
}
if (!(equals(m1Param, m2Param) || equals(m1Param, m2Param.getErasure()))) {
return false;
}
}
return true;
}
}
/**
* Returns <code>true</code> if the given type is a super type of a candidate. <code>true</code>
* is returned if the two type bindings are identical.
* <p>
* <b>Warning:</b> With the addition of generics, this method is valid in less cases than before.
* Consider using {@link TypeRules#canAssign(ITypeBinding, ITypeBinding)} if you're dealing with
* types of variables. The classical notion of supertypes only makes sense if you really need to
* walk the type hierarchy but don't need to play the assignment rules.
* </p>
*
* @param possibleSuperType the type to inspect
* @param type the type whose super types are looked at
* @return <code>true</code> iff <code>possibleSuperType</code> is a super type of
* <code>type</code> or is equal to it
*/
public static boolean isSuperType(ITypeBinding possibleSuperType, ITypeBinding type) {
return isSuperType(possibleSuperType, type, true);
}
/**
* Returns <code>true</code> if the given type is a super type of a candidate. <code>true</code>
* is returned if the two type bindings are identical (TODO)
*
* @param possibleSuperType the type to inspect
* @param type the type whose super types are looked at
* @param considerTypeArguments if <code>true</code>, consider type arguments of <code>type</code>
* @return <code>true</code> iff <code>possibleSuperType</code> is a super type of
* <code>type</code> or is equal to it
*/
public static boolean isSuperType(ITypeBinding possibleSuperType, ITypeBinding type,
boolean considerTypeArguments) {
if (type.isArray() || type.isPrimitive()) {
return false;
}
if (!considerTypeArguments) {
type = type.getTypeDeclaration();
}
if (Bindings.equals(type, possibleSuperType)) {
return true;
}
ITypeBinding superClass = type.getSuperclass();
if (superClass != null) {
if (isSuperType(possibleSuperType, superClass, considerTypeArguments)) {
return true;
}
}
if (possibleSuperType.isInterface()) {
ITypeBinding[] superInterfaces = type.getInterfaces();
for (int i = 0; i < superInterfaces.length; i++) {
if (isSuperType(possibleSuperType, superInterfaces[i], considerTypeArguments)) {
return true;
}
}
}
return false;
}
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 declaringType != null && pack == declaringType.getPackage();
}
public static boolean isVoidType(ITypeBinding binding) {
return "void".equals(binding.getName()); //$NON-NLS-1$
}
//---- Helper methods to convert a method ---------------------------------------------
/**
* Normalizes the binding so that it can be used as a type inside a declaration (e.g. variable
* declaration, method return type, parameter type, ...). For null bindings, java.lang.Object is
* returned. For void bindings, <code>null</code> is returned.
*
* @param binding binding to normalize
* @param ast current AST
* @return the normalized type to be used in declarations, or <code>null</code>
*/
public static ITypeBinding normalizeForDeclarationUse(ITypeBinding binding, AST ast) {
if (binding.isNullType()) {
return ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
}
if (binding.isPrimitive()) {
return binding;
}
binding = normalizeTypeBinding(binding);
if (binding == null || !binding.isWildcardType()) {
return binding;
}
if (binding.isUpperbound()) {
return binding.getBound();
} else {
return ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
}
}
/**
* Normalizes a type binding received from an expression to a type binding that can be used inside
* a declaration signature, but <em>not</em> as type of a declaration (use
* {@link #normalizeForDeclarationUse(ITypeBinding, AST)} for that).
* <p>
* Anonymous types are normalized to the super class or interface. For null or void bindings,
* <code>null</code> is returned.
* </p>
*
* @param binding the binding to normalize
* @return the normalized binding, can be <code>null</code>
* @see #normalizeForDeclarationUse(ITypeBinding, AST)
*/
public static ITypeBinding normalizeTypeBinding(ITypeBinding binding) {
if (binding != null && !binding.isNullType() && !isVoidType(binding)) {
if (binding.isAnonymous()) {
ITypeBinding[] baseBindings = binding.getInterfaces();
if (baseBindings.length > 0) {
return baseBindings[0];
}
return binding.getSuperclass();
}
if (binding.isCapture()) {
return binding.getWildcard();
}
return binding;
}
return null;
}
/**
* Resolve the binding (<em>not</em> the type binding) for the expression or a nested expression
* (e.g. nested in parentheses, cast, ...).
*
* @param expression an expression node
* @param goIntoCast iff <code>true</code>, go into a CastExpression's expression to resolve
* @return the expression binding, or <code>null</code> if the expression has no binding or the
* binding could not be resolved
* @see StubUtility#getVariableNameSuggestions(int, IJavaProject, ITypeBinding, Expression,
* java.util.Collection)
* @since 3.5
*/
public static IBinding resolveExpressionBinding(Expression expression, boolean goIntoCast) {
//TODO: search for callers of resolve*Binding() methods and replace with call to this method
// similar to StubUtility#getVariableNameSuggestions(int, IJavaProject, ITypeBinding, Expression, Collection)
switch (expression.getNodeType()) {
case ASTNode.SIMPLE_NAME:
case ASTNode.QUALIFIED_NAME:
return ((Name) expression).resolveBinding();
case ASTNode.FIELD_ACCESS:
return ((FieldAccess) expression).resolveFieldBinding();
case ASTNode.SUPER_FIELD_ACCESS:
return ((SuperFieldAccess) expression).resolveFieldBinding();
case ASTNode.METHOD_INVOCATION:
return ((MethodInvocation) expression).resolveMethodBinding();
case ASTNode.SUPER_METHOD_INVOCATION:
return ((SuperMethodInvocation) expression).resolveMethodBinding();
case ASTNode.CLASS_INSTANCE_CREATION:
return ((ClassInstanceCreation) expression).resolveConstructorBinding();
case ASTNode.MARKER_ANNOTATION:
case ASTNode.SINGLE_MEMBER_ANNOTATION:
case ASTNode.NORMAL_ANNOTATION:
return ((Annotation) expression).resolveAnnotationBinding();
case ASTNode.ARRAY_ACCESS:
return resolveExpressionBinding(((ArrayAccess) expression).getArray(), goIntoCast);
case ASTNode.CAST_EXPRESSION:
if (goIntoCast) {
return resolveExpressionBinding(((CastExpression) expression).getExpression(), true);
} else {
return null;
}
case ASTNode.PARENTHESIZED_EXPRESSION:
return resolveExpressionBinding(
((ParenthesizedExpression) expression).getExpression(),
goIntoCast);
case ASTNode.PREFIX_EXPRESSION:
return resolveExpressionBinding(((PrefixExpression) expression).getOperand(), goIntoCast);
case ASTNode.POSTFIX_EXPRESSION:
return resolveExpressionBinding(((PostfixExpression) expression).getOperand(), goIntoCast);
default:
return null;
}
}
// /**
// * Method to visit a type hierarchy defined by a given type.
// *
// * @param type the type which hierarchy is to be visited
// * @param visitor the visitor
// * @return <code>false</code> if the visiting got interrupted
// */
// public static boolean visitHierarchy(ITypeBinding type, TypeBindingVisitor visitor) {
// boolean result = visitSuperclasses(type, visitor);
// if (result) {
// result = visitInterfaces(type, visitor);
// }
// return result;
// }
//
// /**
// * Method to visit a interface hierarchy defined by a given type.
// *
// * @param type the type which interface hierarchy is to be visited
// * @param visitor the visitor
// * @return <code>false</code> if the visiting got interrupted
// */
// public static boolean visitInterfaces(ITypeBinding type, TypeBindingVisitor visitor) {
// ITypeBinding[] interfaces = type.getInterfaces();
// for (int i = 0; i < interfaces.length; i++) {
// if (!visitor.visit(interfaces[i])) {
// return false;
// }
// }
// return true;
// }
//
// /**
// * Method to visit a super class hierarchy defined by a given type.
// *
// * @param type the type which super class hierarchy is to be visited
// * @param visitor the visitor
// * @return <code>false</code> if the visiting got interrupted
// */
// public static boolean visitSuperclasses(ITypeBinding type, TypeBindingVisitor visitor) {
// while ((type = type.getSuperclass()) != null) {
// if (!visitor.visit(type)) {
// return false;
// }
// }
// return true;
// }
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;
}
private static boolean areSubTypeCompatible(IMethodBinding overridden, IMethodBinding overridable) {
if (overridden.getParameterTypes().length != overridable.getParameterTypes().length) {
return false;
}
ITypeBinding overriddenReturn = overridden.getReturnType();
ITypeBinding overridableReturn = overridable.getReturnType();
if (overriddenReturn == null || overridableReturn == null) {
return false;
}
if (!overriddenReturn.getErasure().isSubTypeCompatible(overridableReturn.getErasure())) {
return false;
}
ITypeBinding[] overriddenTypes = overridden.getParameterTypes();
ITypeBinding[] overridableTypes = overridable.getParameterTypes();
Assert.isTrue(overriddenTypes.length == overridableTypes.length);
for (int index = 0; index < overriddenTypes.length; index++) {
final ITypeBinding overridableErasure = overridableTypes[index].getErasure();
final ITypeBinding overriddenErasure = overriddenTypes[index].getErasure();
if (!overridableErasure.isSubTypeCompatible(overriddenErasure)
|| !overridableErasure.getKey().equals(overriddenErasure.getKey())) {
return false;
}
}
ITypeBinding[] overriddenExceptions = overridden.getExceptionTypes();
ITypeBinding[] overridableExceptions = overridable.getExceptionTypes();
boolean checked = false;
for (int index = 0; index < overriddenExceptions.length; index++) {
checked = false;
for (int offset = 0; offset < overridableExceptions.length; offset++) {
if (overriddenExceptions[index].isSubTypeCompatible(overridableExceptions[offset])) {
checked = true;
}
}
if (!checked) {
return false;
}
}
return true;
}
private static String asString(IMethodBinding method) {
StringBuffer result = new StringBuffer();
result.append(method.getDeclaringClass().getName());
result.append(':');
result.append(method.getName());
result.append('(');
ITypeBinding[] parameters = method.getParameterTypes();
int lastComma = parameters.length - 1;
for (int i = 0; i < parameters.length; i++) {
ITypeBinding parameter = parameters[i];
result.append(parameter.getName());
if (i < lastComma) {
result.append(", "); //$NON-NLS-1$
}
}
result.append(')');
return result.toString();
}
private static String asString(IVariableBinding variableBinding) {
if (!variableBinding.isField()) {
return variableBinding.toString();
}
if (variableBinding.getDeclaringClass() == null) {
Assert.isTrue(variableBinding.getName().equals("length"));//$NON-NLS-1$
return ARRAY_LENGTH_FIELD_BINDING_STRING;
}
StringBuffer result = new StringBuffer();
result.append(variableBinding.getDeclaringClass().getName());
result.append(':');
result.append(variableBinding.getName());
return result.toString();
}
private static void collectSuperTypes(ITypeBinding curr, Set<ITypeBinding> collection) {
if (collection.add(curr)) {
ITypeBinding[] interfaces = curr.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
collectSuperTypes(interfaces[i], collection);
}
ITypeBinding superClass = curr.getSuperclass();
if (superClass != null) {
collectSuperTypes(superClass, collection);
}
}
}
private static boolean containsTypeVariables(ITypeBinding[] types) {
for (int i = 0; i < types.length; i++) {
if (containsTypeVariables(types[i])) {
return true;
}
}
return false;
}
private static void createName(ITypeBinding type, boolean includePackage, List<String> list) {
ITypeBinding baseType = type;
if (type.isArray()) {
baseType = type.getElementType();
}
if (!baseType.isPrimitive() && !baseType.isNullType()) {
ITypeBinding declaringType = baseType.getDeclaringClass();
if (declaringType != null) {
createName(declaringType, includePackage, list);
} else if (includePackage && !baseType.getPackage().isUnnamed()) {
String[] components = baseType.getPackage().getNameComponents();
for (int i = 0; i < components.length; i++) {
list.add(components[i]);
}
}
}
if (!baseType.isAnonymous()) {
list.add(type.getName());
} else {
list.add("$local$"); //$NON-NLS-1$
}
}
private static String getBoxedTypeName(String primitiveName) {
if ("long".equals(primitiveName)) {
return "java.lang.Long"; //$NON-NLS-1$
} else if ("int".equals(primitiveName)) {
return "java.lang.Integer"; //$NON-NLS-1$
} else if ("short".equals(primitiveName)) {
return "java.lang.Short"; //$NON-NLS-1$
} else if ("char".equals(primitiveName)) {
return "java.lang.Character"; //$NON-NLS-1$
} else if ("byte".equals(primitiveName)) {
return "java.lang.Byte"; //$NON-NLS-1$
} else if ("boolean".equals(primitiveName)) {
return "java.lang.Boolean"; //$NON-NLS-1$
} else if ("float".equals(primitiveName)) {
return "java.lang.Float"; //$NON-NLS-1$
} else if ("double".equals(primitiveName)) {
return "java.lang.Double"; //$NON-NLS-1$
} else {
return null;
}
}
private static Set<ITypeBinding> getTypeBoundsForSubsignature(ITypeBinding typeParameter) {
ITypeBinding[] typeBounds = typeParameter.getTypeBounds();
int count = typeBounds.length;
if (count == 0) {
return Collections.emptySet();
}
Set<ITypeBinding> result = new HashSet<ITypeBinding>(typeBounds.length);
for (int i = 0; i < typeBounds.length; i++) {
ITypeBinding bound = typeBounds[i];
if ("java.lang.Object".equals(typeBounds[0].getQualifiedName())) {
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;
}
private static String getUnboxedTypeName(String boxedName) {
if ("java.lang.Long".equals(boxedName)) {
return "long"; //$NON-NLS-1$
} else if ("java.lang.Integer".equals(boxedName)) {
return "int"; //$NON-NLS-1$
} else if ("java.lang.Short".equals(boxedName)) {
return "short"; //$NON-NLS-1$
} else if ("java.lang.Character".equals(boxedName)) {
return "char"; //$NON-NLS-1$
} else if ("java.lang.Byte".equals(boxedName)) {
return "byte"; //$NON-NLS-1$
} else if ("java.lang.Boolean".equals(boxedName)) {
return "boolean"; //$NON-NLS-1$
} else if ("java.lang.Float".equals(boxedName)) {
return "float"; //$NON-NLS-1$
} else if ("java.lang.Double".equals(boxedName)) {
return "double"; //$NON-NLS-1$
} else {
return null;
}
}
private static boolean isSignatureEquivalentConstructor(IMethodBinding overridden,
IMethodBinding overridable) {
if (!overridden.isConstructor() || !overridable.isConstructor()) {
return false;
}
if (overridden.isDefaultConstructor()) {
return false;
}
return areSubTypeCompatible(overridden, overridable);
}
private static boolean sameParameter(ITypeBinding type, String candidate, IType scope)
throws JavaModelException {
if (type.getDimensions() != Signature.getArrayCount(candidate)) {
return false;
}
// Normalizes types
if (type.isArray()) {
type = type.getElementType();
}
candidate = Signature.getElementType(candidate);
if (Signature.getTypeSignatureKind(candidate) == Signature.BASE_TYPE_SIGNATURE != type.isPrimitive()) {
return false;
}
if (type.isPrimitive() || type.isTypeVariable()) {
return type.getName().equals(Signature.toString(candidate));
} else {
// normalize (quick hack until binding.getJavaElement works)
candidate = Signature.getTypeErasure(candidate);
type = type.getErasure();
if (candidate.charAt(Signature.getArrayCount(candidate)) == Signature.C_RESOLVED) {
return Signature.toString(candidate).equals(Bindings.getFullyQualifiedName(type));
} else {
String[][] qualifiedCandidates = scope.resolveType(Signature.toString(candidate));
if (qualifiedCandidates == null || qualifiedCandidates.length == 0) {
return false;
}
String packageName = type.getPackage().isUnnamed() ? "" : type.getPackage().getName(); //$NON-NLS-1$
String typeName = getTypeQualifiedName(type);
for (int i = 0; i < qualifiedCandidates.length; i++) {
String[] qualifiedCandidate = qualifiedCandidates[i];
if (qualifiedCandidate[0].equals(packageName) && qualifiedCandidate[1].equals(typeName)) {
return true;
}
}
}
}
return false;
}
private static boolean sameParameters(IMethodBinding method, IMethod candidate)
throws JavaModelException {
ITypeBinding[] methodParamters = method.getParameterTypes();
String[] candidateParameters = candidate.getParameterTypes();
if (methodParamters.length != candidateParameters.length) {
return false;
}
IType scope = candidate.getDeclaringType();
for (int i = 0; i < methodParamters.length; i++) {
ITypeBinding methodParameter = methodParamters[i];
String candidateParameter = candidateParameters[i];
if (!sameParameter(methodParameter, candidateParameter, scope)) {
return false;
}
}
return true;
}
private Bindings() {
// No instance
}
}