| 1 | /******************************************************************************* |
|---|---|
| 2 | * Copyright (c) 2000, 2020 IBM Corporation and others. |
| 3 | * |
| 4 | * This program and the accompanying materials |
| 5 | * are made available under the terms of the Eclipse Public License 2.0 |
| 6 | * which accompanies this distribution, and is available at |
| 7 | * https://www.eclipse.org/legal/epl-2.0/ |
| 8 | * |
| 9 | * SPDX-License-Identifier: EPL-2.0 |
| 10 | * |
| 11 | * Contributors: |
| 12 | * IBM Corporation - initial API and implementation |
| 13 | *******************************************************************************/ |
| 14 | package org.eclipse.jdt.astview.views; |
| 15 | |
| 16 | import java.util.ArrayList; |
| 17 | import java.util.Objects; |
| 18 | import java.util.function.Function; |
| 19 | |
| 20 | import org.eclipse.jdt.astview.ASTViewPlugin; |
| 21 | |
| 22 | import org.eclipse.swt.graphics.Image; |
| 23 | |
| 24 | import org.eclipse.jdt.core.IJavaElement; |
| 25 | import org.eclipse.jdt.core.dom.AST; |
| 26 | import org.eclipse.jdt.core.dom.CharacterLiteral; |
| 27 | import org.eclipse.jdt.core.dom.IAnnotationBinding; |
| 28 | import org.eclipse.jdt.core.dom.IBinding; |
| 29 | import org.eclipse.jdt.core.dom.IMemberValuePairBinding; |
| 30 | import org.eclipse.jdt.core.dom.IMethodBinding; |
| 31 | import org.eclipse.jdt.core.dom.IModuleBinding; |
| 32 | import org.eclipse.jdt.core.dom.IPackageBinding; |
| 33 | import org.eclipse.jdt.core.dom.ITypeBinding; |
| 34 | import org.eclipse.jdt.core.dom.IVariableBinding; |
| 35 | import org.eclipse.jdt.core.dom.Modifier; |
| 36 | import org.eclipse.jdt.core.dom.PrimitiveType; |
| 37 | import org.eclipse.jdt.core.dom.StringLiteral; |
| 38 | |
| 39 | /** |
| 40 | * |
| 41 | */ |
| 42 | public class Binding extends ASTAttribute { |
| 43 | |
| 44 | private final IBinding fBinding; |
| 45 | private final String fLabel; |
| 46 | private final Object fParent; |
| 47 | private final boolean fIsRelevant; |
| 48 | |
| 49 | public Binding(Object parent, String label, IBinding binding, boolean isRelevant) { |
| 50 | fParent= parent; |
| 51 | fBinding= binding; |
| 52 | fLabel= label; |
| 53 | fIsRelevant= isRelevant; |
| 54 | } |
| 55 | |
| 56 | @Override |
| 57 | public Object getParent() { |
| 58 | return fParent; |
| 59 | } |
| 60 | |
| 61 | public IBinding getBinding() { |
| 62 | return fBinding; |
| 63 | } |
| 64 | |
| 65 | |
| 66 | public boolean hasBindingProperties() { |
| 67 | return fBinding != null; |
| 68 | } |
| 69 | |
| 70 | public boolean isRelevant() { |
| 71 | return fIsRelevant; |
| 72 | } |
| 73 | |
| 74 | |
| 75 | private static boolean isType(int typeKinds, int kind) { |
| 76 | return (typeKinds & kind) != 0; |
| 77 | } |
| 78 | |
| 79 | @Override |
| 80 | public Object[] getChildren() { |
| 81 | try { |
| 82 | if (fBinding != null) { |
| 83 | fBinding.getKey(); |
| 84 | } |
| 85 | } catch (RuntimeException e) { |
| 86 | ASTViewPlugin.log("Exception thrown in IBinding#getKey() for \"" + fBinding + "\"", e); |
| 87 | return new Object[] { new Error(this, "BrokenBinding: " + fBinding, null) }; |
| 88 | } |
| 89 | if (fBinding != null) { |
| 90 | ArrayList<ASTAttribute> res= new ArrayList<>(); |
| 91 | res.add(new BindingProperty(this, "NAME", fBinding.getName(), true)); //$NON-NLS-1$ |
| 92 | res.add(new BindingProperty(this, "KEY", fBinding.getKey(), true)); //$NON-NLS-1$ |
| 93 | res.add(new BindingProperty(this, "IS RECOVERED", fBinding.isRecovered(), true)); //$NON-NLS-1$ |
| 94 | switch (fBinding.getKind()) { |
| 95 | case IBinding.VARIABLE: |
| 96 | IVariableBinding variableBinding= (IVariableBinding) fBinding; |
| 97 | res.add(new BindingProperty(this, "IS FIELD", variableBinding.isField(), true)); //$NON-NLS-1$ |
| 98 | res.add(new BindingProperty(this, "IS ENUM CONSTANT", variableBinding.isEnumConstant(), true)); //$NON-NLS-1$ |
| 99 | res.add(new BindingProperty(this, "IS PARAMETER", variableBinding.isParameter(), true)); //$NON-NLS-1$ |
| 100 | res.add(new BindingProperty(this, "IS RECORD COMPONENT", variableBinding.isRecordComponent(), true)); //$NON-NLS-1$ |
| 101 | res.add(new BindingProperty(this, "VARIABLE ID", variableBinding.getVariableId(), true)); //$NON-NLS-1$ |
| 102 | res.add(new BindingProperty(this, "MODIFIERS", getModifiersString(fBinding.getModifiers(), false), true)); //$NON-NLS-1$ |
| 103 | res.add(new Binding(this, "TYPE", variableBinding.getType(), true)); //$NON-NLS-1$ |
| 104 | res.add(new Binding(this, "DECLARING CLASS", variableBinding.getDeclaringClass(), true)); //$NON-NLS-1$ |
| 105 | res.add(new Binding(this, "DECLARING METHOD", variableBinding.getDeclaringMethod(), true)); //$NON-NLS-1$ |
| 106 | res.add(new Binding(this, "VARIABLE DECLARATION", variableBinding.getVariableDeclaration(), true)); //$NON-NLS-1$ |
| 107 | res.add(new BindingProperty(this, "IS SYNTHETIC", fBinding.isSynthetic(), true)); //$NON-NLS-1$ |
| 108 | res.add(new BindingProperty(this, "IS DEPRECATED", fBinding.isDeprecated(), true)); //$NON-NLS-1$ |
| 109 | res.add(new BindingProperty(this, "CONSTANT VALUE", variableBinding.getConstantValue(), true)); //$NON-NLS-1$ |
| 110 | res.add(new BindingProperty(this, "IS EFFECTIVELY FINAL", variableBinding.isEffectivelyFinal(), true)); //$NON-NLS-1$ |
| 111 | break; |
| 112 | |
| 113 | case IBinding.PACKAGE: |
| 114 | IPackageBinding packageBinding= (IPackageBinding) fBinding; |
| 115 | res.add(new BindingProperty(this, "IS UNNAMED", packageBinding.isUnnamed(), true)); //$NON-NLS-1$ |
| 116 | res.add(new BindingProperty(this, "IS SYNTHETIC", fBinding.isSynthetic(), true)); //$NON-NLS-1$ |
| 117 | res.add(new BindingProperty(this, "IS DEPRECATED", fBinding.isDeprecated(), true)); //$NON-NLS-1$ |
| 118 | break; |
| 119 | |
| 120 | case IBinding.TYPE: |
| 121 | ITypeBinding typeBinding= (ITypeBinding) fBinding; |
| 122 | res.add(new BindingProperty(this, "QUALIFIED NAME", typeBinding.getQualifiedName(), true)); //$NON-NLS-1$ |
| 123 | |
| 124 | int typeKind= getTypeKind(typeBinding); |
| 125 | boolean isRefType= isType(typeKind, REF_TYPE); |
| 126 | final boolean isNonPrimitive= ! isType(typeKind, PRIMITIVE_TYPE); |
| 127 | |
| 128 | StringBuilder kinds= new StringBuilder("KIND:"); //$NON-NLS-1$ |
| 129 | if (typeBinding.isArray()) kinds.append(" isArray"); //$NON-NLS-1$ |
| 130 | if (typeBinding.isCapture()) kinds.append(" isCapture"); //$NON-NLS-1$ |
| 131 | if (typeBinding.isNullType()) kinds.append(" isNullType"); //$NON-NLS-1$ |
| 132 | if (typeBinding.isPrimitive()) kinds.append(" isPrimitive"); //$NON-NLS-1$ |
| 133 | if (typeBinding.isTypeVariable()) kinds.append(" isTypeVariable"); //$NON-NLS-1$ |
| 134 | if (typeBinding.isWildcardType()) kinds.append(" isWildcardType"); //$NON-NLS-1$ |
| 135 | // ref types |
| 136 | if (typeBinding.isAnnotation()) kinds.append(" isAnnotation"); //$NON-NLS-1$ |
| 137 | if (typeBinding.isClass()) kinds.append(" isClass"); //$NON-NLS-1$ |
| 138 | if (typeBinding.isInterface()) kinds.append(" isInterface"); //$NON-NLS-1$ |
| 139 | if (typeBinding.isEnum()) kinds.append(" isEnum"); //$NON-NLS-1$ |
| 140 | res.add(new BindingProperty(this, kinds, true)); |
| 141 | |
| 142 | StringBuilder generics= new StringBuilder("GENERICS:"); //$NON-NLS-1$ |
| 143 | if (typeBinding.isRawType()) generics.append(" isRawType"); //$NON-NLS-1$ |
| 144 | if (typeBinding.isGenericType()) generics.append(" isGenericType"); //$NON-NLS-1$ |
| 145 | if (typeBinding.isParameterizedType()) generics.append(" isParameterizedType"); //$NON-NLS-1$ |
| 146 | if (!isType(typeKind, GENERIC | PARAMETRIZED)) { |
| 147 | generics.append(" (non-generic, non-parameterized)"); |
| 148 | } |
| 149 | res.add(new BindingProperty(this, generics, isRefType)); |
| 150 | |
| 151 | res.add(new Binding(this, "ELEMENT TYPE", typeBinding.getElementType(), isType(typeKind, ARRAY_TYPE))); //$NON-NLS-1$ |
| 152 | res.add(new Binding(this, "COMPONENT TYPE", typeBinding.getComponentType(), isType(typeKind, ARRAY_TYPE))); //$NON-NLS-1$ |
| 153 | res.add(new BindingProperty(this, "DIMENSIONS", typeBinding.getDimensions(), isType(typeKind, ARRAY_TYPE))); //$NON-NLS-1$ |
| 154 | final String createArrayTypeLabel= "CREATE ARRAY TYPE (+1)"; |
| 155 | try { |
| 156 | ITypeBinding arrayType= typeBinding.createArrayType(1); |
| 157 | res.add(new Binding(this, createArrayTypeLabel, arrayType, true)); |
| 158 | } catch (RuntimeException e) { |
| 159 | String msg= e.getClass().getName() + ": " + e.getLocalizedMessage(); |
| 160 | boolean isRelevant= ! typeBinding.getName().equals(PrimitiveType.VOID.toString()) && ! typeBinding.isRecovered(); |
| 161 | if (isRelevant) { |
| 162 | res.add(new Error(this, createArrayTypeLabel + ": " + msg, e)); |
| 163 | } else { |
| 164 | res.add(new BindingProperty(this, createArrayTypeLabel, msg, false)); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | StringBuilder origin= new StringBuilder("ORIGIN:"); //$NON-NLS-1$ |
| 169 | if (typeBinding.isTopLevel()) origin.append(" isTopLevel"); //$NON-NLS-1$ |
| 170 | if (typeBinding.isNested()) origin.append(" isNested"); //$NON-NLS-1$ |
| 171 | if (typeBinding.isLocal()) origin.append(" isLocal"); //$NON-NLS-1$ |
| 172 | if (typeBinding.isMember()) origin.append(" isMember"); //$NON-NLS-1$ |
| 173 | if (typeBinding.isAnonymous()) origin.append(" isAnonymous"); //$NON-NLS-1$ |
| 174 | res.add(new BindingProperty(this, origin, isRefType)); |
| 175 | |
| 176 | res.add(new BindingProperty(this, "IS FROM SOURCE", typeBinding.isFromSource(), isType(typeKind, REF_TYPE | VARIABLE_TYPE | CAPTURE_TYPE))); //$NON-NLS-1$ |
| 177 | |
| 178 | res.add(new Binding(this, "PACKAGE", typeBinding.getPackage(), isRefType)); //$NON-NLS-1$ |
| 179 | res.add(new Binding(this, "DECLARING CLASS", typeBinding.getDeclaringClass(), isType(typeKind, REF_TYPE | VARIABLE_TYPE | CAPTURE_TYPE))); //$NON-NLS-1$ |
| 180 | res.add(new Binding(this, "DECLARING METHOD", typeBinding.getDeclaringMethod(), isType(typeKind, REF_TYPE | VARIABLE_TYPE | CAPTURE_TYPE))); //$NON-NLS-1$ |
| 181 | res.add(new Binding(this, "DECLARING MEMBER", typeBinding.getDeclaringMember(), typeBinding.isLocal())); //$NON-NLS-1$ |
| 182 | res.add(new BindingProperty(this, "MODIFIERS", getModifiersString(fBinding.getModifiers(), false), isRefType)); //$NON-NLS-1$ |
| 183 | res.add(new BindingProperty(this, "BINARY NAME", typeBinding.getBinaryName(), true)); //$NON-NLS-1$ |
| 184 | |
| 185 | String isTypeDeclaration= typeBinding == typeBinding.getTypeDeclaration() ? " ( == this)" : " ( != this)"; |
| 186 | res.add(new Binding(this, "TYPE DECLARATION" + isTypeDeclaration, typeBinding.getTypeDeclaration(), true)); //$NON-NLS-1$ |
| 187 | String isErasure= typeBinding == typeBinding.getErasure() ? " ( == this)" : " ( != this)"; |
| 188 | res.add(new Binding(this, "ERASURE" + isErasure, typeBinding.getErasure(), isNonPrimitive)); //$NON-NLS-1$ |
| 189 | res.add(new BindingProperty(this, "TYPE PARAMETERS", typeBinding.getTypeParameters(), isType(typeKind, GENERIC))); //$NON-NLS-1$ |
| 190 | res.add(new BindingProperty(this, "TYPE ARGUMENTS", typeBinding.getTypeArguments(), isType(typeKind, PARAMETRIZED))); //$NON-NLS-1$ |
| 191 | res.add(new BindingProperty(this, "TYPE BOUNDS", typeBinding.getTypeBounds(), isType(typeKind, VARIABLE_TYPE | WILDCARD_TYPE | CAPTURE_TYPE))); //$NON-NLS-1$ |
| 192 | res.add(new Binding(this, "BOUND", typeBinding.getBound(), isType(typeKind, WILDCARD_TYPE))); //$NON-NLS-1$ |
| 193 | res.add(new BindingProperty(this, "IS UPPERBOUND", typeBinding.isUpperbound(), isType(typeKind, WILDCARD_TYPE))); //$NON-NLS-1$ |
| 194 | res.add(new Binding(this, "GENERIC TYPE OF WILDCARD TYPE", typeBinding.getGenericTypeOfWildcardType(), isType(typeKind, WILDCARD_TYPE))); //$NON-NLS-1$ |
| 195 | res.add(new BindingProperty(this, "RANK", typeBinding.getRank(), isType(typeKind, WILDCARD_TYPE))); //$NON-NLS-1$ |
| 196 | res.add(new Binding(this, "WILDCARD", typeBinding.getWildcard(), isType(typeKind, CAPTURE_TYPE))); //$NON-NLS-1$ |
| 197 | |
| 198 | res.add(new Binding(this, "SUPERCLASS", typeBinding.getSuperclass(), isRefType)); //$NON-NLS-1$ |
| 199 | res.add(new BindingProperty(this, "INTERFACES", typeBinding.getInterfaces(), isRefType)); //$NON-NLS-1$ |
| 200 | res.add(new BindingProperty(this, "DECLARED TYPES", typeBinding.getDeclaredTypes(), isRefType)); //$NON-NLS-1$ |
| 201 | res.add(new BindingProperty(this, "DECLARED FIELDS", typeBinding.getDeclaredFields(), isRefType)); //$NON-NLS-1$ |
| 202 | res.add(new BindingProperty(this, "DECLARED METHODS", typeBinding.getDeclaredMethods(), isRefType)); //$NON-NLS-1$ |
| 203 | res.add(new Binding(this, "FUNCTIONAL INTERFACE METHOD", typeBinding.getFunctionalInterfaceMethod(), typeBinding.isInterface())); //$NON-NLS-1$ |
| 204 | res.add(new BindingProperty(this, "IS SYNTHETIC", fBinding.isSynthetic(), isNonPrimitive)); //$NON-NLS-1$ |
| 205 | res.add(new BindingProperty(this, "IS DEPRECATED", fBinding.isDeprecated(), isRefType)); //$NON-NLS-1$ |
| 206 | res.add(new BindingProperty(this, "TYPE ANNOTATIONS", typeBinding.getTypeAnnotations(), true)); //$NON-NLS-1$ |
| 207 | break; |
| 208 | |
| 209 | case IBinding.METHOD: |
| 210 | IMethodBinding methodBinding= (IMethodBinding) fBinding; |
| 211 | res.add(new BindingProperty(this, "IS CONSTRUCTOR", methodBinding.isConstructor(), true)); //$NON-NLS-1$ |
| 212 | res.add(new BindingProperty(this, "IS DEFAULT CONSTRUCTOR", methodBinding.isDefaultConstructor(), true)); //$NON-NLS-1$ |
| 213 | res.add(new Binding(this, "DECLARING CLASS", methodBinding.getDeclaringClass(), true)); //$NON-NLS-1$ |
| 214 | res.add(new Binding(this, "DECLARING MEMBER", methodBinding.getDeclaringMember(), methodBinding.getDeclaringMember() != null)); //$NON-NLS-1$ |
| 215 | res.add(new Binding(this, "RETURN TYPE", methodBinding.getReturnType(), true)); //$NON-NLS-1$ |
| 216 | res.add(new BindingProperty(this, "MODIFIERS", getModifiersString(fBinding.getModifiers(), true), true)); //$NON-NLS-1$ |
| 217 | res.add(new BindingProperty(this, "PARAMETER TYPES", methodBinding.getParameterTypes(), true)); //$NON-NLS-1$ |
| 218 | res.add(new BindingProperty(this, "IS VARARGS", methodBinding.isVarargs(), true)); //$NON-NLS-1$ |
| 219 | res.add(new BindingProperty(this, "EXCEPTION TYPES", methodBinding.getExceptionTypes(), true)); //$NON-NLS-1$ |
| 220 | |
| 221 | StringBuilder genericsM= new StringBuilder("GENERICS:"); //$NON-NLS-1$ |
| 222 | if (methodBinding.isRawMethod()) genericsM.append(" isRawMethod"); //$NON-NLS-1$ |
| 223 | if (methodBinding.isGenericMethod()) genericsM.append(" isGenericMethod"); //$NON-NLS-1$ |
| 224 | if (methodBinding.isParameterizedMethod()) genericsM.append(" isParameterizedMethod"); //$NON-NLS-1$ |
| 225 | res.add(new BindingProperty(this, genericsM, true)); |
| 226 | |
| 227 | String isMethodDeclaration= methodBinding == methodBinding.getMethodDeclaration() ? " ( == this)" : " ( != this)"; |
| 228 | res.add(new Binding(this, "METHOD DECLARATION" + isMethodDeclaration, methodBinding.getMethodDeclaration(), true)); //$NON-NLS-1$ |
| 229 | res.add(new BindingProperty(this, "TYPE PARAMETERS", methodBinding.getTypeParameters(), true)); //$NON-NLS-1$ |
| 230 | res.add(new BindingProperty(this, "TYPE ARGUMENTS", methodBinding.getTypeArguments(), true)); //$NON-NLS-1$ |
| 231 | res.add(new BindingProperty(this, "IS SYNTHETIC", fBinding.isSynthetic(), true)); //$NON-NLS-1$ |
| 232 | res.add(new BindingProperty(this, "IS DEPRECATED", fBinding.isDeprecated(), true)); //$NON-NLS-1$ |
| 233 | |
| 234 | res.add(new BindingProperty(this, "IS ANNOTATION MEMBER", methodBinding.isAnnotationMember(), true)); //$NON-NLS-1$ |
| 235 | res.add(Binding.createValueAttribute(this, "DEFAULT VALUE", methodBinding.getDefaultValue())); |
| 236 | |
| 237 | int parameterCount= methodBinding.getParameterTypes().length; |
| 238 | BindingProperty[] parametersAnnotations= new BindingProperty[parameterCount]; |
| 239 | for (int i= 0; i < parameterCount; i++) { |
| 240 | parametersAnnotations[i]= new BindingProperty(this, "Parameter " + String.valueOf(i), methodBinding.getParameterAnnotations(i), true); |
| 241 | } |
| 242 | res.add(new BindingProperty(this, "PARAMETER ANNOTATIONS", parametersAnnotations, true)); |
| 243 | break; |
| 244 | |
| 245 | case IBinding.ANNOTATION: |
| 246 | IAnnotationBinding annotationBinding= (IAnnotationBinding) fBinding; |
| 247 | res.add(new Binding(this, "ANNOTATION TYPE", annotationBinding.getAnnotationType(), true)); |
| 248 | res.add(new BindingProperty(this, "DECLARED MEMBER VALUE PAIRS", annotationBinding.getDeclaredMemberValuePairs(), true)); |
| 249 | res.add(new BindingProperty(this, "ALL MEMBER VALUE PAIRS", annotationBinding.getAllMemberValuePairs(), true)); |
| 250 | break; |
| 251 | |
| 252 | case IBinding.MEMBER_VALUE_PAIR: |
| 253 | IMemberValuePairBinding memberValuePairBinding= (IMemberValuePairBinding) fBinding; |
| 254 | res.add(new Binding(this, "METHOD BINDING", memberValuePairBinding.getMethodBinding(), true)); |
| 255 | res.add(new BindingProperty(this, "IS DEFAULT", memberValuePairBinding.isDefault(), true)); |
| 256 | res.add(Binding.createValueAttribute(this, "VALUE", memberValuePairBinding.getValue())); |
| 257 | break; |
| 258 | |
| 259 | case IBinding.MODULE: |
| 260 | IModuleBinding moduleBinding= (IModuleBinding) fBinding; |
| 261 | res.add(new BindingProperty(this, "REQUIRED MODULES", moduleBinding.getRequiredModules(), true)); |
| 262 | res.add(createPropertiesWithSecondary(moduleBinding.getExportedPackages(), "EXPORTED PACKAGES", "PACKAGE", "TO", |
| 263 | moduleBinding::getExportedTo)); |
| 264 | res.add(createPropertiesWithSecondary(moduleBinding.getOpenedPackages(), "OPENED PACKAGES", "PACKAGE", "TO", |
| 265 | moduleBinding::getOpenedTo)); |
| 266 | res.add(new BindingProperty(this, "USES", moduleBinding.getUses(), true)); |
| 267 | res.add(createPropertiesWithSecondary(moduleBinding.getServices(), "SERVICES", "PROVIDES", "WITH", |
| 268 | moduleBinding::getImplementations)); |
| 269 | break; |
| 270 | |
| 271 | default: |
| 272 | break; |
| 273 | } |
| 274 | try { |
| 275 | IAnnotationBinding[] annotations= fBinding.getAnnotations(); |
| 276 | res.add(new BindingProperty(this, "ANNOTATIONS", annotations, true)); //$NON-NLS-1$ |
| 277 | } catch (RuntimeException e) { |
| 278 | String label= "Error in IBinding#getAnnotations() for \"" + fBinding.getKey() + "\""; |
| 279 | res.add(new Error(this, label, e)); |
| 280 | ASTViewPlugin.log("Exception thrown in IBinding#getAnnotations() for \"" + fBinding.getKey() + "\"", e); |
| 281 | } |
| 282 | try { |
| 283 | IJavaElement javaElement= fBinding.getJavaElement(); |
| 284 | res.add(new JavaElement(this, javaElement)); |
| 285 | } catch (RuntimeException e) { |
| 286 | String label= ">java element: " + e.getClass().getName() + " for \"" + fBinding.getKey() + "\""; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ |
| 287 | res.add(new Error(this, label, e)); |
| 288 | ASTViewPlugin.log("Exception thrown in IBinding#getJavaElement() for \"" + fBinding.getKey() + "\"", e); |
| 289 | } |
| 290 | return res.toArray(); |
| 291 | } |
| 292 | return EMPTY; |
| 293 | } |
| 294 | |
| 295 | private <T extends IBinding> BindingProperty createPropertiesWithSecondary(T[] bindings, String propertiesName, String propertyName, String secondaryName, |
| 296 | Function<T, Object[]> secondaryAccessor) { |
| 297 | int count= bindings.length; |
| 298 | ASTAttribute[] attributes= new ASTAttribute[count * 2]; |
| 299 | BindingProperty property= new BindingProperty(this, propertiesName, attributes, true) { |
| 300 | @Override |
| 301 | public String getLabel() { |
| 302 | return propertiesName + " (" + count + ")"; |
| 303 | } |
| 304 | }; |
| 305 | for (int i= 0; i < count; i++) { |
| 306 | attributes[i * 2]= new Binding(this, propertyName, bindings[i], true); |
| 307 | Object[] secondaryPropertyValues= secondaryAccessor.apply(bindings[i]); |
| 308 | if (secondaryPropertyValues instanceof IBinding[]) { |
| 309 | attributes[i * 2 + 1]= new BindingProperty(this, secondaryName, (IBinding[]) secondaryPropertyValues, true); |
| 310 | } else { |
| 311 | GeneralAttribute[] secondaryProperties= new GeneralAttribute[secondaryPropertyValues.length]; |
| 312 | for (int j= 0; j < secondaryProperties.length; j++) { |
| 313 | secondaryProperties[j]= new GeneralAttribute(property, String.valueOf(j), String.valueOf(secondaryPropertyValues[j])); |
| 314 | } |
| 315 | attributes[i * 2 + 1]= new BindingProperty(this, secondaryName, secondaryProperties, true); |
| 316 | } |
| 317 | } |
| 318 | return property; |
| 319 | } |
| 320 | |
| 321 | private final static int ARRAY_TYPE= 1 << 0; |
| 322 | private final static int NULL_TYPE= 1 << 1; |
| 323 | private final static int VARIABLE_TYPE= 1 << 2; |
| 324 | private final static int WILDCARD_TYPE= 1 << 3; |
| 325 | private final static int CAPTURE_TYPE= 1 << 4; |
| 326 | private final static int PRIMITIVE_TYPE= 1 << 5; |
| 327 | |
| 328 | private final static int REF_TYPE= 1 << 6; |
| 329 | |
| 330 | private final static int GENERIC= 1 << 8; |
| 331 | private final static int PARAMETRIZED= 1 << 9; |
| 332 | |
| 333 | private int getTypeKind(ITypeBinding typeBinding) { |
| 334 | if (typeBinding.isArray()) return ARRAY_TYPE; |
| 335 | if (typeBinding.isCapture()) return CAPTURE_TYPE; |
| 336 | if (typeBinding.isNullType()) return NULL_TYPE; |
| 337 | if (typeBinding.isPrimitive()) return PRIMITIVE_TYPE; |
| 338 | if (typeBinding.isTypeVariable()) return VARIABLE_TYPE; |
| 339 | if (typeBinding.isWildcardType()) return WILDCARD_TYPE; |
| 340 | |
| 341 | if (typeBinding.isGenericType()) return REF_TYPE | GENERIC; |
| 342 | if (typeBinding.isParameterizedType() || typeBinding.isRawType()) return REF_TYPE | PARAMETRIZED; |
| 343 | |
| 344 | return REF_TYPE; |
| 345 | } |
| 346 | |
| 347 | @Override |
| 348 | public String getLabel() { |
| 349 | StringBuffer buf= new StringBuffer(fLabel); |
| 350 | buf.append(": "); //$NON-NLS-1$ |
| 351 | if (fBinding != null) { |
| 352 | switch (fBinding.getKind()) { |
| 353 | case IBinding.VARIABLE: |
| 354 | IVariableBinding variableBinding= (IVariableBinding) fBinding; |
| 355 | if (!variableBinding.isField()) { |
| 356 | buf.append(variableBinding.getName()); |
| 357 | } else { |
| 358 | if (variableBinding.getDeclaringClass() == null) { |
| 359 | buf.append("<some array type>"); //$NON-NLS-1$ |
| 360 | } else { |
| 361 | buf.append(variableBinding.getDeclaringClass().getName()); |
| 362 | } |
| 363 | buf.append('.'); |
| 364 | buf.append(variableBinding.getName()); |
| 365 | } |
| 366 | break; |
| 367 | case IBinding.PACKAGE: |
| 368 | IPackageBinding packageBinding= (IPackageBinding) fBinding; |
| 369 | buf.append(packageBinding.getName()); |
| 370 | break; |
| 371 | case IBinding.TYPE: |
| 372 | ITypeBinding typeBinding= (ITypeBinding) fBinding; |
| 373 | appendAnnotatedQualifiedName(buf, typeBinding); |
| 374 | break; |
| 375 | case IBinding.METHOD: |
| 376 | IMethodBinding methodBinding= (IMethodBinding) fBinding; |
| 377 | buf.append(methodBinding.getDeclaringClass().getName()); |
| 378 | buf.append('.'); |
| 379 | buf.append(methodBinding.getName()); |
| 380 | buf.append('('); |
| 381 | ITypeBinding[] parameters= methodBinding.getParameterTypes(); |
| 382 | for (int i= 0; i < parameters.length; i++) { |
| 383 | if (i > 0) { |
| 384 | buf.append(", "); //$NON-NLS-1$ |
| 385 | } |
| 386 | ITypeBinding parameter= parameters[i]; |
| 387 | buf.append(parameter.getName()); |
| 388 | } |
| 389 | buf.append(')'); |
| 390 | break; |
| 391 | case IBinding.ANNOTATION: |
| 392 | case IBinding.MEMBER_VALUE_PAIR: |
| 393 | buf.append(fBinding.toString()); |
| 394 | break; |
| 395 | case IBinding.MODULE: |
| 396 | buf.append(fBinding.getName()); |
| 397 | } |
| 398 | |
| 399 | } else { |
| 400 | buf.append("null"); //$NON-NLS-1$ |
| 401 | } |
| 402 | return buf.toString(); |
| 403 | |
| 404 | } |
| 405 | |
| 406 | public static void appendAnnotatedQualifiedName(StringBuffer buf, ITypeBinding typeBinding) { |
| 407 | String debugString= typeBinding.toString(); // XXX: hack, but that's OK for a debugging tool... |
| 408 | if (debugString.indexOf('\n') == -1 || typeBinding.getTypeAnnotations().length != 0) { |
| 409 | // one-liner || outermost type has type annotations |
| 410 | buf.append(debugString); |
| 411 | } else { |
| 412 | buf.append(typeBinding.getQualifiedName()); |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | @Override |
| 417 | public Image getImage() { |
| 418 | return null; |
| 419 | } |
| 420 | |
| 421 | @Override |
| 422 | public String toString() { |
| 423 | return getLabel(); |
| 424 | } |
| 425 | |
| 426 | /* |
| 427 | * @see java.lang.Object#equals(java.lang.Object) |
| 428 | */ |
| 429 | @Override |
| 430 | public boolean equals(Object obj) { |
| 431 | if (this == obj) |
| 432 | return true; |
| 433 | if (obj == null || !obj.getClass().equals(getClass())) { |
| 434 | return false; |
| 435 | } |
| 436 | |
| 437 | Binding other= (Binding) obj; |
| 438 | if (!Objects.equals(fParent, other.fParent)) { |
| 439 | return false; |
| 440 | } |
| 441 | |
| 442 | if (!Objects.equals(fBinding, other.fBinding)) { |
| 443 | return false; |
| 444 | } |
| 445 | |
| 446 | if (!Objects.equals(fLabel, other.fLabel)) { |
| 447 | return false; |
| 448 | } |
| 449 | |
| 450 | return true; |
| 451 | } |
| 452 | |
| 453 | /* |
| 454 | * @see java.lang.Object#hashCode() |
| 455 | */ |
| 456 | @Override |
| 457 | public int hashCode() { |
| 458 | int result= fParent != null ? fParent.hashCode() : 0; |
| 459 | result+= (fBinding != null && fBinding.getKey() != null) ? fBinding.getKey().hashCode() : 0; |
| 460 | result+= fLabel != null ? fLabel.hashCode() : 0; |
| 461 | return result; |
| 462 | } |
| 463 | |
| 464 | public static String getBindingLabel(IBinding binding) { |
| 465 | String label; |
| 466 | if (binding == null) { |
| 467 | label= ">binding"; //$NON-NLS-1$ |
| 468 | } else { |
| 469 | switch (binding.getKind()) { |
| 470 | case IBinding.VARIABLE: |
| 471 | label= "> variable binding"; //$NON-NLS-1$ |
| 472 | break; |
| 473 | case IBinding.TYPE: |
| 474 | label= "> type binding"; //$NON-NLS-1$ |
| 475 | break; |
| 476 | case IBinding.METHOD: |
| 477 | label= "> method binding"; //$NON-NLS-1$ |
| 478 | break; |
| 479 | case IBinding.PACKAGE: |
| 480 | label= "> package binding"; //$NON-NLS-1$ |
| 481 | break; |
| 482 | case IBinding.ANNOTATION: |
| 483 | label= "> annotation binding"; //$NON-NLS-1$ |
| 484 | break; |
| 485 | case IBinding.MEMBER_VALUE_PAIR: |
| 486 | label= "> member value pair binding"; //$NON-NLS-1$ |
| 487 | break; |
| 488 | case IBinding.MODULE: |
| 489 | label= "> module binding"; //$NON-NLS-1$ |
| 490 | break; |
| 491 | default: |
| 492 | label= "> unknown binding"; //$NON-NLS-1$ |
| 493 | } |
| 494 | } |
| 495 | return label; |
| 496 | } |
| 497 | |
| 498 | /** |
| 499 | * Creates an {@link ASTAttribute} for a value from |
| 500 | * {@link IMemberValuePairBinding#getValue()} or from |
| 501 | * {@link IMethodBinding#getDefaultValue()}. |
| 502 | * |
| 503 | * @param parent the parent node |
| 504 | * @param name the attribute name |
| 505 | * @param value the attribute value |
| 506 | * @return an ASTAttribute |
| 507 | */ |
| 508 | public static ASTAttribute createValueAttribute(ASTAttribute parent, String name, Object value) { |
| 509 | ASTAttribute res; |
| 510 | if (value instanceof IBinding) { |
| 511 | IBinding binding= (IBinding) value; |
| 512 | res= new Binding(parent, name + ": " + getBindingLabel(binding), binding, true); |
| 513 | |
| 514 | } else if (value instanceof String) { |
| 515 | res= new GeneralAttribute(parent, name, getEscapedStringLiteral((String) value)); |
| 516 | |
| 517 | } else if (value instanceof Object[]) { |
| 518 | res= new GeneralAttribute(parent, name, (Object[]) value); |
| 519 | |
| 520 | } else if (value instanceof ASTAttribute) { |
| 521 | res= (ASTAttribute) value; |
| 522 | |
| 523 | } else { |
| 524 | res= new GeneralAttribute(parent, name, value); |
| 525 | } |
| 526 | return res; |
| 527 | } |
| 528 | |
| 529 | public static String getEscapedStringLiteral(String stringValue) { |
| 530 | StringLiteral stringLiteral= AST.newAST(ASTView.JLS_LATEST, false).newStringLiteral(); |
| 531 | stringLiteral.setLiteralValue(stringValue); |
| 532 | return stringLiteral.getEscapedValue(); |
| 533 | } |
| 534 | |
| 535 | public static String getEscapedCharLiteral(char charValue) { |
| 536 | CharacterLiteral charLiteral= AST.newAST(ASTView.JLS_LATEST, false).newCharacterLiteral(); |
| 537 | charLiteral.setCharValue(charValue); |
| 538 | return charLiteral.getEscapedValue(); |
| 539 | } |
| 540 | |
| 541 | private static StringBuffer getModifiersString(int flags, boolean isMethod) { |
| 542 | StringBuffer sb = new StringBuffer().append("0x").append(Integer.toHexString(flags)).append(" ("); |
| 543 | int prologLen= sb.length(); |
| 544 | int rest= flags; |
| 545 | |
| 546 | rest&= ~ appendFlag(sb, flags, Modifier.PUBLIC, "public "); |
| 547 | rest&= ~ appendFlag(sb, flags, Modifier.PRIVATE, "private "); |
| 548 | rest&= ~ appendFlag(sb, flags, Modifier.PROTECTED, "protected "); |
| 549 | rest&= ~ appendFlag(sb, flags, Modifier.STATIC, "static "); |
| 550 | rest&= ~ appendFlag(sb, flags, Modifier.FINAL, "final "); |
| 551 | if (isMethod) { |
| 552 | rest&= ~ appendFlag(sb, flags, Modifier.SYNCHRONIZED, "synchronized "); |
| 553 | rest&= ~ appendFlag(sb, flags, Modifier.DEFAULT, "default "); |
| 554 | } else { |
| 555 | rest&= ~ appendFlag(sb, flags, Modifier.VOLATILE, "volatile "); |
| 556 | rest&= ~ appendFlag(sb, flags, Modifier.TRANSIENT, "transient "); |
| 557 | } |
| 558 | rest&= ~ appendFlag(sb, flags, Modifier.NATIVE, "native "); |
| 559 | rest&= ~ appendFlag(sb, flags, Modifier.ABSTRACT, "abstract "); |
| 560 | rest&= ~ appendFlag(sb, flags, Modifier.STRICTFP, "strictfp "); |
| 561 | |
| 562 | if (rest != 0) |
| 563 | sb.append("unknown:0x").append(Integer.toHexString(rest)).append(" "); |
| 564 | int len = sb.length(); |
| 565 | if (len != prologLen) |
| 566 | sb.setLength(len - 1); |
| 567 | sb.append(")"); |
| 568 | return sb; |
| 569 | } |
| 570 | |
| 571 | private static int appendFlag(StringBuffer sb, int flags, int flag, String name) { |
| 572 | if ((flags & flag) != 0) { |
| 573 | sb.append(name); |
| 574 | return flag; |
| 575 | } else { |
| 576 | return 0; |
| 577 | } |
| 578 | } |
| 579 | } |
| 580 |
Members