lombok.eclipse.handlers.EclipseHandlerUtil Maven / Gradle / Ivy
/*
* Copyright (C) 2009-2021 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.EcjAugments.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.EclipseReflectiveMembers.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.AssertStatement;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.CharLiteral;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.DoubleLiteral;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ExtendedStringLiteral;
import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.FloatLiteral;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
import org.eclipse.jdt.internal.compiler.ast.Literal;
import org.eclipse.jdt.internal.compiler.ast.LongLiteral;
import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.StringLiteralConcatenation;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding;
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.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.Data;
import lombok.Getter;
import lombok.Lombok;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.LombokImmutableList;
import lombok.core.TypeResolver;
import lombok.core.configuration.CheckerFrameworkVersion;
import lombok.core.configuration.NullAnnotationLibrary;
import lombok.core.configuration.NullCheckExceptionType;
import lombok.core.configuration.TypeName;
import lombok.core.debug.ProblemReporter;
import lombok.core.handlers.HandlerUtil;
import lombok.core.handlers.HandlerUtil.FieldAccess;
import lombok.eclipse.EcjAugments;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAST;
import lombok.eclipse.EclipseNode;
import lombok.experimental.Accessors;
import lombok.experimental.Tolerate;
import lombok.permit.Permit;
/**
* Container for static utility methods useful to handlers written for eclipse.
*/
public class EclipseHandlerUtil {
private EclipseHandlerUtil() {
//Prevent instantiation
}
/**
* Generates an error in the Eclipse error log. Note that most people never look at it!
*
* @param cud The {@code CompilationUnitDeclaration} where the error occurred.
* An error will be generated on line 0 linking to the error log entry. Can be {@code null}.
* @param message Human readable description of the problem.
* @param ex The associated exception. Can be {@code null}.
*/
public static void error(CompilationUnitDeclaration cud, String message, Throwable ex) {
ProblemReporter.error(message, ex);
if (cud != null) EclipseAST.addProblemToCompilationResult(cud.getFileName(), cud.compilationResult, false, message + " - See error log.", 0, 0);
}
/**
* Generates a warning in the Eclipse error log. Note that most people never look at it!
*
* @param message Human readable description of the problem.
* @param ex The associated exception. Can be {@code null}.
*/
public static void warning(String message, Throwable ex) {
ProblemReporter.warning(message, ex);
}
public static ASTNode getGeneratedBy(ASTNode node) {
return ASTNode_generatedBy.get(node);
}
public static boolean isGenerated(ASTNode node) {
return getGeneratedBy(node) != null;
}
public static T setGeneratedBy(T node, ASTNode source) {
ASTNode_generatedBy.set(node, source);
return node;
}
public static MarkerAnnotation generateDeprecatedAnnotation(ASTNode source) {
QualifiedTypeReference qtr = new QualifiedTypeReference(new char[][] {
{'j', 'a', 'v', 'a'}, {'l', 'a', 'n', 'g'}, {'D', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd'}}, poss(source, 3));
setGeneratedBy(qtr, source);
MarkerAnnotation ma = new MarkerAnnotation(qtr, source.sourceStart);
// No matter what value you input for sourceEnd, the AST->DOM converter of eclipse will reparse to find the end, and will fail as
// it can't find code that isn't really there. This results in the end position being set to 2 or 0 or some weird magic value, and thus,
// length, as calculated by end-start, is all screwed up, resulting in IllegalArgumentException during a setSourceRange call MUCH later in the process.
// We solve it by going with a voodoo magic source start value such that the calculated length so happens to exactly be 0. 0 lengths are accepted
// by eclipse. For some reason.
// TL;DR: Don't change 1. 1 is sacred. Trust the 1.
// issue: #408.
ma.sourceStart = 1;
setGeneratedBy(ma, source);
return ma;
}
public static MarkerAnnotation generateNamedAnnotation(ASTNode source, String typeName) {
char[][] cc = fromQualifiedName(typeName);
QualifiedTypeReference qtr = new QualifiedTypeReference(cc, poss(source, cc.length));
setGeneratedBy(qtr, source);
MarkerAnnotation ma = new MarkerAnnotation(qtr, source.sourceStart);
// No matter what value you input for sourceEnd, the AST->DOM converter of eclipse will reparse to find the end, and will fail as
// it can't find code that isn't really there. This results in the end position being set to 2 or 0 or some weird magic value, and thus,
// length, as calculated by end-start, is all screwed up, resulting in IllegalArgumentException during a setSourceRange call MUCH later in the process.
// We solve it by going with a voodoo magic source start value such that the calculated length so happens to exactly be 0. 0 lengths are accepted
// by eclipse. For some reason.
// TL;DR: Don't change 1. 1 is sacred. Trust the 1.
// issue: #408.
ma.sourceStart = 1;
setGeneratedBy(ma, source);
return ma;
}
public static boolean isFieldDeprecated(EclipseNode fieldNode) {
if (!(fieldNode.get() instanceof FieldDeclaration)) return false;
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
if ((field.modifiers & ClassFileConstants.AccDeprecated) != 0) {
return true;
}
if (field.annotations == null) return false;
for (Annotation annotation : field.annotations) {
if (typeMatches(Deprecated.class, fieldNode, annotation.type)) {
return true;
}
}
return false;
}
public static CheckerFrameworkVersion getCheckerFrameworkVersion(EclipseNode node) {
CheckerFrameworkVersion cfv = node.getAst().readConfiguration(ConfigurationKeys.CHECKER_FRAMEWORK);
return cfv != null ? cfv : CheckerFrameworkVersion.NONE;
}
/**
* Checks if the given TypeReference node is likely to be a reference to the provided class.
*
* @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type.
* @param node A Lombok AST node. Any node in the appropriate compilation unit will do (used to get access to import statements).
* @param typeRef A type reference to check.
*/
public static boolean typeMatches(Class> type, EclipseNode node, TypeReference typeRef) {
return typeMatches(type.getName(), node, typeRef);
}
/**
* Checks if the given TypeReference node is likely to be a reference to the provided class.
*
* @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type.
* @param node A Lombok AST node. Any node in the appropriate compilation unit will do (used to get access to import statements).
* @param typeRef A type reference to check.
*/
public static boolean typeMatches(String type, EclipseNode node, TypeReference typeRef) {
char[][] tn = typeRef == null ? null : typeRef.getTypeName();
if (tn == null || tn.length == 0) return false;
char[] lastPartA = tn[tn.length - 1];
int lastIndex = Math.max(type.lastIndexOf('.'), type.lastIndexOf('$')) + 1;
if (lastPartA.length != type.length() - lastIndex) return false;
for (int i = 0; i < lastPartA.length; i++) if (lastPartA[i] != type.charAt(i + lastIndex)) return false;
String typeName = toQualifiedName(tn);
TypeResolver resolver = node.getImportListAsTypeResolver();
return resolver.typeMatches(node, type, typeName);
}
public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(EclipseNode typeNode, EclipseNode errorNode) {
List disallowed = null;
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.ANNOTATION) continue;
for (String annType : INVALID_ON_BUILDERS) {
if (annotationTypeMatches(annType, child)) {
if (disallowed == null) disallowed = new ArrayList();
int lastIndex = annType.lastIndexOf('.');
disallowed.add(lastIndex == -1 ? annType : annType.substring(lastIndex + 1));
}
}
}
int size = disallowed == null ? 0 : disallowed.size();
if (size == 0) return;
if (size == 1) {
errorNode.addError("@" + disallowed.get(0) + " is not allowed on builder classes.");
return;
}
StringBuilder out = new StringBuilder();
for (String a : disallowed) out.append("@").append(a).append(", ");
out.setLength(out.length() - 2);
errorNode.addError(out.append(" are not allowed on builder classes.").toString());
}
public static Annotation copyAnnotation(Annotation annotation, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
if (annotation instanceof MarkerAnnotation) {
MarkerAnnotation ann = new MarkerAnnotation(copyType(annotation.type, source), pS);
setGeneratedBy(ann, source);
ann.declarationSourceEnd = ann.sourceEnd = ann.statementEnd = pE;
copyMemberValuePairName(ann, annotation);
return ann;
}
if (annotation instanceof SingleMemberAnnotation) {
SingleMemberAnnotation ann = new SingleMemberAnnotation(copyType(annotation.type, source), pS);
setGeneratedBy(ann, source);
ann.declarationSourceEnd = ann.sourceEnd = ann.statementEnd = pE;
ann.memberValue = copyAnnotationMemberValue(((SingleMemberAnnotation) annotation).memberValue);
copyMemberValuePairName(ann, annotation);
return ann;
}
if (annotation instanceof NormalAnnotation) {
NormalAnnotation ann = new NormalAnnotation(copyType(annotation.type, source), pS);
setGeneratedBy(ann, source);
ann.declarationSourceEnd = ann.statementEnd = ann.sourceEnd = pE;
MemberValuePair[] inPairs = ((NormalAnnotation) annotation).memberValuePairs;
if (inPairs == null) {
ann.memberValuePairs = null;
} else {
ann.memberValuePairs = new MemberValuePair[inPairs.length];
for (int i = 0; i < inPairs.length; i++) ann.memberValuePairs[i] =
new MemberValuePair(inPairs[i].name, inPairs[i].sourceStart, inPairs[i].sourceEnd, copyAnnotationMemberValue(inPairs[i].value));
}
copyMemberValuePairName(ann, annotation);
return ann;
}
return annotation;
}
private static void copyMemberValuePairName(Annotation source, Annotation target) {
if (ANNOTATION__MEMBER_VALUE_PAIR_NAME == null) return;
try {
reflectSet(ANNOTATION__MEMBER_VALUE_PAIR_NAME, source, reflect(ANNOTATION__MEMBER_VALUE_PAIR_NAME, target));
} catch (Exception ignore) { /* Various eclipse versions don't have it */ }
}
static class EclipseReflectiveMembers {
public static final Field STRING_LITERAL__LINE_NUMBER;
public static final Field ANNOTATION__MEMBER_VALUE_PAIR_NAME;
public static final Field TYPE_REFERENCE__ANNOTATIONS;
public static final Class> INTERSECTION_BINDING1, INTERSECTION_BINDING2;
public static final Field INTERSECTION_BINDING_TYPES1, INTERSECTION_BINDING_TYPES2;
public static final Field TYPE_DECLARATION_RECORD_COMPONENTS;
public static final Class> COMPILATION_UNIT;
public static final Method COMPILATION_UNIT_ORIGINAL_FROM_CLONE;
static {
STRING_LITERAL__LINE_NUMBER = getField(StringLiteral.class, "lineNumber");
ANNOTATION__MEMBER_VALUE_PAIR_NAME = getField(Annotation.class, "memberValuePairName");
TYPE_REFERENCE__ANNOTATIONS = getField(TypeReference.class, "annotations");
INTERSECTION_BINDING1 = getClass("org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18");
INTERSECTION_BINDING2 = getClass("org.eclipse.jdt.internal.compiler.lookup.IntersectionCastTypeBinding");
INTERSECTION_BINDING_TYPES1 = INTERSECTION_BINDING1 == null ? null : getField(INTERSECTION_BINDING1, "intersectingTypes");
INTERSECTION_BINDING_TYPES2 = INTERSECTION_BINDING2 == null ? null : getField(INTERSECTION_BINDING2, "intersectingTypes");
TYPE_DECLARATION_RECORD_COMPONENTS = getField(TypeDeclaration.class, "recordComponents");
COMPILATION_UNIT = getClass("org.eclipse.jdt.internal.core.CompilationUnit");
COMPILATION_UNIT_ORIGINAL_FROM_CLONE = COMPILATION_UNIT == null ? null : Permit.permissiveGetMethod(COMPILATION_UNIT, "originalFromClone");
}
public static int reflectInt(Field f, Object o) {
try {
return ((Number) f.get(o)).intValue();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void reflectSet(Field f, Object o, Object v) {
try {
f.set(o, v);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static Object reflect(Field f, Object o) {
try {
return f.get(o);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private static Class> getClass(String fqn) {
try {
return Class.forName(fqn);
} catch (Throwable t) {
return null;
}
}
private static Field getField(Class> c, String fName) {
try {
return Permit.getField(c, fName);
} catch (Throwable t) {
return null;
}
}
}
public static Expression copyAnnotationMemberValue(Expression in) {
Expression out = copyAnnotationMemberValue0(in);
out.constant = in.constant;
return out;
}
private static Expression copyAnnotationMemberValue0(Expression in) {
int s = in.sourceStart, e = in.sourceEnd;
// literals
if (in instanceof FalseLiteral) return new FalseLiteral(s, e);
if (in instanceof TrueLiteral) return new TrueLiteral(s, e);
if (in instanceof NullLiteral) return new NullLiteral(s, e);
if (in instanceof CharLiteral) return new CharLiteral(((Literal) in).source(), s, e);
if (in instanceof DoubleLiteral) return new DoubleLiteral(((Literal) in).source(), s, e);
if (in instanceof FloatLiteral) return new FloatLiteral(((Literal) in).source(), s, e);
if (in instanceof IntLiteral) return IntLiteral.buildIntLiteral(((Literal) in).source(), s, e);
if (in instanceof LongLiteral) return LongLiteral.buildLongLiteral(((Literal) in).source(), s, e);
if (in instanceof StringLiteral) return new StringLiteral(((Literal) in).source(), s, e, reflectInt(STRING_LITERAL__LINE_NUMBER, in) + 1);
if (in instanceof ExtendedStringLiteral) {
StringLiteral str = new StringLiteral(((Literal) in).source(), s, e, reflectInt(STRING_LITERAL__LINE_NUMBER, in) + 1);
StringLiteral empty = new StringLiteral(new char[0], s, e, reflectInt(STRING_LITERAL__LINE_NUMBER, in) + 1);
return new ExtendedStringLiteral(str, empty);
}
if (in instanceof StringLiteralConcatenation) {
Expression[] literals = ((StringLiteralConcatenation) in).literals;
// 0 and 1 len shouldn't happen.
if (literals.length == 0) return new StringLiteral(new char[0], s, e, 0);
if (literals.length == 1) return copyAnnotationMemberValue0(literals[0]);
StringLiteralConcatenation c = new StringLiteralConcatenation((StringLiteral) literals[0], (StringLiteral) literals[1]);
for (int i = 2; i < literals.length; i++) c = c.extendsWith((StringLiteral) literals[i]);
return c;
}
// enums and field accesses (as long as those are references to compile time constant literals that's also acceptable)
if (in instanceof SingleNameReference) {
SingleNameReference snr = (SingleNameReference) in;
return new SingleNameReference(snr.token, pos(in));
}
if (in instanceof QualifiedNameReference) {
QualifiedNameReference qnr = (QualifiedNameReference) in;
return new QualifiedNameReference(qnr.tokens, poss(in, qnr.tokens.length), s, e);
}
// class refs
if (in instanceof ClassLiteralAccess) return new ClassLiteralAccess(e, copyType(((ClassLiteralAccess) in).type));
// arrays
if (in instanceof ArrayInitializer) {
Expression[] exprs = ((ArrayInitializer) in).expressions;
Expression[] copy = new Expression[exprs.length];
for (int i = 0; i < exprs.length; i++) copy[i] = copyAnnotationMemberValue(exprs[i]);
ArrayInitializer out = new ArrayInitializer();
out.sourceStart = s;
out.sourceEnd = e;
out.bits = in.bits;
out.implicitConversion = in.implicitConversion;
out.statementEnd = e;
out.expressions = copy;
return out;
}
if (in instanceof BinaryExpression) {
BinaryExpression be = (BinaryExpression) in;
BinaryExpression out = new BinaryExpression(be);
out.left = copyAnnotationMemberValue(be.left);
out.right = copyAnnotationMemberValue(be.right);
out.sourceStart = s;
out.sourceEnd = e;
out.statementEnd = e;
return out;
}
return in;
}
/**
* You can't share TypeParameter objects or bad things happen; for example, one 'T' resolves differently
* from another 'T', even for the same T in a single class file. Unfortunately the TypeParameter type hierarchy
* is complicated and there's no clone method on TypeParameter itself. This method can clone them.
*/
public static TypeParameter[] copyTypeParams(TypeParameter[] params, ASTNode source) {
if (params == null) return null;
TypeParameter[] out = new TypeParameter[params.length];
int idx = 0;
for (TypeParameter param : params) {
TypeParameter o = new TypeParameter();
setGeneratedBy(o, source);
o.annotations = param.annotations;
o.bits = param.bits;
o.modifiers = param.modifiers;
o.name = param.name;
o.type = copyType(param.type, source);
o.sourceStart = param.sourceStart;
o.sourceEnd = param.sourceEnd;
o.declarationEnd = param.declarationEnd;
o.declarationSourceStart = param.declarationSourceStart;
o.declarationSourceEnd = param.declarationSourceEnd;
if (param.bounds != null) {
TypeReference[] b = new TypeReference[param.bounds.length];
int idx2 = 0;
for (TypeReference ref : param.bounds) b[idx2++] = copyType(ref, source);
o.bounds = b;
}
out[idx++] = o;
}
return out;
}
public static Annotation[] getTypeUseAnnotations(TypeReference from) {
Annotation[][] a;
try {
a = (Annotation[][]) reflect(TYPE_REFERENCE__ANNOTATIONS, from);
} catch (Exception e) {
return null;
}
if (a == null) return null;
Annotation[] b = a[a.length - 1];
return b.length == 0 ? null : b;
}
public static void removeTypeUseAnnotations(TypeReference from) {
try {
reflectSet(TYPE_REFERENCE__ANNOTATIONS, from, null);
} catch (Exception ignore) {}
}
public static TypeReference namePlusTypeParamsToTypeReference(EclipseNode type, TypeParameter[] params, long p) {
TypeDeclaration td = (TypeDeclaration) type.get();
boolean instance = (td.modifiers & MODIFIERS_INDICATING_STATIC) == 0;
return namePlusTypeParamsToTypeReference(type.up(), td.name, instance, params, p);
}
public static TypeReference namePlusTypeParamsToTypeReference(EclipseNode parentType, char[] typeName, boolean instance, TypeParameter[] params, long p) {
if (params != null && params.length > 0) {
TypeReference[] refs = new TypeReference[params.length];
int idx = 0;
for (TypeParameter param : params) {
TypeReference typeRef = new SingleTypeReference(param.name, p);
refs[idx++] = typeRef;
}
return generateParameterizedTypeReference(parentType, typeName, instance, refs, p);
}
return generateTypeReference(parentType, typeName, instance, p);
}
public static TypeReference[] copyTypes(TypeReference[] refs) {
return copyTypes(refs, null);
}
/**
* Convenience method that creates a new array and copies each TypeReference in the source array via
* {@link #copyType(TypeReference, ASTNode)}.
*/
public static TypeReference[] copyTypes(TypeReference[] refs, ASTNode source) {
if (refs == null) return null;
TypeReference[] outs = new TypeReference[refs.length];
int idx = 0;
for (TypeReference ref : refs) {
outs[idx++] = copyType(ref, source);
}
return outs;
}
public static TypeReference copyType(TypeReference ref) {
return copyType(ref, null);
}
/**
* You can't share TypeReference objects or subtle errors start happening.
* Unfortunately the TypeReference type hierarchy is complicated and there's no clone
* method on TypeReference itself. This method can clone them.
*/
public static TypeReference copyType(TypeReference ref, ASTNode source) {
if (ref instanceof ParameterizedQualifiedTypeReference) {
ParameterizedQualifiedTypeReference iRef = (ParameterizedQualifiedTypeReference) ref;
TypeReference[][] args = null;
if (iRef.typeArguments != null) {
args = new TypeReference[iRef.typeArguments.length][];
int idx = 0;
for (TypeReference[] inRefArray : iRef.typeArguments) {
if (inRefArray == null) args[idx++] = null;
else {
TypeReference[] outRefArray = new TypeReference[inRefArray.length];
int idx2 = 0;
for (TypeReference inRef : inRefArray) {
outRefArray[idx2++] = copyType(inRef, source);
}
args[idx++] = outRefArray;
}
}
}
TypeReference typeRef = new ParameterizedQualifiedTypeReference(iRef.tokens, args, iRef.dimensions(), copy(iRef.sourcePositions));
copyTypeAnns(ref, typeRef);
if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof ArrayQualifiedTypeReference) {
ArrayQualifiedTypeReference iRef = (ArrayQualifiedTypeReference) ref;
TypeReference typeRef = new ArrayQualifiedTypeReference(iRef.tokens, iRef.dimensions(), copy(iRef.sourcePositions));
copyTypeAnns(ref, typeRef);
if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof QualifiedTypeReference) {
QualifiedTypeReference iRef = (QualifiedTypeReference) ref;
TypeReference typeRef = new QualifiedTypeReference(iRef.tokens, copy(iRef.sourcePositions));
copyTypeAnns(ref, typeRef);
if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof ParameterizedSingleTypeReference) {
ParameterizedSingleTypeReference iRef = (ParameterizedSingleTypeReference) ref;
TypeReference[] args = null;
if (iRef.typeArguments != null) {
args = new TypeReference[iRef.typeArguments.length];
int idx = 0;
for (TypeReference inRef : iRef.typeArguments) {
if (inRef == null) args[idx++] = null;
else args[idx++] = copyType(inRef, source);
}
}
TypeReference typeRef = new ParameterizedSingleTypeReference(iRef.token, args, iRef.dimensions(), (long) iRef.sourceStart << 32 | iRef.sourceEnd);
copyTypeAnns(ref, typeRef);
if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof ArrayTypeReference) {
ArrayTypeReference iRef = (ArrayTypeReference) ref;
TypeReference typeRef = new ArrayTypeReference(iRef.token, iRef.dimensions(), (long) iRef.sourceStart << 32 | iRef.sourceEnd);
copyTypeAnns(ref, typeRef);
if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof Wildcard) {
Wildcard original = (Wildcard) ref;
Wildcard wildcard = new Wildcard(original.kind);
wildcard.sourceStart = original.sourceStart;
wildcard.sourceEnd = original.sourceEnd;
if (original.bound != null) wildcard.bound = copyType(original.bound, source);
copyTypeAnns(ref, wildcard);
if (source != null) setGeneratedBy(wildcard, source);
return wildcard;
}
if (ref instanceof SingleTypeReference) {
SingleTypeReference iRef = (SingleTypeReference) ref;
TypeReference typeRef = new SingleTypeReference(iRef.token, (long) iRef.sourceStart << 32 | iRef.sourceEnd);
copyTypeAnns(ref, typeRef);
if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
return ref;
}
private static void copyTypeAnns(TypeReference in, TypeReference out) {
Annotation[][] a;
try {
a = (Annotation[][]) reflect(TYPE_REFERENCE__ANNOTATIONS, in);
} catch (Exception e) {
return;
}
if (a == null) {
reflectSet(TYPE_REFERENCE__ANNOTATIONS, out, null);
return;
}
Annotation[][] b = new Annotation[a.length][];
for (int i = 0; i < a.length; i++) {
if (a[i] != null) {
b[i] = new Annotation[a[i].length];
for (int j = 0 ; j < a[i].length; j++) {
b[i][j] = copyAnnotation(a[i][j], a[i][j]);
}
}
}
reflectSet(TYPE_REFERENCE__ANNOTATIONS, out, b);
}
public static Annotation[] copyAnnotations(ASTNode source, Annotation[]... allAnnotations) {
List result = null;
for (Annotation[] annotations : allAnnotations) {
if (annotations != null) {
for (Annotation annotation : annotations) {
if (result == null) result = new ArrayList();
result.add(copyAnnotation(annotation, source));
}
}
}
return result == null ? null : result.toArray(new Annotation[0]);
}
public static boolean hasAnnotation(Class extends java.lang.annotation.Annotation> type, EclipseNode node) {
if (node == null) return false;
if (type == null) return false;
switch (node.getKind()) {
case ARGUMENT:
case FIELD:
case LOCAL:
case TYPE:
case METHOD:
for (EclipseNode child : node.down()) {
if (annotationTypeMatches(type, child)) return true;
}
// intentional fallthrough
default:
return false;
}
}
public static boolean hasAnnotation(String type, EclipseNode node) {
if (node == null) return false;
if (type == null) return false;
switch (node.getKind()) {
case ARGUMENT:
case FIELD:
case LOCAL:
case TYPE:
case METHOD:
for (EclipseNode child : node.down()) {
if (annotationTypeMatches(type, child)) return true;
}
// intentional fallthrough
default:
return false;
}
}
public static EclipseNode findInnerClass(EclipseNode parent, String name) {
char[] c = name.toCharArray();
for (EclipseNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
TypeDeclaration td = (TypeDeclaration) child.get();
if (Arrays.equals(td.name, c)) return child;
}
return null;
}
public static EclipseNode findAnnotation(Class extends java.lang.annotation.Annotation> type, EclipseNode node) {
if (node == null) return null;
if (type == null) return null;
switch (node.getKind()) {
case ARGUMENT:
case FIELD:
case LOCAL:
case TYPE:
case METHOD:
for (EclipseNode child : node.down()) {
if (annotationTypeMatches(type, child)) return child;
}
// intentional fallthrough
default:
return null;
}
}
public static String scanForNearestAnnotation(EclipseNode node, String... anns) {
while (node != null) {
for (EclipseNode ann : node.down()) {
if (ann.getKind() != Kind.ANNOTATION) continue;
Annotation a = (Annotation) ann.get();
TypeReference aType = a.type;
for (String annToFind : anns) if (typeMatches(annToFind, node, aType)) return annToFind;
}
node = node.up();
}
return null;
}
public static boolean hasNonNullAnnotations(EclipseNode node) {
for (EclipseNode child : node.down()) {
if (child.getKind() != Kind.ANNOTATION) continue;
Annotation annotation = (Annotation) child.get();
for (String bn : NONNULL_ANNOTATIONS) if (typeMatches(bn, node, annotation.type)) return true;
}
return false;
}
public static boolean hasNonNullAnnotations(EclipseNode node, List anns) {
if (anns == null) return false;
for (Annotation annotation : anns) {
TypeReference typeRef = annotation.type;
if (typeRef != null && typeRef.getTypeName() != null) {
for (String bn : NONNULL_ANNOTATIONS) if (typeMatches(bn, node, typeRef)) return true;
}
}
return false;
}
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
/**
* Searches the given field node for annotations and returns each one that is 'copyable' (either via configuration or from the base list).
*/
public static Annotation[] findCopyableAnnotations(EclipseNode node) {
AbstractVariableDeclaration avd = (AbstractVariableDeclaration) node.get();
if (avd.annotations == null) return EMPTY_ANNOTATIONS_ARRAY;
List result = new ArrayList();
List configuredCopyable = node.getAst().readConfiguration(ConfigurationKeys.COPYABLE_ANNOTATIONS);
for (Annotation annotation : avd.annotations) {
TypeReference typeRef = annotation.type;
boolean match = false;
if (typeRef != null && typeRef.getTypeName() != null) {
for (TypeName cn : configuredCopyable) if (cn != null && typeMatches(cn.toString(), node, typeRef)) {
result.add(annotation);
match = true;
break;
}
if (!match) for (String bn : BASE_COPYABLE_ANNOTATIONS) if (typeMatches(bn, node, typeRef)) {
result.add(annotation);
break;
}
}
}
return result.toArray(EMPTY_ANNOTATIONS_ARRAY);
}
/**
* Searches the given field node for annotations that are specifically intentioned to be copied to the setter.
*/
public static Annotation[] findCopyableToSetterAnnotations(EclipseNode node) {
return findAnnotationsInList(node, COPY_TO_SETTER_ANNOTATIONS);
}
/**
* Searches the given field node for annotations that are specifically intentioned to be copied to the builder's singular method.
*/
public static Annotation[] findCopyableToBuilderSingularSetterAnnotations(EclipseNode node) {
return findAnnotationsInList(node, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS);
}
/**
* Searches the given field node for annotations that are in the given list, and returns those.
*/
private static Annotation[] findAnnotationsInList(EclipseNode node, java.util.List annotationsToFind) {
AbstractVariableDeclaration avd = (AbstractVariableDeclaration) node.get();
if (avd.annotations == null) return EMPTY_ANNOTATIONS_ARRAY;
List result = new ArrayList();
for (Annotation annotation : avd.annotations) {
TypeReference typeRef = annotation.type;
if (typeRef != null && typeRef.getTypeName() != null) {
for (String bn : annotationsToFind) if (typeMatches(bn, node, typeRef)) {
result.add(annotation);
break;
}
}
}
return result.toArray(EMPTY_ANNOTATIONS_ARRAY);
}
/**
* Checks if the provided annotation type is likely to be the intended type for the given annotation node.
*
* This is a guess, but a decent one.
*/
public static boolean annotationTypeMatches(Class extends java.lang.annotation.Annotation> type, EclipseNode node) {
if (node.getKind() != Kind.ANNOTATION) return false;
return typeMatches(type, node, ((Annotation) node.get()).type);
}
/**
* Checks if the provided annotation type is likely to be the intended type for the given annotation node.
*
* This is a guess, but a decent one.
*/
public static boolean annotationTypeMatches(String type, EclipseNode node) {
if (node.getKind() != Kind.ANNOTATION) return false;
return typeMatches(type, node, ((Annotation) node.get()).type);
}
public static TypeReference cloneSelfType(EclipseNode context) {
return cloneSelfType(context, null);
}
public static TypeReference cloneSelfType(EclipseNode context, ASTNode source) {
int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
long p = (long) pS << 32 | pE;
EclipseNode type = context;
TypeReference result = null;
while (type != null && type.getKind() != Kind.TYPE) type = type.up();
if (type != null && type.get() instanceof TypeDeclaration) {
TypeDeclaration typeDecl = (TypeDeclaration) type.get();
if (typeDecl.typeParameters != null && typeDecl.typeParameters.length > 0) {
TypeReference[] refs = new TypeReference[typeDecl.typeParameters.length];
int idx = 0;
for (TypeParameter param : typeDecl.typeParameters) {
TypeReference typeRef = new SingleTypeReference(param.name, (long)param.sourceStart << 32 | param.sourceEnd);
if (source != null) setGeneratedBy(typeRef, source);
refs[idx++] = typeRef;
}
result = generateParameterizedTypeReference(type, refs, p);
} else {
result = generateTypeReference(type, p);
}
}
if (result != null && source != null) setGeneratedBy(result, source);
return result;
}
public static TypeReference generateParameterizedTypeReference(EclipseNode type, TypeReference[] typeParams, long p) {
TypeDeclaration td = (TypeDeclaration) type.get();
char[][] tn = getQualifiedInnerName(type.up(), td.name);
if (tn.length == 1) return new ParameterizedSingleTypeReference(tn[0], typeParams, 0, p);
int tnLen = tn.length;
long[] ps = new long[tnLen];
for (int i = 0; i < tnLen; i++) ps[i] = p;
TypeReference[][] rr = new TypeReference[tnLen][];
rr[tnLen - 1] = typeParams;
boolean instance = (td.modifiers & MODIFIERS_INDICATING_STATIC) == 0;
if (instance) fillOuterTypeParams(rr, tnLen - 2, type.up(), p);
return new ParameterizedQualifiedTypeReference(tn, rr, 0, ps);
}
public static TypeReference generateParameterizedTypeReference(EclipseNode parent, char[] name, boolean instance, TypeReference[] typeParams, long p) {
char[][] tn = getQualifiedInnerName(parent, name);
if (tn.length == 1) return new ParameterizedSingleTypeReference(tn[0], typeParams, 0, p);
int tnLen = tn.length;
long[] ps = new long[tnLen];
for (int i = 0; i < tnLen; i++) ps[i] = p;
TypeReference[][] rr = new TypeReference[tnLen][];
rr[tnLen - 1] = typeParams;
if (instance) fillOuterTypeParams(rr, tnLen - 2, parent, p);
return new ParameterizedQualifiedTypeReference(tn, rr, 0, ps);
}
private static final int MODIFIERS_INDICATING_STATIC = ClassFileConstants.AccInterface | ClassFileConstants.AccStatic | ClassFileConstants.AccEnum;
/**
* This class will add type params to fully qualified chain of type references for inner types, such as {@code GrandParent.Parent.Child}; this is needed only as long as the chain does not involve static.
*
* @return {@code true} if at least one parameterization is actually added, {@code false} otherwise.
*/
private static boolean fillOuterTypeParams(TypeReference[][] rr, int idx, EclipseNode node, long p) {
if (idx < 0 || node == null || !(node.get() instanceof TypeDeclaration)) return false;
boolean filled = false;
TypeDeclaration td = (TypeDeclaration) node.get();
if (0 != (td.modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccEnum))) {
// any class defs inside an enum or interface are static, even if not marked as such.
return false;
}
TypeParameter[] tps = td.typeParameters;
if (tps != null && tps.length > 0) {
TypeReference[] trs = new TypeReference[tps.length];
for (int i = 0; i < tps.length; i++) {
trs[i] = new SingleTypeReference(tps[i].name, p);
}
rr[idx] = trs;
filled = true;
}
if ((td.modifiers & MODIFIERS_INDICATING_STATIC) != 0) return filled; // Once we hit a static class, no further typeparams needed.
boolean f2 = fillOuterTypeParams(rr, idx - 1, node.up(), p);
return f2 || filled;
}
public static NameReference generateNameReference(EclipseNode type, long p) {
char[][] tn = getQualifiedInnerName(type.up(), ((TypeDeclaration) type.get()).name);
if (tn.length == 1) return new SingleNameReference(tn[0], p);
int tnLen = tn.length;
long[] ps = new long[tnLen];
for (int i = 0; i < tnLen; i++) ps[i] = p;
int ss = (int) (p >> 32);
int se = (int) p;
return new QualifiedNameReference(tn, ps, ss, se);
}
public static NameReference generateNameReference(EclipseNode parent, char[] name, long p) {
char[][] tn = getQualifiedInnerName(parent, name);
if (tn.length == 1) return new SingleNameReference(tn[0], p);
int tnLen = tn.length;
long[] ps = new long[tnLen];
for (int i = 0; i < tnLen; i++) ps[i] = p;
int ss = (int) (p >> 32);
int se = (int) p;
return new QualifiedNameReference(tn, ps, ss, se);
}
public static TypeReference generateTypeReference(EclipseNode type, long p) {
TypeDeclaration td = (TypeDeclaration) type.get();
char[][] tn = getQualifiedInnerName(type.up(), td.name);
if (tn.length == 1) return new SingleTypeReference(tn[0], p);
int tnLen = tn.length;
long[] ps = new long[tnLen];
for (int i = 0; i < tnLen; i++) ps[i] = p;
boolean instance = (td.modifiers & MODIFIERS_INDICATING_STATIC) == 0 && type.up() != null && type.up().get() instanceof TypeDeclaration;
if (instance) {
TypeReference[][] trs = new TypeReference[tn.length][];
boolean filled = fillOuterTypeParams(trs, trs.length - 2, type.up(), p);
if (filled) return new ParameterizedQualifiedTypeReference(tn, trs, 0, ps);
}
return new QualifiedTypeReference(tn, ps);
}
public static TypeReference generateTypeReference(EclipseNode parent, char[] name, boolean instance, long p) {
char[][] tn = getQualifiedInnerName(parent, name);
if (tn.length == 1) return new SingleTypeReference(tn[0], p);
int tnLen = tn.length;
long[] ps = new long[tnLen];
for (int i = 0; i < tnLen; i++) ps[i] = p;
if (instance && parent != null && parent.get() instanceof TypeDeclaration) {
TypeReference[][] trs = new TypeReference[tn.length][];
if (fillOuterTypeParams(trs, tn.length - 2, parent, p)) return new ParameterizedQualifiedTypeReference(tn, trs, 0, ps);
}
return new QualifiedTypeReference(tn, ps);
}
/**
* Generate a chain of names for the enclosing classes.
*
* Given for example {@code class Outer { class Inner {} }} this would generate {@code char[][] { "Outer", "Inner" }}.
* For method local and top level types, this generates a size-1 char[][] where the only char[] element is {@code name} itself.
*/
public static char[][] getQualifiedInnerName(EclipseNode parent, char[] name) {
int count = 0;
EclipseNode n = parent;
while (n != null && n.getKind() == Kind.TYPE && n.get() instanceof TypeDeclaration) {
TypeDeclaration td = (TypeDeclaration) n.get();
if (td.name == null || td.name.length == 0) break;
count++;
n = n.up();
}
if (count == 0) return new char[][] { name };
char[][] res = new char[count + 1][];
res[count] = name;
n = parent;
while (count > 0) {
TypeDeclaration td = (TypeDeclaration) n.get();
res[--count] = td.name;
n = n.up();
}
return res;
}
private static final char[] OBJECT_SIG = "Ljava/lang/Object;".toCharArray();
private static int compare(char[] a, char[] b) {
if (a == null) return b == null ? 0 : -1;
if (b == null) return +1;
int len = Math.min(a.length, b.length);
for (int i = 0; i < len; i++) {
if (a[i] < b[i]) return -1;
if (a[i] > b[i]) return +1;
}
return a.length < b.length ? -1 : a.length > b.length ? +1 : 0;
}
public static TypeReference makeType(TypeBinding binding, ASTNode pos, boolean allowCompound) {
Object[] arr = null;
if (binding.getClass() == EclipseReflectiveMembers.INTERSECTION_BINDING1) {
arr = (Object[]) EclipseReflectiveMembers.reflect(EclipseReflectiveMembers.INTERSECTION_BINDING_TYPES1, binding);
} else if (binding.getClass() == EclipseReflectiveMembers.INTERSECTION_BINDING2) {
arr = (Object[]) EclipseReflectiveMembers.reflect(EclipseReflectiveMembers.INTERSECTION_BINDING_TYPES2, binding);
}
if (arr != null) {
// Is there a class? Alphabetically lowest wins.
TypeBinding winner = null;
int winLevel = 0; // 100 = array, 50 = class, 20 = typevar, 15 = wildcard, 10 = interface, 1 = Object.
for (Object b : arr) {
if (b instanceof TypeBinding) {
TypeBinding tb = (TypeBinding) b;
int level = 0;
if (tb.isArrayType()) level = 100;
else if (tb.isClass()) level = 50;
else if (tb.isTypeVariable()) level = 20;
else if (tb.isWildcard()) level = 15;
else level = 10;
if (level == 50 && compare(tb.signature(), OBJECT_SIG) == 0) level = 1;
if (winLevel > level) continue;
if (winLevel < level) {
winner = tb;
winLevel = level;
continue;
}
if (compare(winner.signature(), tb.signature()) > 0) winner = tb;
}
}
binding = winner;
}
int dims = binding.dimensions();
binding = binding.leafComponentType();
// Primitives
char[] base = null;
switch (binding.id) {
case TypeIds.T_int:
base = TypeConstants.INT;
break;
case TypeIds.T_long:
base = TypeConstants.LONG;
break;
case TypeIds.T_short:
base = TypeConstants.SHORT;
break;
case TypeIds.T_byte:
base = TypeConstants.BYTE;
break;
case TypeIds.T_double:
base = TypeConstants.DOUBLE;
break;
case TypeIds.T_float:
base = TypeConstants.FLOAT;
break;
case TypeIds.T_boolean:
base = TypeConstants.BOOLEAN;
break;
case TypeIds.T_char:
base = TypeConstants.CHAR;
break;
case TypeIds.T_void:
base = TypeConstants.VOID;
break;
case TypeIds.T_null:
return null;
}
if (base != null) {
if (dims > 0) {
TypeReference result = new ArrayTypeReference(base, dims, pos(pos));
setGeneratedBy(result, pos);
return result;
}
TypeReference result = new SingleTypeReference(base, pos(pos));
setGeneratedBy(result, pos);
return result;
}
if (binding.isAnonymousType()) {
ReferenceBinding ref = (ReferenceBinding)binding;
ReferenceBinding[] supers = ref.superInterfaces();
if (supers == null || supers.length == 0) supers = new ReferenceBinding[] {ref.superclass()};
if (supers[0] == null) {
TypeReference result = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
setGeneratedBy(result, pos);
return result;
}
return makeType(supers[0], pos, false);
}
if (binding instanceof CaptureBinding) {
return makeType(((CaptureBinding)binding).wildcard, pos, allowCompound);
}
if (binding.isUnboundWildcard()) {
if (!allowCompound) {
TypeReference result = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
setGeneratedBy(result, pos);
return result;
} else {
Wildcard out = new Wildcard(Wildcard.UNBOUND);
setGeneratedBy(out, pos);
out.sourceStart = pos.sourceStart;
out.sourceEnd = pos.sourceEnd;
return out;
}
}
if (binding.isWildcard()) {
WildcardBinding wildcard = (WildcardBinding) binding;
if (wildcard.boundKind == Wildcard.EXTENDS) {
if (!allowCompound) {
TypeBinding bound = wildcard.bound;
boolean isObject = bound.id == TypeIds.T_JavaLangObject;
TypeBinding[] otherBounds = wildcard.otherBounds;
if (isObject && otherBounds != null && otherBounds.length > 0) {
return makeType(otherBounds[0], pos, false);
} else return makeType(bound, pos, false);
} else {
Wildcard out = new Wildcard(Wildcard.EXTENDS);
setGeneratedBy(out, pos);
out.bound = makeType(wildcard.bound, pos, false);
out.sourceStart = pos.sourceStart;
out.sourceEnd = pos.sourceEnd;
return out;
}
} else if (allowCompound && wildcard.boundKind == Wildcard.SUPER) {
Wildcard out = new Wildcard(Wildcard.SUPER);
setGeneratedBy(out, pos);
out.bound = makeType(wildcard.bound, pos, false);
out.sourceStart = pos.sourceStart;
out.sourceEnd = pos.sourceEnd;
return out;
} else {
TypeReference result = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
setGeneratedBy(result, pos);
return result;
}
}
// Keep moving up via 'binding.enclosingType()' and gather generics from each binding. We stop after a local type, or a static type, or a top-level type.
// Finally, add however many nullTypeArgument[] arrays as that are missing, inverse the list, toArray it, and use that as PTR's typeArgument argument.
List params = new ArrayList();
/* Calculate generics */
if (!(binding instanceof RawTypeBinding)) {
TypeBinding b = binding;
while (true) {
boolean isFinalStop = b.isLocalType() || !b.isMemberType() || b.enclosingType() == null;
TypeReference[] tyParams = null;
if (b instanceof ParameterizedTypeBinding) {
ParameterizedTypeBinding paramized = (ParameterizedTypeBinding) b;
if (paramized.arguments != null) {
tyParams = new TypeReference[paramized.arguments.length];
for (int i = 0; i < tyParams.length; i++) {
tyParams[i] = makeType(paramized.arguments[i], pos, true);
}
}
}
params.add(tyParams);
if (isFinalStop) break;
b = b.enclosingType();
}
}
char[][] parts;
if (binding.isTypeVariable()) {
parts = new char[][] { binding.shortReadableName() };
} else if (binding.isLocalType()) {
parts = new char[][] { binding.sourceName() };
} else {
String[] pkg = new String(binding.qualifiedPackageName()).split("\\.");
String[] name = new String(binding.qualifiedSourceName()).split("\\.");
if (pkg.length == 1 && pkg[0].isEmpty()) pkg = new String[0];
parts = new char[pkg.length + name.length][];
int ptr;
for (ptr = 0; ptr < pkg.length; ptr++) parts[ptr] = pkg[ptr].toCharArray();
for (; ptr < pkg.length + name.length; ptr++) parts[ptr] = name[ptr - pkg.length].toCharArray();
}
while (params.size() < parts.length) params.add(null);
Collections.reverse(params);
boolean isParamized = false;
for (TypeReference[] tyParams : params) {
if (tyParams != null) {
isParamized = true;
break;
}
}
if (isParamized) {
if (parts.length > 1) {
TypeReference[][] typeArguments = params.toArray(new TypeReference[0][]);
TypeReference result = new ParameterizedQualifiedTypeReference(parts, typeArguments, dims, poss(pos, parts.length));
setGeneratedBy(result, pos);
return result;
}
TypeReference result = new ParameterizedSingleTypeReference(parts[0], params.get(0), dims, pos(pos));
setGeneratedBy(result, pos);
return result;
}
if (dims > 0) {
if (parts.length > 1) {
TypeReference result = new ArrayQualifiedTypeReference(parts, dims, poss(pos, parts.length));
setGeneratedBy(result, pos);
return result;
}
TypeReference result = new ArrayTypeReference(parts[0], dims, pos(pos));
setGeneratedBy(result, pos);
return result;
}
if (parts.length > 1) {
TypeReference result = new QualifiedTypeReference(parts, poss(pos, parts.length));
setGeneratedBy(result, pos);
return result;
}
TypeReference result = new SingleTypeReference(parts[0], pos(pos));
setGeneratedBy(result, pos);
return result;
}
/**
* Provides AnnotationValues with the data it needs to do its thing.
*/
public static AnnotationValues
createAnnotation(Class type, final EclipseNode annotationNode) {
final Annotation annotation = (Annotation) annotationNode.get();
Map values = new HashMap();
MemberValuePair[] memberValuePairs = annotation.memberValuePairs();
if (memberValuePairs != null) for (final MemberValuePair pair : memberValuePairs) {
List raws = new ArrayList();
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy