/* * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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 org.codehaus.jdt.groovy.internal.compiler.ast; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.List; import java.util.Set; import org.codehaus.groovy.GroovyBugError; import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.GenericsType; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitDeclaration.FieldDeclarationWithInitializer; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.BooleanConstant; import org.eclipse.jdt.internal.compiler.impl.ByteConstant; import org.eclipse.jdt.internal.compiler.impl.CharConstant; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.impl.DoubleConstant; import org.eclipse.jdt.internal.compiler.impl.FloatConstant; import org.eclipse.jdt.internal.compiler.impl.IntConstant; import org.eclipse.jdt.internal.compiler.impl.LongConstant; import org.eclipse.jdt.internal.compiler.impl.ShortConstant; import org.eclipse.jdt.internal.compiler.impl.StringConstant; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; /** * Groovy can use these to ask questions of JDT bindings. They are only built as * required (as groovy references to java files are resolved). They remain * uninitialized until groovy starts digging into them - at that time the details * are filled in (eg. members). */ public class JDTClassNode extends ClassNode implements JDTNode { // arbitrary choice of first eight; maintaining these as a constant array prevents 10000 strings called 'arg0' consuming memory private final static String[] argNames = { "arg0", "arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "arg7" }; // The binding which this JDTClassNode represents ReferenceBinding jdtBinding; private boolean beingInitialized = false; private boolean anyGenericsInitialized = false; // The resolver instance involved at the moment JDTResolver resolver; // Configuration flags private int bits = 0; private static final int ANNOTATIONS_INITIALIZED = 0x0001; private static final int PROPERTIES_INITIALIZED = 0x0002; private TypeDeclaration groovyDecl = null; static final ClassNode unboundWildcard; // represents plain old '?' static final GenericsType genericsTypeUnboundWildcard; static { ClassNode base = ClassHelper.makeWithoutCaching("?"); base.setRedirect(ClassHelper.OBJECT_TYPE); // ClassNode[] allUppers = new ClassNode[] { ClassHelper.OBJECT_TYPE }; GenericsType t = new GenericsType(base, null, null); t.setName("?"); t.setWildcard(true); // Can't use the constant ClassHelper.OBJECT_TYPE here as we are about to setGenericTypes on it. unboundWildcard = ClassHelper.makeWithoutCaching("?"); unboundWildcard.setRedirect(ClassHelper.OBJECT_TYPE); genericsTypeUnboundWildcard = t; } /** * Create a new JDT ClassNode. Minimal setup is done initially (the superclass and superinterfaces are setup) and the rest of * the initialization is done later when required. */ public JDTClassNode(ReferenceBinding jdtReferenceBinding, JDTResolver resolver) { super(getName(jdtReferenceBinding), getMods(jdtReferenceBinding), null); this.jdtBinding = jdtReferenceBinding; this.resolver = resolver; // population of the methods/ctors/fields/etc is not done until required this.lazyInitDone = false; // a primary node will result in a class file this.isPrimaryNode = false; } private static String getName(TypeBinding tb) { if (tb instanceof ArrayBinding) { return String.valueOf(((ArrayBinding) tb).signature()); } else if (tb instanceof MemberTypeBinding) { MemberTypeBinding mtb = (MemberTypeBinding) tb; return CharOperation.toString(mtb.compoundName); } else if (tb instanceof ReferenceBinding) { return CharOperation.toString(((ReferenceBinding) tb).compoundName); } else { return String.valueOf(tb.sourceName()); } } private static int getMods(TypeBinding tb) { if (tb instanceof ReferenceBinding) { return ((ReferenceBinding) tb).modifiers; } else { // FIXASC need to be smarter here? Who is affected? return ClassFileConstants.AccPublic; } } @Override public void lazyClassInit() { synchronized (lazyInitLock) { if (lazyInitDone) { return; } initialize(); lazyInitDone = true; } } public void setupGenerics() { if (anyGenericsInitialized) { return; } try { if (jdtBinding instanceof ParameterizedTypeBinding && !(jdtBinding instanceof RawTypeBinding)) { // GenericsType[] gts = configureTypeArguments(((ParameterizedTypeBinding) jdtBinding).arguments); GenericsType[] gts = new JDTClassNodeBuilder(this.resolver) .configureTypeArguments(((ParameterizedTypeBinding) jdtBinding).arguments); setGenericsTypes(gts); // return base; } else if (jdtBinding instanceof RawTypeBinding) { // nothing to do } else { // SourceTB, BinaryTB, TypeVariableB, WildcardB TypeVariableBinding[] typeVariables = jdtBinding.typeVariables(); GenericsType[] generics = new JDTClassNodeBuilder(this.resolver).configureTypeVariables(typeVariables); if (generics != null) { this.setGenericsTypes(generics); } } } finally { anyGenericsInitialized = true; } } // JDTClassNodes are created because of a JDT Reference Binding file so are // always 'resolved' (although not initialized on creation) @Override public boolean isResolved() { return true; } public void setGenericsTypes(GenericsType[] genericsTypes) { this.anyGenericsInitialized = true; super.setGenericsTypes(genericsTypes); } /** * Basic initialization of the node - try and do most resolution lazily but some elements are worth getting correct up front: * superclass, superinterfaces */ // FIXASC confusing (and problematic?) that the superclass is setup after the generics information void initialize() { if (beingInitialized) { return; } try { beingInitialized = true; if (!jdtBinding.isInterface()) { ReferenceBinding superClass = jdtBinding.superclass(); if (superClass != null) { setUnresolvedSuperClass(resolver.convertToClassNode(superClass)); } } ReferenceBinding[] superInterfaceBindings = jdtBinding.superInterfaces(); superInterfaceBindings = superInterfaceBindings == null ? Binding.NO_SUPERINTERFACES : superInterfaceBindings; ClassNode[] interfaces = new ClassNode[superInterfaceBindings.length]; for (int i = 0; i < superInterfaceBindings.length; i++) { interfaces[i] = resolver.convertToClassNode(superInterfaceBindings[i]); } setInterfaces(interfaces); initializeMembers(); } finally { beingInitialized = false; } } private void initializeMembers() { if (jdtBinding instanceof SourceTypeBinding) { SourceTypeBinding sourceType = (SourceTypeBinding) jdtBinding; if (sourceType.scope != null) { TypeDeclaration typeDecl = sourceType.scope.referenceContext; if (typeDecl instanceof GroovyTypeDeclaration) { groovyDecl = typeDecl; } } } // We do this here rather than at the start of the method because // the preceding code sets 'groovyDecl', later used to 'initializeProperties'. // From this point onward... the code is only about initializing fields, constructors and methods. if (redirect != null) { // The code in ClassNode seems set up to get field information *always* from the end of the 'redirect' chain. // So, the redirect target should be responsible for its own members initialisation. // If we initialize members here again, when redirect target is already // initialised then we will be adding duplicated methods to the redirect target. return; } MethodBinding[] bindings = null; if (jdtBinding instanceof ParameterizedTypeBinding) { ReferenceBinding genericType = ((ParameterizedTypeBinding) jdtBinding).genericType(); bindings = genericType.methods(); } else { bindings = jdtBinding.methods(); } if (bindings != null) { for (int i = 0; i < bindings.length; i++) { if (bindings[i].isConstructor()) { ConstructorNode cNode = constructorBindingToConstructorNode(bindings[i]); addConstructor(cNode); } else { MethodNode mNode = methodBindingToMethodNode(bindings[i]); addMethod(mNode); } } } if (jdtBinding instanceof BinaryTypeBinding) { MethodBinding[] infraBindings = ((BinaryTypeBinding) jdtBinding).infraMethods(); for (int i = 0; i < infraBindings.length; i++) { if (infraBindings[i].isConstructor()) { ConstructorNode cNode = constructorBindingToConstructorNode(infraBindings[i]); addConstructor(cNode); } else { MethodNode mNode = methodBindingToMethodNode(infraBindings[i]); addMethod(mNode); } } } // Synthetic bindings are created for features like covariance, where the method implementing an interface method uses a // different return type (interface I { A foo(); } class C implements I { AA foo(); } - this needs a method 'A foo()' in C. if (jdtBinding instanceof SourceTypeBinding) { SourceTypeBinding jdtSourceTypeBinding = (SourceTypeBinding) jdtBinding; ClassScope classScope = jdtSourceTypeBinding.scope; // a null scope indicates it has already been 'cleaned up' so nothing to do (CUDeclaration.cleanUp()) if (classScope != null) { CompilationUnitScope cuScope = classScope.compilationUnitScope(); LookupEnvironment environment = classScope.environment(); MethodVerifier verifier = environment.methodVerifier(); cuScope.verifyMethods(verifier); } SyntheticMethodBinding[] syntheticMethodBindings = ((SourceTypeBinding) jdtBinding).syntheticMethods(); if (syntheticMethodBindings != null) { for (int i = 0; i < syntheticMethodBindings.length; i++) { if (syntheticMethodBindings[i].isConstructor()) { ConstructorNode cNode = constructorBindingToConstructorNode(bindings[i]); addConstructor(cNode); } else { MethodNode mNode = methodBindingToMethodNode(syntheticMethodBindings[i]); addMethod(mNode); } } } } FieldBinding[] fieldBindings = null; if (jdtBinding instanceof ParameterizedTypeBinding) { fieldBindings = ((ParameterizedTypeBinding) jdtBinding).genericType().fields(); } else { fieldBindings = jdtBinding.fields(); } if (fieldBindings != null) { for (int i = 0; i < fieldBindings.length; i++) { FieldNode fNode = fieldBindingToFieldNode(fieldBindings[i], groovyDecl); addField(fNode); } } } @Override public boolean mightHaveInners() { // return super.hasInnerClasses(); return jdtBinding.memberTypes().length != 0; } /** * Convert a JDT MethodBinding to a Groovy MethodNode */ private MethodNode methodBindingToMethodNode(MethodBinding methodBinding) { // FIXASC What value is there in getting the parameter names correct? (for methods and ctors) // If they need to be correct we need to retrieve the method decl from the binding scope String name = String.valueOf(methodBinding.selector); int modifiers = methodBinding.modifiers; if (jdtBinding.isInterface() && (modifiers & (0x10000 /*Modifier.DEFAULT*/ | Modifier.STATIC)) == 0) { modifiers |= Modifier.ABSTRACT; } ClassNode returnType = resolver.convertToClassNode(methodBinding.returnType); Parameter[] parameters = makeParameters(methodBinding.parameters); int nExceptions; ClassNode[] exceptions = ClassNode.EMPTY_ARRAY; if (methodBinding.thrownExceptions != null && (nExceptions = methodBinding.thrownExceptions.length) > 0) { exceptions = new ClassNode[nExceptions]; for (int i = 0; i < nExceptions; i += 1) { exceptions[i] = resolver.convertToClassNode(methodBinding.thrownExceptions[i]); } } MethodNode mNode = new JDTMethodNode(methodBinding, resolver, name, modifiers, returnType, parameters, exceptions, null /*body*/); // FIXASC (M3) likely to need something like this... //if (jdtBinding.isEnum() && methodBinding.getDefaultValue() != null) { // mNode.setAnnotationDefault(true); //} GenericsType[] generics = new JDTClassNodeBuilder(resolver).configureTypeVariables(methodBinding.typeVariables()); mNode.setGenericsTypes(generics); return mNode; } private Parameter[] makeParameters(TypeBinding[] jdtParameters) { int nParameters; Parameter[] parameters = Parameter.EMPTY_ARRAY; if (jdtParameters != null && (nParameters = jdtParameters.length) > 0) { parameters = new Parameter[nParameters]; for (int i = 0; i < nParameters; i += 1) { parameters[i] = makeParameter(jdtParameters[i], i); } } return parameters; } private Parameter makeParameter(TypeBinding parameterType, int parameterPosition) { TypeBinding erasureType; if (parameterType instanceof ParameterizedTypeBinding) { erasureType = ((ParameterizedTypeBinding) parameterType).genericType(); } else { erasureType = new JDTClassNodeBuilder(resolver).toRawType(parameterType); } ClassNode paramType = makeClassNode(parameterType, erasureType); String paramName = (parameterPosition < argNames.length ? argNames[parameterPosition] : "arg" + parameterPosition); return new Parameter(paramType, paramName); } /** * @param t type * @param c erasure of type */ private ClassNode makeClassNode(TypeBinding t, TypeBinding c) { ClassNode back = resolver.convertToClassNode(c); if (!((t instanceof BinaryTypeBinding) || (t instanceof SourceTypeBinding))) { ClassNode front = JDTClassNodeBuilder.build(this.resolver, t); front.setRedirect(back); return front; } return back; } public GenericsType[] getGenericsTypes() { ensureGenericsInitialized(); return genericsTypes; } @Override public boolean isUsingGenerics() { ensureGenericsInitialized(); return super.isUsingGenerics(); } private void ensureGenericsInitialized() { if (!anyGenericsInitialized) { setupGenerics(); } } private ConstructorNode constructorBindingToConstructorNode(MethodBinding methodBinding) { TypeVariableBinding[] typeVariables = methodBinding.typeVariables(); GenericsType[] generics = new JDTClassNodeBuilder(resolver).configureTypeVariables(typeVariables); ConstructorNode ctorNode = null; int modifiers = methodBinding.modifiers; Parameter[] parameters = makeParameters(methodBinding.parameters); ClassNode[] thrownExceptions = ClassNode.EMPTY_ARRAY; if (methodBinding.thrownExceptions != null) { thrownExceptions = new ClassNode[methodBinding.thrownExceptions.length]; for (int i = 0; i < methodBinding.thrownExceptions.length; i++) { thrownExceptions[i] = resolver.convertToClassNode(methodBinding.thrownExceptions[i]); } } ctorNode = new ConstructorNode(modifiers, parameters, thrownExceptions, null); ctorNode.setGenericsTypes(generics); return ctorNode; } private FieldNode fieldBindingToFieldNode(FieldBinding fieldBinding, TypeDeclaration groovyTypeDecl) { String name = String.valueOf(fieldBinding.name); int modifiers = fieldBinding.modifiers; ClassNode fieldType = resolver.convertToClassNode(fieldBinding.type); Constant c = fieldBinding.constant(); Expression initializerExpression = null; // FIXASC for performance reasons could fetch the initializer lazily if a JDTFieldNode were created if (c == Constant.NotAConstant) { /** * If the field binding is for a real source field, we should be able to see any initializer in it. */ if (groovyTypeDecl != null) { FieldDeclaration fieldDecl = groovyTypeDecl.declarationOf(fieldBinding); if (fieldDecl instanceof FieldDeclarationWithInitializer) { initializerExpression = ((FieldDeclarationWithInitializer) fieldDecl).getGroovyInitializer(); } } } else { if (c instanceof StringConstant) { initializerExpression = new ConstantExpression(((StringConstant) c).stringValue()); } else if (c instanceof BooleanConstant) { initializerExpression = new ConstantExpression(((BooleanConstant) c).booleanValue()); } else if (c instanceof IntConstant) { initializerExpression = new ConstantExpression(((IntConstant) c).intValue()); } else if (c instanceof LongConstant) { initializerExpression = new ConstantExpression(((LongConstant) c).longValue()); } else if (c instanceof DoubleConstant) { initializerExpression = new ConstantExpression(((DoubleConstant) c).doubleValue()); } else if (c instanceof FloatConstant) { initializerExpression = new ConstantExpression(((FloatConstant) c).floatValue()); } else if (c instanceof ByteConstant) { initializerExpression = new ConstantExpression(((ByteConstant) c).byteValue()); } else if (c instanceof CharConstant) { initializerExpression = new ConstantExpression(((CharConstant) c).charValue()); } else if (c instanceof ShortConstant) { initializerExpression = new ConstantExpression(((ShortConstant) c).shortValue()); } } FieldNode fNode = new JDTFieldNode(fieldBinding, resolver, name, modifiers, fieldType, this, initializerExpression); return fNode; } @Override public boolean isReallyResolved() { return true; } @Override public String getClassInternalName() { return getName().replace('.', '/'); } @Override public boolean isPrimitive() { // FIXASC (M3) verify always true. Think it is a jdtReferenceBinding is a // reference binding and not a typebinding return false; } /** * Annotations on a JDTClassNode are initialized lazily when requested. */ @Override public List<AnnotationNode> getAnnotations() { ensureAnnotationsInitialized(); return super.getAnnotations(); } @Override public List<AnnotationNode> getAnnotations(ClassNode type) { ensureAnnotationsInitialized(); return super.getAnnotations(type); } private synchronized void ensureAnnotationsInitialized() { if ((bits & ANNOTATIONS_INITIALIZED) == 0) { if ((jdtBinding instanceof SourceTypeBinding)) { // ensure resolved ((SourceTypeBinding) jdtBinding).getAnnotationTagBits(); } AnnotationBinding[] annotationBindings = jdtBinding.getAnnotations(); for (AnnotationBinding annotationBinding : annotationBindings) { addAnnotation(new JDTAnnotationNode(annotationBinding, this.resolver)); } bits |= ANNOTATIONS_INITIALIZED; } } protected void ensurePropertiesInitialized() { if ((bits & PROPERTIES_INITIALIZED) == 0) { initializeProperties(); } } protected synchronized void initializeProperties() { if ((bits & PROPERTIES_INITIALIZED) == 0) { lazyClassInit(); // getX methods // make it behave like groovy - no property nodes unless it is groovy source if (groovyDecl != null) { Set<String> existing = new HashSet<String>(); for (MethodNode methodNode : getMethods()) { if (isGetter(methodNode)) { // STS-2628 be careful not to double-add properties if there is a getter and an isser variant String propertyName = convertToPropertyName(methodNode.getName()); if (!existing.contains(propertyName)) { existing.add(propertyName); // Adding a real field for these accessors can trip up CompileStatic which // will attempt to access it as a real field super.addPropertyWithoutField(createPropertyNodeForMethodNode(methodNode, propertyName)); // super.addProperty(createPropertyNodeForMethodNode(methodNode, propertyName)); } } } // fields - FIXASC nyi for fields // for (FieldNode fieldNode : getFields()) { // super.addProperty(createPropertyNodeFromFieldNode(fieldNode)); // } } bits |= PROPERTIES_INITIALIZED; } } private PropertyNode createPropertyNodeForMethodNode(MethodNode methodNode, String propertyName) { ClassNode propertyType = methodNode.getReturnType(); int mods = methodNode.getModifiers(); FieldNode field = this.getField(propertyName); if (field == null) { field = new FieldNode(propertyName, mods, propertyType, this, null); field.setDeclaringClass(this); } else { // field already exists // must remove this field since when "addProperty" is called // later on, it will add it again. We do not want dups. this.removeField(propertyName); } PropertyNode property = new PropertyNode(field, mods, null, null); property.setDeclaringClass(this); return property; } /** * Converts from a method get/set/is name to a property name. * Assumes that methodName is more than 4/3 characters long and starts with a proper prefix. */ private String convertToPropertyName(String methodName) { StringBuilder propertyName = new StringBuilder(); int prefixLen; if (methodName.startsWith("is")) { prefixLen = 2; } else { prefixLen = 3; } propertyName.append(Character.toLowerCase(methodName.charAt(prefixLen))); if (methodName.length() > prefixLen + 1) { propertyName.append(methodName.substring(prefixLen + 1)); } String name = propertyName.toString(); return name; } /** * @return {@code true} if the methodNode looks like a setter method for a property: * method starting set<Something> with a void return type and taking one parameter */ @SuppressWarnings("unused") private boolean isSetter(MethodNode methodNode) { return methodNode.getReturnType() == ClassHelper.VOID_TYPE && methodNode.getParameters().length == 1 && methodNode.getName().startsWith("set") && methodNode.getName().length() > 3; } /** * @return {@code true} if the methodNode looks like a getter method for a property: * method starting get<Something> with a non void return type and taking no parameters */ private boolean isGetter(MethodNode methodNode) { return methodNode.getReturnType() != ClassHelper.VOID_TYPE && methodNode.getParameters().length == 0 && ((methodNode.getName().startsWith("get") && methodNode.getName().length() > 3) || (methodNode.getName().startsWith("is") && methodNode.getName().length() > 2)); } @Override public List<PropertyNode> getProperties() { ensurePropertiesInitialized(); return super.getProperties(); } @Override public PropertyNode getProperty(String name) { ensurePropertiesInitialized(); return super.getProperty(name); } @Override public boolean hasProperty(String name) { ensurePropertiesInitialized(); return super.hasProperty(name); } @Override public void addProperty(PropertyNode node) { new RuntimeException("JDTClassNode is immutable, should not be called to add property: " + node.getName()).printStackTrace(); } @Override public PropertyNode addProperty(String name, int modifiers, ClassNode type, Expression initialValueExpression, Statement getterBlock, Statement setterBlock) { new RuntimeException("JDTClassNode is immutable, should not be called to add property: " + name).printStackTrace(); return null; } public ReferenceBinding getJdtBinding() { return jdtBinding; } public JDTResolver getResolver() { return resolver; } public boolean isDeprecated() { return jdtBinding.isDeprecated(); } private boolean unfindable = false; /** * Some AST transforms are written such that they refer to typeClass on a ClassNode. * This is not available under Eclipse. However, we can support it in a rudimentary * fashion by attempting a class load for the class using the transform loader (if * available). */ public Class getTypeClass() { if (clazz != null || unfindable) { return clazz; } ClassLoader transformLoader = resolver.compilationUnit.getTransformLoader(); if (transformLoader != null) { // What about array types try { clazz = Class.forName(this.getName(), false, transformLoader); return clazz; } catch (ClassNotFoundException e) { unfindable = true; } } throw new GroovyBugError("JDTClassNode.getTypeClass() cannot locate class for " + getName() + " using transform loader " + transformLoader); } // When working with parameterized types, groovy will create a simple ClassNode for the raw type and then initialize the // generics structure behind it. This setter is used to ensure that these 'simple' ClassNodes that are created for raw types // but are intended to represent parameterized types will have their generics info available (from the parameterized // jdt binding). public void setJdtBinding(ReferenceBinding parameterizedType) { this.jdtBinding = parameterizedType; } }