package lombok.javac.handlers;
import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.Javac.*;
import static lombok.javac.JavacAugments.JCTree_generatedNode;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.lang.model.element.Element;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Scope;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCNewArray;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCWildcard;
import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Options;
import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.Data;
import lombok.Getter;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.CleanupTask;
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.handlers.HandlerUtil;
import lombok.core.handlers.HandlerUtil.FieldAccess;
import lombok.delombok.LombokOptionsFactory;
import lombok.experimental.Accessors;
import lombok.experimental.Tolerate;
import lombok.javac.Javac;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.permit.Permit;
public class JavacHandlerUtil {
private JavacHandlerUtil() {
}
private static class MarkingScanner extends TreeScanner {
private final JCTree source;
private final Context context;
MarkingScanner(JCTree source, Context context) {
this.source = source;
this.context = context;
}
@Override public void scan(JCTree tree) {
if (tree == null) return;
setGeneratedBy(tree, source, context);
super.scan(tree);
}
}
public static boolean inNetbeansEditor(JavacNode node) {
return inNetbeansEditor(node.getContext());
}
public static boolean inNetbeansEditor(Context context) {
Options options = Options.instance(context);
return (options.keySet().contains("ide") && !options.keySet().contains("backgroundCompilation"));
}
public static boolean inNetbeansCompileOnSave(Context context) {
Options options = Options.instance(context);
return (options.keySet().contains("ide") && options.keySet().contains("backgroundCompilation"));
}
public static JCTree getGeneratedBy(JCTree node) {
return JCTree_generatedNode.get(node);
}
public static boolean isGenerated(JCTree node) {
return getGeneratedBy(node) != null;
}
public static <T extends JCTree> T recursiveSetGeneratedBy(T node, JCTree source, Context context) {
if (node == null) return null;
setGeneratedBy(node, source, context);
node.accept(new MarkingScanner(source, context));
return node;
}
public static <T extends JCTree> T setGeneratedBy(T node, JCTree source, Context context) {
if (node == null) return null;
if (source == null) JCTree_generatedNode.clear(node);
else JCTree_generatedNode.set(node, source);
if (source != null && (!inNetbeansEditor(context) || (node instanceof JCVariableDecl && (((JCVariableDecl) node).mods.flags & Flags.PARAMETER) != 0))) node.pos = source.pos;
return node;
}
public static boolean hasAnnotation(String type, JavacNode node) {
return hasAnnotation(type, node, false);
}
public static boolean hasAnnotation(Class<? extends Annotation> type, JavacNode node) {
return hasAnnotation(type, node, false);
}
public static boolean hasAnnotationAndDeleteIfNeccessary(Class<? extends Annotation> type, JavacNode node) {
return hasAnnotation(type, node, true);
}
private static boolean hasAnnotation(Class<? extends Annotation> type, JavacNode node, boolean delete) {
if (node == null) return false;
if (type == null) return false;
switch (node.getKind()) {
case ARGUMENT:
case FIELD:
case LOCAL:
case TYPE:
case METHOD:
for (JavacNode child : node.down()) {
if (annotationTypeMatches(type, child)) {
if (delete) deleteAnnotationIfNeccessary(child, type);
return true;
}
}
default:
return false;
}
}
private static boolean hasAnnotation(String type, JavacNode node, boolean delete) {
if (node == null) return false;
if (type == null) return false;
switch (node.getKind()) {
case ARGUMENT:
case FIELD:
case LOCAL:
case TYPE:
case METHOD:
for (JavacNode child : node.down()) {
if (annotationTypeMatches(type, child)) {
if (delete) deleteAnnotationIfNeccessary(child, type);
return true;
}
}
default:
return false;
}
}
public static JavacNode findInnerClass(JavacNode parent, String name) {
for (JavacNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
JCClassDecl td = (JCClassDecl) child.get();
if (td.name.contentEquals(name)) return child;
}
return null;
}
public static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node) {
return findAnnotation(type, node, false);
}
public static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node, boolean delete) {
if (node == null) return null;
if (type == null) return null;
switch (node.getKind()) {
case ARGUMENT:
case FIELD:
case LOCAL:
case TYPE:
case METHOD:
for (JavacNode child : node.down()) {
if (annotationTypeMatches(type, child)) {
if (delete) deleteAnnotationIfNeccessary(child, type);
return child;
}
}
default:
return null;
}
}
public static boolean annotationTypeMatches(Class<? extends Annotation> type, JavacNode node) {
if (node.getKind() != Kind.ANNOTATION) return false;
return typeMatches(type, node, ((JCAnnotation) node.get()).annotationType);
}
public static boolean annotationTypeMatches(String type, JavacNode node) {
if (node.getKind() != Kind.ANNOTATION) return false;
return typeMatches(type, node, ((JCAnnotation) node.get()).annotationType);
}
public static boolean typeMatches(Class<?> type, JavacNode node, JCTree typeNode) {
return typeMatches(type.getName(), node, typeNode);
}
public static boolean typeMatches(String type, JavacNode node, JCTree typeNode) {
String typeName = typeNode == null ? null : typeNode.toString();
if (typeName == null || typeName.length() == 0) return false;
int lastIndexA = typeName.lastIndexOf('.') + 1;
int lastIndexB = Math.max(type.lastIndexOf('.'), type.lastIndexOf('$')) + 1;
int len = typeName.length() - lastIndexA;
if (len != type.length() - lastIndexB) return false;
for (int i = 0; i < len; i++) if (typeName.charAt(i + lastIndexA) != type.charAt(i + lastIndexB)) return false;
TypeResolver resolver = node.getImportListAsTypeResolver();
return resolver.typeMatches(node, type, typeName);
}
public static boolean isFieldDeprecated(JavacNode field) {
if (!(field.get() instanceof JCVariableDecl)) return false;
JCVariableDecl fieldNode = (JCVariableDecl) field.get();
if ((fieldNode.mods.flags & Flags.DEPRECATED) != 0) {
return true;
}
for (JavacNode child : field.down()) {
if (annotationTypeMatches(Deprecated.class, child)) {
return true;
}
}
return false;
}
public static CheckerFrameworkVersion getCheckerFrameworkVersion(JavacNode node) {
CheckerFrameworkVersion cfv = node.getAst().readConfiguration(ConfigurationKeys.CHECKER_FRAMEWORK);
return cfv == null ? CheckerFrameworkVersion.NONE : cfv;
}
public static boolean nodeHasDeprecatedFlag(JCTree node) {
if (node instanceof JCVariableDecl) return (((JCVariableDecl) node).mods.flags & Flags.DEPRECATED) != 0;
if (node instanceof JCMethodDecl) return (((JCMethodDecl) node).mods.flags & Flags.DEPRECATED) != 0;
if (node instanceof JCClassDecl) return (((JCClassDecl) node).mods.flags & Flags.DEPRECATED) != 0;
return false;
}
public static <A extends Annotation> AnnotationValues<A> createAnnotation(Class<A> type, final JavacNode node) {
return createAnnotation(type, (JCAnnotation) node.get(), node);
}
public static <A extends Annotation> AnnotationValues<A> createAnnotation(Class<A> type, JCAnnotation anno, final JavacNode node) {
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
List<JCExpression> arguments = anno.getArguments();
for (JCExpression arg : arguments) {
String mName;
JCExpression rhs;
java.util.List<String> raws = new ArrayList<String>();
java.util.List<Object> guesses = new ArrayList<Object>();
java.util.List<Object> expressions = new ArrayList<Object>();
final java.util.List<DiagnosticPosition> positions = new ArrayList<DiagnosticPosition>();
if (arg instanceof JCAssign) {
JCAssign assign = (JCAssign) arg;
mName = assign.lhs.toString();
rhs = assign.rhs;
} else {
rhs = arg;
mName = "value";
}
if (rhs instanceof JCNewArray) {
List<JCExpression> elems = ((JCNewArray) rhs).elems;
for (JCExpression inner : elems) {
raws.add(inner.toString());
expressions.add(inner);
if (inner instanceof JCAnnotation) {
try {
@SuppressWarnings("unchecked")
Class<A> innerClass = (Class<A>) Class.forName(inner.type.toString());
guesses.add(createAnnotation(innerClass, (JCAnnotation) inner, node));
} catch (ClassNotFoundException ex) {
guesses.add(calculateGuess(inner));
}
} else {
guesses.add(calculateGuess(inner));
}
positions.add(inner.pos());
}
} else {
raws.add(rhs.toString());
expressions.add(rhs);
if (rhs instanceof JCAnnotation) {
try {
@SuppressWarnings("unchecked")
Class<A> innerClass = (Class<A>) Class.forName(rhs.type.toString());
guesses.add(createAnnotation(innerClass, (JCAnnotation) rhs, node));
} catch (ClassNotFoundException ex) {
guesses.add(calculateGuess(rhs));
}
} else {
guesses.add(calculateGuess(rhs));
}
positions.add(rhs.pos());
}
values.put(mName, new AnnotationValue(node, raws, expressions, guesses, true) {
@Override public void setError(String message, int valueIdx) {
if (valueIdx < 0) node.addError(message);
else node.addError(message, positions.get(valueIdx));
}
@Override public void setWarning(String message, int valueIdx) {
if (valueIdx < 0) node.addWarning(message);
else node.addWarning(message, positions.get(valueIdx));
}
});
}
for (Method m : type.getDeclaredMethods()) {
if (!Modifier.isPublic(m.getModifiers())) continue;
String name = m.getName();
if (!values.containsKey(name)) {
values.put(name, new AnnotationValue(node, new ArrayList<String>(), new ArrayList<Object>(), new ArrayList<Object>(), false) {
@Override public void setError(String message, int valueIdx) {
node.addError(message);
}
@Override public void setWarning(String message, int valueIdx) {
node.addWarning(message);
}
});
}
}
return new AnnotationValues<A>(type, values, node);
}
public static void deleteAnnotationIfNeccessary(JavacNode annotation, String annotationType) {
deleteAnnotationIfNeccessary0(annotation, annotationType);
}
public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) {
deleteAnnotationIfNeccessary0(annotation, annotationType.getName());
}
public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, Class<? extends Annotation> annotationType2) {
deleteAnnotationIfNeccessary0(annotation, annotationType1.getName(), annotationType2.getName());
}
public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, String annotationType2) {
deleteAnnotationIfNeccessary0(annotation, annotationType1.getName(), annotationType2);
}
private static void deleteAnnotationIfNeccessary0(JavacNode annotation, String... annotationTypes) {
if (inNetbeansEditor(annotation)) return;
if (!annotation.shouldDeleteLombokAnnotations()) return;
JavacNode parentNode = annotation.directUp();
switch (parentNode.getKind()) {
case FIELD:
case ARGUMENT:
case LOCAL:
JCVariableDecl variable = (JCVariableDecl) parentNode.get();
variable.mods.annotations = filterList(variable.mods.annotations, annotation.get());
break;
case METHOD:
JCMethodDecl method = (JCMethodDecl) parentNode.get();
method.mods.annotations = filterList(method.mods.annotations, annotation.get());
break;
case TYPE:
try {
JCClassDecl type = (JCClassDecl) parentNode.get();
type.mods.annotations = filterList(type.mods.annotations, annotation.get());
} catch (ClassCastException e) {
}
break;
default:
return;
}
parentNode.getAst().setChanged();
for (String annotationType : annotationTypes) {
deleteImportFromCompilationUnit(annotation, annotationType);
}
}
public static void deleteImportFromCompilationUnit(JavacNode node, String name) {
if (inNetbeansEditor(node)) return;
if (!node.shouldDeleteLombokAnnotations()) return;
ListBuffer<JCTree> newDefs = new ListBuffer<JCTree>();
JCCompilationUnit unit = (JCCompilationUnit) node.top().get();
for (JCTree def : unit.defs) {
boolean delete = false;
if (def instanceof JCImport) {
JCImport imp0rt = (JCImport)def;
delete = (!imp0rt.staticImport && imp0rt.qualid.toString().equals(name));
}
if (!delete) newDefs.append(def);
}
unit.defs = newDefs.toList();
}
private static List<JCAnnotation> filterList(List<JCAnnotation> annotations, JCTree jcTree) {
ListBuffer<JCAnnotation> newAnnotations = new ListBuffer<JCAnnotation>();
for (JCAnnotation ann : annotations) {
if (jcTree != ann) newAnnotations.append(ann);
}
return newAnnotations.toList();
}
public enum MemberExistsResult {
NOT_EXISTS, EXISTS_BY_LOMBOK, EXISTS_BY_USER;
}
public static java.util.List<String> toAllGetterNames(JavacNode field) {
return HandlerUtil.toAllGetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field));
}
public static String toGetterName(JavacNode field) {
return HandlerUtil.toGetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field));
}
public static java.util.List<String> toAllSetterNames(JavacNode field) {
return HandlerUtil.toAllSetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field));
}
public static String toSetterName(JavacNode field) {
return HandlerUtil.toSetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field));
}
public static java.util.List<String> toAllWithNames(JavacNode field) {
return HandlerUtil.toAllWithNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field));
}
public static java.util.List<String> toAllWithByNames(JavacNode field) {
return HandlerUtil.toAllWithByNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field));
}
public static String toWithName(JavacNode field) {
return HandlerUtil.toWithName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field));
}
public static String toWithByName(JavacNode field) {
return HandlerUtil.toWithByName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field));
}
public static boolean shouldReturnThis(JavacNode field) {
if ((((JCVariableDecl) field.get()).mods.flags & Flags.STATIC) != 0) return false;
AnnotationValues<Accessors> accessors = JavacHandlerUtil.getAccessorsForField(field);
return HandlerUtil.shouldReturnThis0(accessors, field.getAst());
}
public static JCExpression cloneSelfType(JavacNode childOfType) {
JavacNode typeNode = childOfType;
JavacTreeMaker maker = childOfType.getTreeMaker();
while (typeNode != null && typeNode.getKind() != Kind.TYPE) typeNode = typeNode.up();
return JavacHandlerUtil.namePlusTypeParamsToTypeReference(maker, typeNode, ((JCClassDecl) typeNode.get()).typarams);
}
public static boolean isBoolean(JavacNode field) {
JCExpression varType = ((JCVariableDecl) field.get()).vartype;
return isBoolean(varType);
}
public static boolean isBoolean(JCExpression varType) {
return varType != null && varType.toString().equals("boolean");
}
public static Name removePrefixFromField(JavacNode field) {
java.util.List<String> prefixes = null;
for (JavacNode node : field.down()) {
if (annotationTypeMatches(Accessors.class, node)) {
AnnotationValues<Accessors> ann = createAnnotation(Accessors.class, node);
if (ann.isExplicit("prefix")) prefixes = Arrays.asList(ann.getInstance().prefix());
break;
}
}
if (prefixes == null) {
JavacNode current = field.up();
outer:
while (current != null) {
for (JavacNode node : current.down()) {
if (annotationTypeMatches(Accessors.class, node)) {
AnnotationValues<Accessors> ann = createAnnotation(Accessors.class, node);
if (ann.isExplicit("prefix")) prefixes = Arrays.asList(ann.getInstance().prefix());
break outer;
}
}
current = current.up();
}
}
if (prefixes == null) prefixes = field.getAst().readConfiguration(ConfigurationKeys.ACCESSORS_PREFIX);
if (!prefixes.isEmpty()) {
CharSequence newName = removePrefix(field.getName(), prefixes);
if (newName != null) return field.toName(newName.toString());
}
return ((JCVariableDecl) field.get()).name;
}
public static AnnotationValues<Accessors> getAccessorsForField(JavacNode field) {
for (JavacNode node : field.down()) {
if (annotationTypeMatches(Accessors.class, node)) {
return createAnnotation(Accessors.class, node);
}
}
JavacNode current = field.up();
while (current != null) {
for (JavacNode node : current.down()) {
if (annotationTypeMatches(Accessors.class, node)) {
return createAnnotation(Accessors.class, node);
}
}
current = current.up();
}
return AnnotationValues.of(Accessors.class, field);
}
public static MemberExistsResult fieldExists(String fieldName, JavacNode node) {
node = upToTypeNode(node);
if (node != null && node.get() instanceof JCClassDecl) {
for (JCTree def : ((JCClassDecl)node.get()).defs) {
if (def instanceof JCVariableDecl) {
if (((JCVariableDecl)def).name.contentEquals(fieldName)) {
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
}
}
}
return MemberExistsResult.NOT_EXISTS;
}
public static MemberExistsResult methodExists(String methodName, JavacNode node, int params) {
return methodExists(methodName, node, true, params);
}
public static MemberExistsResult methodExists(String methodName, JavacNode node, boolean caseSensitive, int params) {
node = upToTypeNode(node);
if (node != null && node.get() instanceof JCClassDecl) {
top: for (JCTree def : ((JCClassDecl)node.get()).defs) {
if (def instanceof JCMethodDecl) {
JCMethodDecl md = (JCMethodDecl) def;
String name = md.name.toString();
boolean matches = caseSensitive ? name.equals(methodName) : name.equalsIgnoreCase(methodName);
if (matches) {
if (params > -1) {
List<JCVariableDecl> ps = md.params;
int minArgs = 0;
int maxArgs = 0;
if (ps != null && ps.length() > 0) {
minArgs = ps.length();
if ((ps.last().mods.flags & Flags.VARARGS) != 0) {
maxArgs = Integer.MAX_VALUE;
minArgs--;
} else {
maxArgs = minArgs;
}
}
if (params < minArgs || params > maxArgs) continue;
}
if (isTolerate(node, md)) continue top;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
}
}
}
return MemberExistsResult.NOT_EXISTS;
}
public static boolean isTolerate(JavacNode node, JCTree.JCMethodDecl md) {
List<JCAnnotation> annotations = md.getModifiers().getAnnotations();
if (annotations != null) for (JCTree.JCAnnotation anno : annotations) {
if (typeMatches(Tolerate.class, node, anno.getAnnotationType())) return true;
}
return false;
}
public static MemberExistsResult constructorExists(JavacNode node) {
node = upToTypeNode(node);
if (node != null && node.get() instanceof JCClassDecl) {
for (JCTree def : ((JCClassDecl)node.get()).defs) {
if (def instanceof JCMethodDecl) {
JCMethodDecl md = (JCMethodDecl) def;
if (md.name.contentEquals("<init>")) {
if ((md.mods.flags & Flags.GENERATEDCONSTR) != 0) continue;
if (isTolerate(node, md)) continue;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
}
}
}
return MemberExistsResult.NOT_EXISTS;
}
public static boolean isConstructorCall(final JCStatement statement) {
if (!(statement instanceof JCExpressionStatement)) return false;
JCExpression expr = ((JCExpressionStatement) statement).expr;
if (!(expr instanceof JCMethodInvocation)) return false;
JCExpression invocation = ((JCMethodInvocation) expr).meth;
String name;
if (invocation instanceof JCFieldAccess) {
name = ((JCFieldAccess) invocation).name.toString();
} else if (invocation instanceof JCIdent) {
name = ((JCIdent) invocation).name.toString();
} else {
name = "";
}
return "super".equals(name) || "this".equals(name);
}
public static int toJavacModifier(AccessLevel accessLevel) {
switch (accessLevel) {
case MODULE:
case PACKAGE:
return 0;
default:
case PUBLIC:
return Flags.PUBLIC;
case NONE:
case PRIVATE:
return Flags.PRIVATE;
case PROTECTED:
return Flags.PROTECTED;
}
}
private static class GetterMethod {
private final Name name;
private final JCExpression type;
GetterMethod(Name name, JCExpression type) {
this.name = name;
this.type = type;
}
}
private static GetterMethod findGetter(JavacNode field) {
JCVariableDecl decl = (JCVariableDecl)field.get();
JavacNode typeNode = field.up();
for (String potentialGetterName : toAllGetterNames(field)) {
for (JavacNode potentialGetter : typeNode.down()) {
if (potentialGetter.getKind() != Kind.METHOD) continue;
JCMethodDecl method = (JCMethodDecl) potentialGetter.get();
if (!method.name.toString().equalsIgnoreCase(potentialGetterName)) continue;
if ((method.mods.flags & Flags.STATIC) != 0) continue;
if (method.params != null && method.params.size() > 0) continue;
return new GetterMethod(method.name, method.restype);
}
}
boolean hasGetterAnnotation = false;
for (JavacNode child : field.down()) {
if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Getter.class, child)) {
AnnotationValues<Getter> ann = createAnnotation(Getter.class, child);
if (ann.getInstance().value() == AccessLevel.NONE) return null;
hasGetterAnnotation = true;
}
}
if (!hasGetterAnnotation && HandleGetter.fieldQualifiesForGetterGeneration(field)) {
JavacNode containingType = field.up();
if (containingType != null) for (JavacNode child : containingType.down()) {
if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Data.class, child)) hasGetterAnnotation = true;
if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Getter.class, child)) {
AnnotationValues<Getter> ann = createAnnotation(Getter.class, child);
if (ann.getInstance().value() == AccessLevel.NONE) return null;
hasGetterAnnotation = true;
}
}
}
if (hasGetterAnnotation) {
String getterName = toGetterName(field);
if (getterName == null) return null;
return new GetterMethod(field.toName(getterName), decl.vartype);
}
return null;
}
static boolean lookForGetter(JavacNode field, FieldAccess fieldAccess) {
if (fieldAccess == FieldAccess.GETTER) return true;
if (fieldAccess == FieldAccess.ALWAYS_FIELD) return false;
for (JavacNode child : field.down()) {
if (child.getKind() != Kind.ANNOTATION) continue;
if (annotationTypeMatches(Getter.class, child)) {
AnnotationValues<Getter> ann = createAnnotation(Getter.class, child);
if (ann.getInstance().lazy()) return true;
}
}
return false;
}
static JCExpression getFieldType(JavacNode field, FieldAccess fieldAccess) {
if (field.getKind() == Kind.METHOD) return ((JCMethodDecl) field.get()).restype;
boolean lookForGetter = lookForGetter(field, fieldAccess);
GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
return ((JCVariableDecl) field.get()).vartype;
}
return getter.type;
}
static JCExpression createFieldAccessor(JavacTreeMaker maker, JavacNode field, FieldAccess fieldAccess) {
return createFieldAccessor(maker, field, fieldAccess, null);
}
static JCExpression createFieldAccessor(JavacTreeMaker maker, JavacNode field, FieldAccess fieldAccess, JCExpression receiver) {
boolean lookForGetter = lookForGetter(field, fieldAccess);
GetterMethod getter = lookForGetter ? findGetter(field) : null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
if (getter == null) {
if (receiver == null) {
if ((fieldDecl.mods.flags & Flags.STATIC) == 0) {
receiver = maker.Ident(field.toName("this"));
} else {
JavacNode containerNode = field.up();
if (containerNode != null && containerNode.get() instanceof JCClassDecl) {
JCClassDecl container = (JCClassDecl) field.up().get();
receiver = maker.Ident(container.name);
}
}
}
return receiver == null ? maker.Ident(fieldDecl.name) : maker.Select(receiver, fieldDecl.name);
}
if (receiver == null) receiver = maker.Ident(field.toName("this"));
JCMethodInvocation call = maker.Apply(List.<JCExpression>nil(),
maker.Select(receiver, getter.name), List.<JCExpression>nil());
return call;
}
static JCExpression createMethodAccessor(JavacTreeMaker maker, JavacNode method) {
return createMethodAccessor(maker, method, null);
}
static JCExpression createMethodAccessor(JavacTreeMaker maker, JavacNode method, JCExpression receiver) {
JCMethodDecl methodDecl = (JCMethodDecl) method.get();
if (receiver == null && (methodDecl.mods.flags & Flags.STATIC) == 0) {
receiver = maker.Ident(method.toName("this"));
} else if (receiver == null) {
JavacNode containerNode = method.up();
if (containerNode != null && containerNode.get() instanceof JCClassDecl) {
JCClassDecl container = (JCClassDecl) method.up().get();
receiver = maker.Ident(container.name);
}
}
JCMethodInvocation call = maker.Apply(List.<JCExpression>nil(),
receiver == null ? maker.Ident(methodDecl.name) : maker.Select(receiver, methodDecl.name), List.<JCExpression>nil());
return call;
}
public static Type getMirrorForFieldType(JavacNode fieldNode) {
Element fieldElement = fieldNode.getElement();
if (fieldElement instanceof VarSymbol) return ((VarSymbol) fieldElement).type;
return null;
}
public static JavacNode injectFieldAndMarkGenerated(JavacNode typeNode, JCVariableDecl field) {
return injectField(typeNode, field, true);
}
public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field) {
return injectField(typeNode, field, false);
}
public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addGenerated) {
return injectField(typeNode, field, addGenerated, false);
}
public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addGenerated, boolean specialEnumHandling) {
JCClassDecl type = (JCClassDecl) typeNode.get();
if (addGenerated) {
addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext());
addGenerated(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext());
}
List<JCTree> insertAfter = null;
List<JCTree> insertBefore = type.defs;
while (true) {
boolean skip = false;
if (insertBefore.head instanceof JCVariableDecl) {
JCVariableDecl f = (JCVariableDecl) insertBefore.head;
if ((!specialEnumHandling && isEnumConstant(f)) || isGenerated(f)) skip = true;
} else if (insertBefore.head instanceof JCMethodDecl) {
if ((((JCMethodDecl) insertBefore.head).mods.flags & GENERATEDCONSTR) != 0) skip = true;
}
if (skip) {
insertAfter = insertBefore;
insertBefore = insertBefore.tail;
continue;
}
break;
}
List<JCTree> fieldEntry = List.<JCTree>of(field);
fieldEntry.tail = insertBefore;
if (insertAfter == null) {
type.defs = fieldEntry;
} else {
insertAfter.tail = fieldEntry;
}
return typeNode.add(field, Kind.FIELD);
}
public static boolean isEnumConstant(final JCVariableDecl field) {
return (field.mods.flags & Flags.ENUM) != 0;
}
static class JCAnnotatedTypeReflect {
private static Class<?> TYPE;
private static Constructor<?> CONSTRUCTOR;
private static Field ANNOTATIONS, UNDERLYING_TYPE;
private static void initByLoader(ClassLoader classLoader) {
if (TYPE != null) return;
Class<?> c;
try {
c = classLoader.loadClass("com.sun.tools.javac.tree.JCTree$JCAnnotatedType");
} catch (Exception e) {
return;
}
init(c);
}
private static void init(Class<?> in) {
if (TYPE != null) return;
if (!in.getName().equals("com.sun.tools.javac.tree.JCTree$JCAnnotatedType")) return;
try {
CONSTRUCTOR = Permit.getConstructor(in, List.class, JCExpression.class);
ANNOTATIONS = Permit.getField(in, "annotations");
UNDERLYING_TYPE = Permit.getField(in, "underlyingType");
TYPE = in;
} catch (Exception ignore) {}
}
static boolean is(JCTree obj) {
if (obj == null) return false;
init(obj.getClass());
return obj.getClass() == TYPE;
}
@SuppressWarnings("unchecked")
static List<JCAnnotation> getAnnotations(JCTree obj) {
init(obj.getClass());
try {
return (List<JCAnnotation>) ANNOTATIONS.get(obj);
} catch (Exception e) {
return List.nil();
}
}
static void setAnnotations(JCTree obj, List<JCAnnotation> anns) {
init(obj.getClass());
try {
ANNOTATIONS.set(obj, anns);
} catch (Exception e) {}
}
static JCExpression getUnderlyingType(JCTree obj) {
init(obj.getClass());
try {
return (JCExpression) UNDERLYING_TYPE.get(obj);
} catch (Exception e) {
return null;
}
}
static JCExpression create(List<JCAnnotation> annotations, JCExpression underlyingType) {
initByLoader(underlyingType.getClass().getClassLoader());
try {
return (JCExpression) CONSTRUCTOR.newInstance(annotations, underlyingType);
} catch (Exception e) {
return underlyingType;
}
}
}
static class JCAnnotationReflect {
private static Field ATTRIBUTE;
static {
try {
ATTRIBUTE = Permit.getField(JCAnnotation.class, "attribute");
} catch (Exception ignore) {}
}
static Attribute.Compound getAttribute(JCAnnotation jcAnnotation) {
try {
return (Attribute.Compound) ATTRIBUTE.get(jcAnnotation);
} catch (Exception e) {
return null;
}
}
}
static class ClassSymbolMembersField {
private static final Field membersField;
private static final Method removeMethod;
private static final Method enterMethod;
static {
Field f = null;
Method r = null;
Method e = null;
try {
f = Permit.getField(ClassSymbol.class, "members_field");
r = Permit.getMethod(f.getType(), "remove", Symbol.class);
e = Permit.getMethod(f.getType(), "enter", Symbol.class);
} catch (Exception ex) {}
membersField = f;
removeMethod = r;
enterMethod = e;
}
static void remove(ClassSymbol from, Symbol toRemove) {
if (from == null) return;
try {
Scope scope = (Scope) membersField.get(from);
if (scope == null) return;
removeMethod.invoke(scope, toRemove);
} catch (Exception e) {}
}
static void enter(ClassSymbol from, Symbol toEnter) {
if (from == null) return;
try {
Scope scope = (Scope) membersField.get(from);
if (scope == null) return;
enterMethod.invoke(scope, toEnter);
} catch (Exception e) {}
}
}
public static void injectMethod(JavacNode typeNode, JCMethodDecl method) {
injectMethod(typeNode, method, null, null);
}
public static void injectMethod(JavacNode typeNode, JCMethodDecl method, List<Type> paramTypes, Type returnType) {
Context context = typeNode.getContext();
Symtab symtab = Symtab.instance(context);
JCClassDecl type = (JCClassDecl) typeNode.get();
if (method.getName().contentEquals("<init>")) {
int idx = 0;
for (JCTree def : type.defs) {
if (def instanceof JCMethodDecl) {
if ((((JCMethodDecl) def).mods.flags & Flags.GENERATEDCONSTR) != 0) {
JavacNode tossMe = typeNode.getNodeFor(def);
if (tossMe != null) tossMe.up().removeChild(tossMe);
type.defs = addAllButOne(type.defs, idx);
ClassSymbolMembersField.remove(type.sym, ((JCMethodDecl) def).sym);
break;
}
}
idx++;
}
}
addSuppressWarningsAll(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext());
addGenerated(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext());
type.defs = type.defs.append(method);
List<Symbol.VarSymbol> params = null;
if (method.getParameters() != null && !method.getParameters().isEmpty()) {
ListBuffer<Symbol.VarSymbol> newParams = new ListBuffer<Symbol.VarSymbol>();
for (int i = 0; i < method.getParameters().size(); i++) {
JCTree.JCVariableDecl param = method.getParameters().get(i);
if (param.sym == null) {
Type paramType = paramTypes == null ? param.getType().type : paramTypes.get(i);
VarSymbol varSymbol = new VarSymbol(param.mods.flags, param.name, paramType, symtab.noSymbol);
List<JCAnnotation> annotations = param.getModifiers().getAnnotations();
if (annotations != null && !annotations.isEmpty()) {
ListBuffer<Attribute.Compound> newAnnotations = new ListBuffer<Attribute.Compound>();
for (JCAnnotation jcAnnotation : annotations) {
Attribute.Compound attribute = JCAnnotationReflect.getAttribute(jcAnnotation);
if (attribute != null) {
newAnnotations.append(attribute);
}
}
if (annotations.length() == newAnnotations.length()) {
varSymbol.appendAttributes(newAnnotations.toList());
}
}
newParams.append(varSymbol);
} else {
newParams.append(param.sym);
}
}
params = newParams.toList();
if (params.length() != method.getParameters().length()) params = null;
}
fixMethodMirror(typeNode.getContext(), typeNode.getElement(), method.getModifiers().flags, method.getName(), paramTypes, params, returnType);
typeNode.add(method, Kind.METHOD);
}
private static void fixMethodMirror(Context context, Element typeMirror, long access, Name methodName, List<Type> paramTypes, List<Symbol.VarSymbol> params, Type returnType) {
if (typeMirror == null || paramTypes == null || returnType == null) return;
ClassSymbol cs = (ClassSymbol) typeMirror;
MethodSymbol methodSymbol = new MethodSymbol(access, methodName, new MethodType(paramTypes, returnType, List.<Type>nil(), Symtab.instance(context).methodClass), cs);
if (params != null && !params.isEmpty()) {
methodSymbol.params = params;
}
ClassSymbolMembersField.enter(cs, methodSymbol);
}
public static JavacNode injectType(JavacNode typeNode, final JCClassDecl type) {
JCClassDecl typeDecl = (JCClassDecl) typeNode.get();
addSuppressWarningsAll(type.mods, typeNode, type.pos, getGeneratedBy(type), typeNode.getContext());
addGenerated(type.mods, typeNode, type.pos, getGeneratedBy(type), typeNode.getContext());
typeDecl.defs = typeDecl.defs.append(type);
return typeNode.add(type, Kind.TYPE);
}
public static long addFinalIfNeeded(long flags, Context context) {
boolean addFinal = LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateFinalParams();
if (addFinal) flags |= Flags.FINAL;
return flags;
}
public static JCExpression genTypeRef(JavacNode node, String complexName) {
String[] parts = complexName.split("\\.");
if (parts.length > 2 && parts[0].equals("java") && parts[1].equals("lang")) {
String[] subParts = new String[parts.length - 2];
System.arraycopy(parts, 2, subParts, 0, subParts.length);
return genJavaLangTypeRef(node, subParts);
}
return chainDots(node, parts);
}
public static JCExpression genJavaLangTypeRef(JavacNode node, String... simpleNames) {
if (LombokOptionsFactory.getDelombokOptions(node.getContext()).getFormatPreferences().javaLangAsFqn()) {
return chainDots(node, "java", "lang", simpleNames);
} else {
return chainDots(node, null, null, simpleNames);
}
}
public static JCExpression genJavaLangTypeRef(JavacNode node, int pos, String... simpleNames) {
if (LombokOptionsFactory.getDelombokOptions(node.getContext()).getFormatPreferences().javaLangAsFqn()) {
return chainDots(node, pos, "java", "lang", simpleNames);
} else {
return chainDots(node, pos, null, null, simpleNames);
}
}
public static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) {
if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateSuppressWarnings()) return;
boolean addJLSuppress = !Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_SUPPRESSWARNINGS_ANNOTATIONS));
if (addJLSuppress) {
for (JCAnnotation ann : mods.annotations) {
JCTree type = ann.getAnnotationType();
Name n = null;
if (type instanceof JCIdent) n = ((JCIdent) type).name;
else if (type instanceof JCFieldAccess) n = ((JCFieldAccess) type).name;
if (n != null && n.contentEquals("SuppressWarnings")) {
addJLSuppress = false;
}
}
}
if (addJLSuppress) addAnnotation(mods, node, pos, source, context, "java.lang.SuppressWarnings", node.getTreeMaker().Literal("all"));
if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_FINDBUGS_SUPPRESSWARNINGS_ANNOTATIONS))) {
JavacTreeMaker maker = node.getTreeMaker();
JCExpression arg = maker.Assign(maker.Ident(node.toName("justification")), maker.Literal("generated code"));
addAnnotation(mods, node, pos, source, context, "edu.umd.cs.findbugs.annotations.SuppressFBWarnings", arg);
}
}
public static void addGenerated(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) {
if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateGenerated()) return;
if (HandlerUtil.shouldAddGenerated(node)) {
addAnnotation(mods, node, pos, source, context, "javax.annotation.Generated", node.getTreeMaker().Literal("lombok"));
}
if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_LOMBOK_GENERATED_ANNOTATIONS))) {
addAnnotation(mods, node, pos, source, context, "lombok.Generated", null);
}
}
public static void addAnnotation(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context, String annotationTypeFqn, JCExpression arg) {
boolean isJavaLangBased;
String simpleName; {
int idx = annotationTypeFqn.lastIndexOf('.');
simpleName = idx == -1 ? annotationTypeFqn : annotationTypeFqn.substring(idx + 1);
isJavaLangBased = idx == 9 && annotationTypeFqn.regionMatches(0, "java.lang.", 0, 10);
}
for (JCAnnotation ann : mods.annotations) {
JCTree annType = ann.getAnnotationType();
if (annType instanceof JCIdent) {
Name lastPart = ((JCIdent) annType).name;
if (lastPart.contentEquals(simpleName)) return;
}
if (annType instanceof JCFieldAccess) {
if (annType.toString().equals(annotationTypeFqn)) return;
}
}
JavacTreeMaker maker = node.getTreeMaker();
JCExpression annType = isJavaLangBased ? genJavaLangTypeRef(node, simpleName) : chainDotsString(node, annotationTypeFqn);
annType.pos = pos;
if (arg != null) {
arg.pos = pos;
if (arg instanceof JCAssign) {
((JCAssign) arg).lhs.pos = pos;
((JCAssign) arg).rhs.pos = pos;
}
}
List<JCExpression> argList = arg != null ? List.of(arg) : List.<JCExpression>nil();
JCAnnotation annotation = recursiveSetGeneratedBy(maker.Annotation(annType, argList), source, context);
annotation.pos = pos;
mods.annotations = mods.annotations.append(annotation);
}
private static List<JCTree> addAllButOne(List<JCTree> defs, int idx) {
ListBuffer<JCTree> out = new ListBuffer<JCTree>();
int i = 0;
for (JCTree def : defs) {
if (i++ != idx) out.append(def);
}
return out.toList();
}
public static JCExpression chainDots(JavacNode node, String elem1, String elem2, String... elems) {
return chainDots(node, -1, elem1, elem2, elems);
}
public static JCExpression chainDots(JavacNode node, String[] elems) {
return chainDots(node, -1, null, null, elems);
}
public static JCExpression chainDots(JavacNode node, LombokImmutableList<String> elems) {
assert elems != null;
JavacTreeMaker maker = node.getTreeMaker();
JCExpression e = null;
for (String elem : elems) {
if (e == null) e = maker.Ident(node.toName(elem));
else e = maker.Select(e, node.toName(elem));
}
return e;
}
public static JCExpression chainDots(JavacNode node, int pos, String elem1, String elem2, String... elems) {
assert elems != null;
JavacTreeMaker maker = node.getTreeMaker();
if (pos != -1) maker = maker.at(pos);
JCExpression e = null;
if (elem1 != null) e = maker.Ident(node.toName(elem1));
if (elem2 != null) e = e == null ? maker.Ident(node.toName(elem2)) : maker.Select(e, node.toName(elem2));
for (int i = 0 ; i < elems.length ; i++) {
e = e == null ? maker.Ident(node.toName(elems[i])) : maker.Select(e, node.toName(elems[i]));
}
assert e != null;
return e;
}
public static JCExpression chainDotsString(JavacNode node, String elems) {
return chainDots(node, null, null, elems.split("\\."));
}
public static List<JCAnnotation> findAnnotations(JavacNode fieldNode, Pattern namePattern) {
ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>();
for (JavacNode child : fieldNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
JCAnnotation annotation = (JCAnnotation) child.get();
String name = annotation.annotationType.toString();
int idx = name.lastIndexOf(".");
String suspect = idx == -1 ? name : name.substring(idx + 1);
if (namePattern.matcher(suspect).matches()) {
result.append(annotation);
}
}
}
return result.toList();
}
public static String scanForNearestAnnotation(JavacNode node, String... anns) {
while (node != null) {
for (JavacNode ann : node.down()) {
if (ann.getKind() != Kind.ANNOTATION) continue;
JCAnnotation a = (JCAnnotation) ann.get();
for (String annToFind : anns) if (typeMatches(annToFind, node, a.annotationType)) return annToFind;
}
node = node.up();
}
return null;
}
public static boolean hasNonNullAnnotations(JavacNode node) {
for (JavacNode child : node.down()) {
if (child.getKind() == Kind.ANNOTATION) {
JCAnnotation annotation = (JCAnnotation) child.get();
for (String nn : NONNULL_ANNOTATIONS) if (typeMatches(nn, node, annotation.annotationType)) return true;
}
}
return false;
}
public static boolean hasNonNullAnnotations(JavacNode node, List<JCAnnotation> anns) {
if (anns == null) return false;
for (JCAnnotation ann : anns) {
for (String nn : NONNULL_ANNOTATIONS) if (typeMatches(nn, node, ann)) return true;
}
return false;
}
public static List<JCAnnotation> findCopyableAnnotations(JavacNode node) {
JCAnnotation anno = null;
String annoName = null;
for (JavacNode child : node.down()) {
if (child.getKind() == Kind.ANNOTATION) {
if (anno != null) {
annoName = "";
break;
}
JCAnnotation annotation = (JCAnnotation) child.get();
annoName = annotation.annotationType.toString();
anno = annotation;
}
}
if (annoName == null) return List.nil();
java.util.List<TypeName> configuredCopyable = node.getAst().readConfiguration(ConfigurationKeys.COPYABLE_ANNOTATIONS);
if (!annoName.isEmpty()) {
for (TypeName cn : configuredCopyable) if (cn != null && typeMatches(cn.toString(), node, anno.annotationType)) return List.of(anno);
for (String bn : BASE_COPYABLE_ANNOTATIONS) if (typeMatches(bn, node, anno.annotationType)) return List.of(anno);
}
ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>();
for (JavacNode child : node.down()) {
if (child.getKind() == Kind.ANNOTATION) {
JCAnnotation annotation = (JCAnnotation) child.get();
boolean match = false;
for (TypeName cn : configuredCopyable) if (cn != null && typeMatches(cn.toString(), node, annotation.annotationType)) {
result.append(annotation);
match = true;
break;
}
if (!match) for (String bn : BASE_COPYABLE_ANNOTATIONS) if (typeMatches(bn, node, annotation.annotationType)) {
result.append(annotation);
break;
}
}
}
return result.toList();
}
public static List<JCAnnotation> findCopyableToSetterAnnotations(JavacNode node) {
return findAnnotationsInList(node, COPY_TO_SETTER_ANNOTATIONS);
}
public static List<JCAnnotation> findCopyableToBuilderSingularSetterAnnotations(JavacNode node) {
return findAnnotationsInList(node, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS);
}
private static List<JCAnnotation> findAnnotationsInList(JavacNode node, java.util.List<String> annotationsToFind) {
JCAnnotation anno = null;
String annoName = null;
for (JavacNode child : node.down()) {
if (child.getKind() == Kind.ANNOTATION) {
if (anno != null) {
annoName = "";
break;
}
JCAnnotation annotation = (JCAnnotation) child.get();
annoName = annotation.annotationType.toString();
anno = annotation;
}
}
if (annoName == null) return List.nil();
if (!annoName.isEmpty()) {
for (String bn : annotationsToFind) if (typeMatches(bn, node, anno.annotationType)) return List.of(anno);
}
ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>();
for (JavacNode child : node.down()) {
if (child.getKind() == Kind.ANNOTATION) {
JCAnnotation annotation = (JCAnnotation) child.get();
boolean match = false;
if (!match) for (String bn : annotationsToFind) if (typeMatches(bn, node, annotation.annotationType)) {
result.append(annotation);
break;
}
}
}
return result.toList();
}
public static JCStatement generateNullCheck(JavacTreeMaker maker, JavacNode variable, JavacNode source) {
return generateNullCheck(maker, (JCVariableDecl) variable.get(), source);
}
public static JCStatement generateNullCheck(JavacTreeMaker maker, JCExpression typeNode, Name varName, JavacNode source, String customMessage) {
NullCheckExceptionType exceptionType = source.getAst().readConfiguration(ConfigurationKeys.NON_NULL_EXCEPTION_TYPE);
if (exceptionType == null) exceptionType = NullCheckExceptionType.NULL_POINTER_EXCEPTION;
if (typeNode != null && isPrimitive(typeNode)) return null;
JCLiteral message = maker.Literal(exceptionType.toExceptionMessage(varName.toString(), customMessage));
LombokImmutableList<String> method = exceptionType.getMethod();
if (method != null) {
return maker.Exec(maker.Apply(List.<JCExpression>nil(), chainDots(source, method), List.of(maker.Ident(varName), message)));
}
if (exceptionType == NullCheckExceptionType.ASSERTION) {
return maker.Assert(maker.Binary(CTC_NOT_EQUAL, maker.Ident(varName), maker.Literal(CTC_BOT, null)), message);
}
JCExpression exType = genTypeRef(source, exceptionType.getExceptionType());
JCExpression exception = maker.NewClass(null, List.<JCExpression>nil(), exType, List.<JCExpression>of(message), null);
JCStatement throwStatement = maker.Throw(exception);
JCBlock throwBlock = maker.Block(0, List.of(throwStatement));
return maker.If(maker.Binary(CTC_EQUAL, maker.Ident(varName), maker.Literal(CTC_BOT, null)), throwBlock, null);
}
public static JCStatement generateNullCheck(JavacTreeMaker maker, JCVariableDecl varDecl, JavacNode source) {
return generateNullCheck(maker, varDecl.vartype, varDecl.name, source, null);
}
public static List<Integer> createListOfNonExistentFields(List<String> list, JavacNode type, boolean excludeStandard, boolean excludeTransient) {
boolean[] matched = new boolean[list.size()];
for (JavacNode child : type.down()) {
if (list.isEmpty()) break;
if (child.getKind() != Kind.FIELD) continue;
JCVariableDecl field = (JCVariableDecl)child.get();
if (excludeStandard) {
if ((field.mods.flags & Flags.STATIC) != 0) continue;
if (field.name.toString().startsWith("$")) continue;
}
if (excludeTransient && (field.mods.flags & Flags.TRANSIENT) != 0) continue;
int idx = list.indexOf(child.getName());
if (idx > -1) matched[idx] = true;
}
ListBuffer<Integer> problematic = new ListBuffer<Integer>();
for (int i = 0 ; i < list.size() ; i++) {
if (!matched[i]) problematic.append(i);
}
return problematic.toList();
}
static List<JCAnnotation> unboxAndRemoveAnnotationParameter(JCAnnotation ast, String parameterName, String errorName, JavacNode annotationNode) {
ListBuffer<JCExpression> params = new ListBuffer<JCExpression>();
ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>();
outer:
for (JCExpression param : ast.args) {
boolean allowRaw;
String nameOfParam = "value";
JCExpression valueOfParam = null;
if (param instanceof JCAssign) {
JCAssign assign = (JCAssign) param;
if (assign.lhs instanceof JCIdent) {
JCIdent ident = (JCIdent) assign.lhs;
nameOfParam = ident.name.toString();
}
valueOfParam = assign.rhs;
}
{
int lastIdx;
for (lastIdx = nameOfParam.length() ; lastIdx > 0; lastIdx--) {
if (nameOfParam.charAt(lastIdx - 1) != '_') break;
}
allowRaw = lastIdx < nameOfParam.length();
nameOfParam = nameOfParam.substring(0, lastIdx);
}
if (!parameterName.equals(nameOfParam)) {
params.append(param);
continue outer;
}
int endPos = Javac.getEndPosition(param.pos(), (JCCompilationUnit) annotationNode.top().get());
annotationNode.getAst().removeFromDeferredDiagnostics(param.pos, endPos);
if (valueOfParam instanceof JCAnnotation) {
String dummyAnnotationName = ((JCAnnotation) valueOfParam).annotationType.toString();
dummyAnnotationName = dummyAnnotationName.replace("_", "").replace("$", "").replace("x", "").replace("X", "");
if (dummyAnnotationName.length() > 0) {
if (allowRaw) {
result.append((JCAnnotation) valueOfParam);
} else {
addError(errorName, annotationNode);
continue outer;
}
} else {
for (JCExpression expr : ((JCAnnotation) valueOfParam).args) {
if (expr instanceof JCAssign && ((JCAssign) expr).lhs instanceof JCIdent) {
JCIdent id = (JCIdent) ((JCAssign) expr).lhs;
if ("value".equals(id.name.toString())) {
expr = ((JCAssign) expr).rhs;
} else {
addError(errorName, annotationNode);
}
}
if (expr instanceof JCAnnotation) {
result.append((JCAnnotation) expr);
} else if (expr instanceof JCNewArray) {
for (JCExpression expr2 : ((JCNewArray) expr).elems) {
if (expr2 instanceof JCAnnotation) {
result.append((JCAnnotation) expr2);
} else {
addError(errorName, annotationNode);
continue outer;
}
}
} else {
addError(errorName, annotationNode);
continue outer;
}
}
}
} else if (valueOfParam instanceof JCNewArray) {
JCNewArray arr = (JCNewArray) valueOfParam;
if (arr.elems.isEmpty()) {
} else if (allowRaw) {
for (JCExpression jce : arr.elems) {
if (jce instanceof JCAnnotation) result.append((JCAnnotation) jce);
else addError(errorName, annotationNode);
}
} else {
addError(errorName, annotationNode);
}
} else {
addError(errorName, annotationNode);
}
}
ast.args = params.toList();
return result.toList();
}
private static void addError(String errorName, JavacNode node) {
if (node.getLatestJavaSpecSupported() < 8) {
node.addError("The correct format up to JDK7 is " + errorName + "=@__({@SomeAnnotation, @SomeOtherAnnotation}))");
} else {
node.addError("The correct format for JDK8+ is " + errorName + "_={@SomeAnnotation, @SomeOtherAnnotation})");
}
}
public static List<JCTypeParameter> copyTypeParams(JavacNode source, List<JCTypeParameter> params) {
if (params == null || params.isEmpty()) return params;
ListBuffer<JCTypeParameter> out = new ListBuffer<JCTypeParameter>();
JavacTreeMaker maker = source.getTreeMaker();
Context context = source.getContext();
for (JCTypeParameter tp : params) {
List<JCExpression> bounds = tp.bounds;
if (bounds != null && !bounds.isEmpty()) {
ListBuffer<JCExpression> boundsCopy = new ListBuffer<JCExpression>();
for (JCExpression expr : tp.bounds) {
boundsCopy.append(cloneType(maker, expr, source.get(), context));
}
bounds = boundsCopy.toList();
}
out.append(maker.TypeParameter(tp.name, bounds));
}
return out.toList();
}
public static List<JCAnnotation> getTypeUseAnnotations(JCExpression from) {
if (!JCAnnotatedTypeReflect.is(from)) return List.nil();
return JCAnnotatedTypeReflect.getAnnotations(from);
}
public static JCExpression removeTypeUseAnnotations(JCExpression from) {
if (!JCAnnotatedTypeReflect.is(from)) return from;
return JCAnnotatedTypeReflect.getUnderlyingType(from);
}
public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, JavacNode type, List<JCTypeParameter> params) {
JCClassDecl td = (JCClassDecl) type.get();
boolean instance = (td.mods.flags & Flags.STATIC) == 0;
return namePlusTypeParamsToTypeReference(maker, type.up(), td.name, instance, params, List.<JCAnnotation>nil());
}
public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, JavacNode type, List<JCTypeParameter> params, List<JCAnnotation> annotations) {
JCClassDecl td = (JCClassDecl) type.get();
boolean instance = (td.mods.flags & Flags.STATIC) == 0;
return namePlusTypeParamsToTypeReference(maker, type.up(), td.name, instance, params, annotations);
}
public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, JavacNode parentType, Name typeName, boolean instance, List<JCTypeParameter> params) {
return namePlusTypeParamsToTypeReference(maker, parentType, typeName, instance, params, List.<JCAnnotation>nil());
}
public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, JavacNode parentType, Name typeName, boolean instance, List<JCTypeParameter> params, List<JCAnnotation> annotations) {
JCExpression r = null;
if (parentType != null && parentType.getKind() == Kind.TYPE) {
JCClassDecl td = (JCClassDecl) parentType.get();
boolean outerInstance = instance && ((td.mods.flags & Flags.STATIC) == 0);
List<JCTypeParameter> outerParams = instance ? td.typarams : List.<JCTypeParameter>nil();
r = namePlusTypeParamsToTypeReference(maker, parentType.up(), td.name, outerInstance, outerParams, List.<JCAnnotation>nil());
}
r = r == null ? maker.Ident(typeName) : maker.Select(r, typeName);
if (!annotations.isEmpty()) r = JCAnnotatedTypeReflect.create(annotations, r);
if (!params.isEmpty()) r = maker.TypeApply(r, typeParameterNames(maker, params));
return r;
}
public static List<JCExpression> typeParameterNames(JavacTreeMaker maker, List<JCTypeParameter> params) {
ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
for (JCTypeParameter param : params) {
typeArgs.append(maker.Ident(param.name));
}
return typeArgs.toList();
}
public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(JavacNode typeNode, JavacNode errorNode) {
List<String> disallowed = List.nil();
for (JavacNode child : typeNode.down()) {
for (String annType : INVALID_ON_BUILDERS) {
if (annotationTypeMatches(annType, child)) {
int lastIndex = annType.lastIndexOf('.');
disallowed = disallowed.append(lastIndex == -1 ? annType : annType.substring(lastIndex + 1));
}
}
}
int size = disallowed.size();
if (size == 0) return;
if (size == 1) {
errorNode.addError("@" + disallowed.head + " 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());
}
static List<JCAnnotation> copyAnnotations(List<? extends JCExpression> in) {
ListBuffer<JCAnnotation> out = new ListBuffer<JCAnnotation>();
for (JCExpression expr : in) {
if (!(expr instanceof JCAnnotation)) continue;
out.append((JCAnnotation) expr.clone());
}
return out.toList();
}
static List<JCAnnotation> mergeAnnotations(List<JCAnnotation> a, List<JCAnnotation> b) {
if (a == null || a.isEmpty()) return b;
if (b == null || b.isEmpty()) return a;
ListBuffer<JCAnnotation> out = new ListBuffer<JCAnnotation>();
for (JCAnnotation ann : a) out.append(ann);
for (JCAnnotation ann : b) out.append(ann);
return out.toList();
}
static boolean isClass(JavacNode typeNode) {
return isClassAndDoesNotHaveFlags(typeNode, Flags.INTERFACE | Flags.ENUM | Flags.ANNOTATION);
}
static boolean isClassOrEnum(JavacNode typeNode) {
return isClassAndDoesNotHaveFlags(typeNode, Flags.INTERFACE | Flags.ANNOTATION);
}
public static boolean isClassAndDoesNotHaveFlags(JavacNode typeNode, int flags) {
JCClassDecl typeDecl = null;
if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl)typeNode.get();
else return false;
long typeDeclflags = typeDecl == null ? 0 : typeDecl.mods.flags;
return (typeDeclflags & flags) == 0;
}
public static JavacNode upToTypeNode(JavacNode node) {
if (node == null) throw new NullPointerException("node");
while ((node != null) && !(node.get() instanceof JCClassDecl)) node = node.up();
return node;
}
public static List<JCExpression> cloneTypes(JavacTreeMaker maker, List<JCExpression> in, JCTree source, Context context) {
if (in.isEmpty()) return List.nil();
if (in.size() == 1) return List.of(cloneType(maker, in.get(0), source, context));
ListBuffer<JCExpression> lb = new ListBuffer<JCExpression>();
for (JCExpression expr : in) lb.append(cloneType(maker, expr, source, context));
return lb.toList();
}
public static JCExpression cloneType(JavacTreeMaker maker, JCExpression in, JCTree source, Context context) {
JCExpression out = cloneType0(maker, in);
if (out != null) recursiveSetGeneratedBy(out, source, context);
return out;
}
private static JCExpression cloneType0(JavacTreeMaker maker, JCTree in) {
if (in == null) return null;
if (in instanceof JCPrimitiveTypeTree) return (JCExpression) in;
if (in instanceof JCIdent) {
return maker.Ident(((JCIdent) in).name);
}
if (in instanceof JCFieldAccess) {
JCFieldAccess fa = (JCFieldAccess) in;
return maker.Select(cloneType0(maker, fa.selected), fa.name);
}
if (in instanceof JCArrayTypeTree) {
JCArrayTypeTree att = (JCArrayTypeTree) in;
return maker.TypeArray(cloneType0(maker, att.elemtype));
}
if (in instanceof JCTypeApply) {
JCTypeApply ta = (JCTypeApply) in;
ListBuffer<JCExpression> lb = new ListBuffer<JCExpression>();
for (JCExpression typeArg : ta.arguments) {
lb.append(cloneType0(maker, typeArg));
}
return maker.TypeApply(cloneType0(maker, ta.clazz), lb.toList());
}
if (in instanceof JCWildcard) {
JCWildcard w = (JCWildcard) in;
JCExpression newInner = cloneType0(maker, w.inner);
TypeBoundKind newKind;
switch (w.getKind()) {
case SUPER_WILDCARD:
newKind = maker.TypeBoundKind(BoundKind.SUPER);
break;
case EXTENDS_WILDCARD:
newKind = maker.TypeBoundKind(BoundKind.EXTENDS);
break;
default:
case UNBOUNDED_WILDCARD:
newKind = maker.TypeBoundKind(BoundKind.UNBOUND);
break;
}
return maker.Wildcard(newKind, newInner);
}
if (JCAnnotatedTypeReflect.is(in)) {
JCExpression underlyingType = cloneType0(maker, JCAnnotatedTypeReflect.getUnderlyingType(in));
List<JCAnnotation> anns = copyAnnotations(JCAnnotatedTypeReflect.getAnnotations(in));
return JCAnnotatedTypeReflect.create(anns, underlyingType);
}
return (JCExpression) in;
}
public static enum CopyJavadoc {
VERBATIM {
@Override public String apply(final JCCompilationUnit cu, final JavacNode node) {
return Javac.getDocComment(cu, node.get());
}
},
GETTER {
@Override public String apply(final JCCompilationUnit cu, final JavacNode node) {
final JCTree n = node.get();
String javadoc = Javac.getDocComment(cu, n);
String out = getJavadocSection(javadoc, "GETTER");
final boolean sectionBased = out != null;
if (!sectionBased) {
out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@param(?:eter)?\\s+.*");
}
node.getAst().cleanupTask("javadocfilter-getter", n, new CleanupTask() {
@Override public void cleanup() {
String javadoc = Javac.getDocComment(cu, n);
if (javadoc == null || javadoc.isEmpty()) return;
javadoc = stripSectionsFromJavadoc(javadoc);
if (!sectionBased) {
javadoc = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@returns?\\s+.*");
}
Javac.setDocComment(cu, n, javadoc);
}
});
return out;
}
},
SETTER {
@Override public String apply(final JCCompilationUnit cu, final JavacNode node) {
return applySetter(cu, node, "SETTER");
}
},
WITH {
@Override public String apply(final JCCompilationUnit cu, final JavacNode node) {
return addReturnsUpdatedSelfIfNeeded(applySetter(cu, node, "WITH|WITHER"));
}
},
WITH_BY {
@Override public String apply(final JCCompilationUnit cu, final JavacNode node) {
return applySetter(cu, node, "WITHBY|WITH_BY");
}
};
public abstract String apply(final JCCompilationUnit cu, final JavacNode node);
private static String applySetter(final JCCompilationUnit cu, JavacNode node, String sectionName) {
final JCTree n = node.get();
String javadoc = Javac.getDocComment(cu, n);
String out = getJavadocSection(javadoc, sectionName);
final boolean sectionBased = out != null;
if (!sectionBased) {
out = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@returns?\\s+.*");
}
node.getAst().cleanupTask("javadocfilter-setter", n, new CleanupTask() {
@Override public void cleanup() {
String javadoc = Javac.getDocComment(cu, n);
if (javadoc == null || javadoc.isEmpty()) return;
javadoc = stripSectionsFromJavadoc(javadoc);
if (!sectionBased) {
javadoc = stripLinesWithTagFromJavadoc(stripSectionsFromJavadoc(javadoc), "@param(?:eter)?\\s+.*");
}
Javac.setDocComment(cu, n, javadoc);
}
});
return shouldReturnThis(node) ? addReturnsThisIfNeeded(out) : out;
}
}
public static void copyJavadoc(JavacNode from, JCTree to, CopyJavadoc copyMode) {
copyJavadoc(from, to, copyMode, false);
}
public static void copyJavadoc(JavacNode from, JCTree to, CopyJavadoc copyMode, boolean forceAddReturn) {
if (copyMode == null) copyMode = CopyJavadoc.VERBATIM;
try {
JCCompilationUnit cu = ((JCCompilationUnit) from.top().get());
String newJavadoc = copyMode.apply(cu, from);
if (newJavadoc != null) {
if (forceAddReturn) newJavadoc = addReturnsThisIfNeeded(newJavadoc);
Javac.setDocComment(cu, to, newJavadoc);
}
} catch (Exception ignore) {}
}
public static boolean isDirectDescendantOfObject(JavacNode typeNode) {
if (!(typeNode.get() instanceof JCClassDecl)) throw new IllegalArgumentException("not a type node");
JCTree extending = Javac.getExtendsClause((JCClassDecl) typeNode.get());
if (extending == null) return true;
String p = extending.toString();
return p.equals("Object") || p.equals("java.lang.Object");
}
public static void createRelevantNullableAnnotation(JavacNode typeNode, JCMethodDecl mth) {
NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
if (lib == null) return;
applyAnnotationToMethodDecl(typeNode, mth, lib.getNullableAnnotation(), lib.isTypeUse());
}
public static void createRelevantNonNullAnnotation(JavacNode typeNode, JCMethodDecl mth) {
NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
if (lib == null) return;
applyAnnotationToMethodDecl(typeNode, mth, lib.getNonNullAnnotation(), lib.isTypeUse());
}
public static void createRelevantNonNullAnnotation(JavacNode typeNode, JCVariableDecl arg) {
NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
if (lib == null) return;
applyAnnotationToVarDecl(typeNode, arg, lib.getNonNullAnnotation(), lib.isTypeUse());
}
public static void createRelevantNullableAnnotation(JavacNode typeNode, JCVariableDecl arg) {
NullAnnotationLibrary lib = typeNode.getAst().readConfiguration(ConfigurationKeys.ADD_NULL_ANNOTATIONS);
if (lib == null) return;
applyAnnotationToVarDecl(typeNode, arg, lib.getNullableAnnotation(), lib.isTypeUse());
}
private static void applyAnnotationToMethodDecl(JavacNode typeNode, JCMethodDecl mth, String annType, boolean typeUse) {
if (annType == null) return;
JavacTreeMaker maker = typeNode.getTreeMaker();
JCAnnotation m = maker.Annotation(genTypeRef(typeNode, annType), List.<JCExpression>nil());
if (typeUse) {
JCExpression resType = mth.restype;
if (resType instanceof JCTypeApply) {
JCTypeApply ta = (JCTypeApply) resType;
resType = ta.clazz;
}
if (resType instanceof JCFieldAccess || resType instanceof JCArrayTypeTree) {
mth.restype = maker.AnnotatedType(List.of(m), resType);
return;
}
if (JCAnnotatedTypeReflect.is(resType)) {
List<JCAnnotation> annotations = JCAnnotatedTypeReflect.getAnnotations(resType);
JCAnnotatedTypeReflect.setAnnotations(resType, annotations.prepend(m));
return;
}
if (resType instanceof JCPrimitiveTypeTree || resType instanceof JCIdent) {
mth.mods.annotations = mth.mods.annotations == null ? List.of(m) : mth.mods.annotations.prepend(m);
}
} else {
mth.mods.annotations = mth.mods.annotations == null ? List.of(m) : mth.mods.annotations.prepend(m);
}
}
private static void applyAnnotationToVarDecl(JavacNode typeNode, JCVariableDecl arg, String annType, boolean typeUse) {
if (annType == null) return;
JavacTreeMaker maker = typeNode.getTreeMaker();
JCAnnotation m = maker.Annotation(genTypeRef(typeNode, annType), List.<JCExpression>nil());
if (typeUse) {
JCExpression varType = arg.vartype;
JCTypeApply ta = null;
if (varType instanceof JCTypeApply) {
ta = (JCTypeApply) varType;
varType = ta.clazz;
}
if (varType instanceof JCFieldAccess || varType instanceof JCArrayTypeTree) {
varType = maker.AnnotatedType(List.of(m), varType);
if (ta != null) ta.clazz = varType;
else arg.vartype = varType;
return;
}
if (JCAnnotatedTypeReflect.is(varType)) {
List<JCAnnotation> annotations = JCAnnotatedTypeReflect.getAnnotations(varType);
JCAnnotatedTypeReflect.setAnnotations(varType, annotations.prepend(m));
return;
}
if (varType instanceof JCPrimitiveTypeTree || varType instanceof JCIdent) {
arg.mods.annotations = arg.mods.annotations == null ? List.of(m) : arg.mods.annotations.prepend(m);
}
} else {
arg.mods.annotations = arg.mods.annotations == null ? List.of(m) : arg.mods.annotations.prepend(m);
}
}
}