Added initial support for the @Data annotation. Currently produces getters and setters only,

not yet a constructor, toString, hashCode, or equals.

HandleGetter and HandleSetter have been updated to handle static (theoretic; you can't put annotations on static fields normally).

You can now make AnnotationValue objects using just an annotationNode and a target type, as well as check if a given annotationNode is likely to represent a target annotation type. This is in Javac and Eclipse classes.

HandleGetter and HandleSetter can now be asked to make a getter/setter, and will grab access level off of a Getter/Setter annotation, if present.
This commit is contained in:
Reinier Zwitserloot committed 2009-06-19 16:24:10 +02:00
1 parent e56ab6c245
commit ff306fa5d9
13 files changed
+506 -195

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
package lombok;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
String staticConstructor() default "";
int hashCodePrime() default 31;
}
+3 -1
View File
@@ -8,5 +8,7 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
AccessLevel value() default lombok.AccessLevel.PUBLIC;
lombok.AccessLevel DEFAULT_ACCESS_LEVEL = lombok.AccessLevel.PUBLIC;
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
}
+3 -1
View File
@@ -8,5 +8,7 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
AccessLevel value() default lombok.AccessLevel.PUBLIC;
lombok.AccessLevel DEFAULT_ACCESS_LEVEL = lombok.AccessLevel.PUBLIC;
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
}
+128
View File
@@ -1,12 +1,41 @@
package lombok.eclipse;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.core.AnnotationValues;
import lombok.core.TypeLibrary;
import lombok.core.TypeResolver;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.eclipse.EclipseAST.Node;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.Literal;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.osgi.framework.Bundle;
public class Eclipse {
private Eclipse() {
//Prevent instantiation
}
private static final String DEFAULT_BUNDLE = "org.eclipse.jdt.core";
public static void error(String message) {
error(message, DEFAULT_BUNDLE, null);
@@ -41,4 +70,103 @@ public class Eclipse {
}
return sb.toString();
}
public static boolean annotationTypeMatches(Class<? extends java.lang.annotation.Annotation> type, Node node) {
if ( node.getKind() != Kind.ANNOTATION ) return false;
TypeReference typeRef = ((Annotation)node.get()).type;
if ( typeRef == null || typeRef.getTypeName() == null ) return false;
String typeName = toQualifiedName(typeRef.getTypeName());
TypeLibrary library = new TypeLibrary();
library.addType(type.getName());
TypeResolver resolver = new TypeResolver(library, node.getPackageDeclaration(), node.getImportStatements());
Collection<String> typeMatches = resolver.findTypeMatches(node, typeName);
for ( String match : typeMatches ) {
if ( match.equals(type.getName()) ) return true;
}
return false;
}
public static <A extends java.lang.annotation.Annotation> AnnotationValues<A>
createAnnotation(Class<A> type, final Node annotationNode) {
Annotation annotation = (Annotation) annotationNode.get();
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
final MemberValuePair[] pairs = annotation.memberValuePairs();
for ( Method m : type.getDeclaredMethods() ) {
if ( !Modifier.isPublic(m.getModifiers()) ) continue;
String name = m.getName();
List<String> raws = new ArrayList<String>();
List<Object> guesses = new ArrayList<Object>();
Expression fullExpression = null;
Expression[] expressions = null;
if ( pairs != null ) for ( MemberValuePair pair : pairs ) {
char[] n = pair.name;
String mName = n == null ? "value" : new String(name);
if ( !mName.equals(name) ) continue;
fullExpression = pair.value;
}
if ( fullExpression != null ) {
if ( fullExpression instanceof ArrayInitializer ) {
expressions = ((ArrayInitializer)fullExpression).expressions;
} else expressions = new Expression[] { fullExpression };
for ( Expression ex : expressions ) {
StringBuffer sb = new StringBuffer();
ex.print(0, sb);
raws.add(sb.toString());
guesses.add(calculateValue(ex));
}
}
final Expression fullExpr = fullExpression;
final Expression[] exprs = expressions;
values.put(name, new AnnotationValue(annotationNode, raws, guesses) {
@Override public void setError(String message, int valueIdx) {
Expression ex;
if ( valueIdx == -1 ) ex = fullExpr;
else ex = exprs[valueIdx];
int sourceStart = ex.sourceStart;
int sourceEnd = ex.sourceEnd;
annotationNode.addError(message, sourceStart, sourceEnd);
}
});
}
return new AnnotationValues<A>(type, values, annotationNode);
}
private static Object calculateValue(Expression e) {
if ( e instanceof Literal ) {
((Literal)e).computeConstant();
switch ( e.constant.typeID() ) {
case TypeIds.T_int: return e.constant.intValue();
case TypeIds.T_byte: return e.constant.byteValue();
case TypeIds.T_short: return e.constant.shortValue();
case TypeIds.T_char: return e.constant.charValue();
case TypeIds.T_float: return e.constant.floatValue();
case TypeIds.T_double: return e.constant.doubleValue();
case TypeIds.T_boolean: return e.constant.booleanValue();
case TypeIds.T_long: return e.constant.longValue();
case TypeIds.T_JavaLangString: return e.constant.stringValue();
default: return null;
}
} else if ( e instanceof ClassLiteralAccess ) {
return Eclipse.toQualifiedName(((ClassLiteralAccess)e).type.getTypeName());
} else if ( e instanceof SingleNameReference ) {
return new String(((SingleNameReference)e).token);
} else if ( e instanceof QualifiedNameReference ) {
String qName = Eclipse.toQualifiedName(((QualifiedNameReference)e).tokens);
int idx = qName.lastIndexOf('.');
return idx == -1 ? qName : qName.substring(idx+1);
}
return null;
}
}
+2 -88
View File
@@ -3,13 +3,10 @@ package lombok.eclipse;
import static lombok.eclipse.Eclipse.toQualifiedName;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
@@ -18,20 +15,11 @@ import lombok.core.AnnotationValues;
import lombok.core.SpiLoadUtil;
import lombok.core.TypeLibrary;
import lombok.core.TypeResolver;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.AnnotationValues.AnnotationValueDecodeFail;
import lombok.eclipse.EclipseAST.Node;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.Literal;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
public class HandlerLibrary {
private TypeLibrary typeLibrary = new TypeLibrary();
@@ -47,54 +35,8 @@ public class HandlerLibrary {
public boolean handle(org.eclipse.jdt.internal.compiler.ast.Annotation annotation,
final Node annotationNode) {
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
final MemberValuePair[] pairs = annotation.memberValuePairs();
for ( Method m : annotationClass.getDeclaredMethods() ) {
if ( !Modifier.isPublic(m.getModifiers()) ) continue;
String name = m.getName();
List<String> raws = new ArrayList<String>();
List<Object> guesses = new ArrayList<Object>();
Expression fullExpression = null;
Expression[] expressions = null;
if ( pairs != null ) for ( MemberValuePair pair : pairs ) {
char[] n = pair.name;
String mName = n == null ? "value" : new String(name);
if ( !mName.equals(name) ) continue;
fullExpression = pair.value;
}
if ( fullExpression != null ) {
if ( fullExpression instanceof ArrayInitializer ) {
expressions = ((ArrayInitializer)fullExpression).expressions;
} else expressions = new Expression[] { fullExpression };
for ( Expression ex : expressions ) {
StringBuffer sb = new StringBuffer();
ex.print(0, sb);
raws.add(sb.toString());
guesses.add(calculateValue(ex));
}
}
final Expression fullExpr = fullExpression;
final Expression[] exprs = expressions;
values.put(name, new AnnotationValue(annotationNode, raws, guesses) {
@Override public void setError(String message, int valueIdx) {
Expression ex;
if ( valueIdx == -1 ) ex = fullExpr;
else ex = exprs[valueIdx];
int sourceStart = ex.sourceStart;
int sourceEnd = ex.sourceEnd;
annotationNode.addError(message, sourceStart, sourceEnd);
}
});
}
return handler.handle(new AnnotationValues<T>(annotationClass, values, annotationNode), annotation, annotationNode);
AnnotationValues<T> annValues = Eclipse.createAnnotation(annotationClass, annotationNode);
return handler.handle(annValues, annotation, annotationNode);
}
}
@@ -103,34 +45,6 @@ public class HandlerLibrary {
private Collection<EclipseASTVisitor> visitorHandlers = new ArrayList<EclipseASTVisitor>();
private static Object calculateValue(Expression e) {
if ( e instanceof Literal ) {
((Literal)e).computeConstant();
switch ( e.constant.typeID() ) {
case TypeIds.T_int: return e.constant.intValue();
case TypeIds.T_byte: return e.constant.byteValue();
case TypeIds.T_short: return e.constant.shortValue();
case TypeIds.T_char: return e.constant.charValue();
case TypeIds.T_float: return e.constant.floatValue();
case TypeIds.T_double: return e.constant.doubleValue();
case TypeIds.T_boolean: return e.constant.booleanValue();
case TypeIds.T_long: return e.constant.longValue();
case TypeIds.T_JavaLangString: return e.constant.stringValue();
default: return null;
}
} else if ( e instanceof ClassLiteralAccess ) {
return Eclipse.toQualifiedName(((ClassLiteralAccess)e).type.getTypeName());
} else if ( e instanceof SingleNameReference ) {
return new String(((SingleNameReference)e).token);
} else if ( e instanceof QualifiedNameReference ) {
String qName = Eclipse.toQualifiedName(((QualifiedNameReference)e).tokens);
int idx = qName.lastIndexOf('.');
return idx == -1 ? qName : qName.substring(idx+1);
}
return null;
}
public static HandlerLibrary load() {
HandlerLibrary lib = new HandlerLibrary();
@@ -0,0 +1,49 @@
package lombok.eclipse.handlers;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.mangosdk.spi.ProviderFor;
import lombok.Data;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseAST.Node;
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleData implements EclipseAnnotationHandler<Data> {
@Override public boolean handle(AnnotationValues<Data> annotation, Annotation ast, Node annotationNode) {
Node typeNode = annotationNode.up();
TypeDeclaration typeDecl = null;
if ( typeNode.get() instanceof TypeDeclaration ) typeDecl = (TypeDeclaration) typeNode.get();
int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
boolean notAClass = (modifiers &
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0;
if ( typeDecl == null || notAClass ) {
annotationNode.addError("@Data is only supported on a class.");
return false;
}
List<Node> nodesForEquality = new ArrayList<Node>();
for ( Node child : typeNode.down() ) {
if ( child.getKind() != Kind.FIELD ) continue;
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
//Skip static fields.
if ( (fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0 ) continue;
if ( (fieldDecl.modifiers & ClassFileConstants.AccTransient) == 0 ) nodesForEquality.add(child);
new HandleGetter().generateGetterForField(child, annotationNode.get());
if ( (fieldDecl.modifiers & ClassFileConstants.AccFinal) == 0 )
new HandleSetter().generateSetterForField(child, annotationNode.get());
}
//TODO generate constructor, hashCode, equals, toString.
return true;
}
}
+41 -9
View File
@@ -2,9 +2,12 @@ package lombok.eclipse.handlers;
import static lombok.eclipse.handlers.PKG.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.core.AnnotationValues;
import lombok.core.TransformationsUtil;
import lombok.core.AST.Kind;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseAST.Node;
@@ -19,30 +22,59 @@ import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.mangosdk.spi.ProviderFor;
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleGetter implements EclipseAnnotationHandler<Getter> {
public void generateGetterForField(Node fieldNode, ASTNode pos) {
AccessLevel level = Getter.DEFAULT_ACCESS_LEVEL;
Node errorNode = fieldNode;
for ( Node child : fieldNode.down() ) {
if ( child.getKind() == Kind.ANNOTATION ) {
if ( Eclipse.annotationTypeMatches(Getter.class, child) ) {
level = Eclipse.createAnnotation(Getter.class, child).getInstance().value();
errorNode = child;
pos = child.get();
break;
}
}
}
createGetterForField(level, fieldNode, errorNode, pos);
}
@Override public boolean handle(AnnotationValues<Getter> annotation, Annotation ast, Node annotationNode) {
if ( !(annotationNode.up().get() instanceof FieldDeclaration) ) return false;
FieldDeclaration field = (FieldDeclaration) annotationNode.up().get();
Node fieldNode = annotationNode.up();
AccessLevel level = annotation.getInstance().value();
return createGetterForField(level, fieldNode, annotationNode, annotationNode.get());
}
private boolean createGetterForField(AccessLevel level, Node fieldNode, Node errorNode, ASTNode pos) {
if ( fieldNode.getKind() != Kind.FIELD ) {
errorNode.addError("@Getter is only supported on a field.");
return false;
}
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
TypeReference fieldType = field.type;
String getterName = TransformationsUtil.toGetterName(
new String(field.name), nameEquals(fieldType.getTypeName(), "boolean"));
TypeDeclaration parent = (TypeDeclaration) annotationNode.up().up().get();
TypeDeclaration parent = (TypeDeclaration) fieldNode.up().get();
if ( parent.methods != null ) for ( AbstractMethodDeclaration method : parent.methods ) {
if ( method.selector != null && new String(method.selector).equals(getterName) ) {
annotationNode.addWarning(String.format(
errorNode.addWarning(String.format(
"Not generating %s(): A method with that name already exists", getterName));
return false;
}
}
int modifier = toModifier(annotation.getInstance().value());
int modifier = toModifier(level) | (field.modifiers & ClassFileConstants.AccStatic);
MethodDeclaration method = generateGetter(parent, field, getterName, modifier, ast);
MethodDeclaration method = generateGetter(parent, field, getterName, modifier, pos);
if ( parent.methods == null ) {
parent.methods = new AbstractMethodDeclaration[1];
@@ -58,7 +90,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
}
private MethodDeclaration generateGetter(TypeDeclaration parent, FieldDeclaration field, String name,
int modifier, Annotation ast) {
int modifier, ASTNode pos) {
MethodDeclaration method = new MethodDeclaration(parent.compilationResult);
method.modifiers = modifier;
method.returnType = field.type;
@@ -72,8 +104,8 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
method.bits |= ASTNode.Bit24;
Expression fieldExpression = new SingleNameReference(field.name, (field.declarationSourceStart << 32) | field.declarationSourceEnd);
Statement returnStatement = new ReturnStatement(fieldExpression, field.sourceStart, field.sourceEnd);
method.bodyStart = method.declarationSourceStart = method.sourceStart = ast.sourceStart;
method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = ast.sourceEnd;
method.bodyStart = method.declarationSourceStart = method.sourceStart = pos.sourceStart;
method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = pos.sourceEnd;
method.statements = new Statement[] { returnStatement };
return method;
}
+36 -7
View File
@@ -15,36 +15,65 @@ import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.mangosdk.spi.ProviderFor;
import lombok.AccessLevel;
import lombok.Setter;
import lombok.core.AnnotationValues;
import lombok.core.TransformationsUtil;
import lombok.core.AST.Kind;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseAST.Node;
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleSetter implements EclipseAnnotationHandler<Setter> {
public void generateSetterForField(Node fieldNode, ASTNode pos) {
AccessLevel level = Setter.DEFAULT_ACCESS_LEVEL;
Node errorNode = fieldNode;
for ( Node child : fieldNode.down() ) {
if ( child.getKind() == Kind.ANNOTATION ) {
if ( Eclipse.annotationTypeMatches(Setter.class, child) ) {
level = Eclipse.createAnnotation(Setter.class, child).getInstance().value();
errorNode = child;
pos = child.get();
break;
}
}
}
createSetterForField(level, fieldNode, errorNode, pos);
}
@Override public boolean handle(AnnotationValues<Setter> annotation, Annotation ast, Node annotationNode) {
if ( !(annotationNode.up().get() instanceof FieldDeclaration) ) return false;
FieldDeclaration field = (FieldDeclaration) annotationNode.up().get();
Node fieldNode = annotationNode.up();
if ( fieldNode.getKind() != Kind.FIELD ) return false;
AccessLevel level = annotation.getInstance().value();
return createSetterForField(level, fieldNode, annotationNode, annotationNode.get());
}
private boolean createSetterForField(AccessLevel level, Node fieldNode, Node errorNode, ASTNode pos) {
if ( fieldNode.getKind() != Kind.FIELD ) return false;
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
String setterName = TransformationsUtil.toSetterName(new String(field.name));
TypeDeclaration parent = (TypeDeclaration) annotationNode.up().up().get();
TypeDeclaration parent = (TypeDeclaration) fieldNode.up().get();
if ( parent.methods != null ) for ( AbstractMethodDeclaration method : parent.methods ) {
if ( method.selector != null && new String(method.selector).equals(setterName) ) {
annotationNode.addWarning(String.format(
errorNode.addWarning(String.format(
"Not generating %s(%s %s): A method with that name already exists",
setterName, field.type, new String(field.name)));
return false;
}
}
int modifier = toModifier(annotation.getInstance().value());
int modifier = toModifier(level) | (field.modifiers & ClassFileConstants.AccStatic);
MethodDeclaration method = generateSetter(parent, field, setterName, modifier, ast);
MethodDeclaration method = generateSetter(parent, field, setterName, modifier, pos);
if ( parent.methods == null ) {
parent.methods = new AbstractMethodDeclaration[1];
@@ -60,7 +89,7 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> {
}
private MethodDeclaration generateSetter(TypeDeclaration parent, FieldDeclaration field, String name,
int modifier, Annotation ast) {
int modifier, ASTNode ast) {
long pos = (((long)ast.sourceStart) << 32) | ast.sourceEnd;
MethodDeclaration method = new MethodDeclaration(parent.compilationResult);
method.modifiers = modifier;
+1 -63
View File
@@ -1,13 +1,10 @@
package lombok.javac;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
@@ -16,22 +13,13 @@ import javax.annotation.processing.Messager;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import lombok.core.AnnotationValues;
import lombok.core.SpiLoadUtil;
import lombok.core.TypeLibrary;
import lombok.core.TypeResolver;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.AnnotationValues.AnnotationValueDecodeFail;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCNewArray;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
public class HandlerLibrary {
@@ -53,58 +41,8 @@ public class HandlerLibrary {
this.annotationClass = annotationClass;
}
private Object calculateGuess(JCExpression expr) {
if ( expr instanceof JCLiteral ) {
return ((JCLiteral)expr).value;
} else if ( expr instanceof JCIdent || expr instanceof JCFieldAccess ) {
String x = expr.toString();
if ( x.endsWith(".class") ) x = x.substring(0, x.length() - 6);
else {
int idx = x.lastIndexOf('.');
if ( idx > -1 ) x = x.substring(idx + 1);
}
return x;
} else return null;
}
public boolean handle(final JavacAST.Node node) {
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
JCAnnotation anno = (JCAnnotation) node.get();
List<JCExpression> arguments = anno.getArguments();
for ( Method m : annotationClass.getDeclaredMethods() ) {
if ( !Modifier.isPublic(m.getModifiers()) ) continue;
String name = m.getName();
List<String> raws = new ArrayList<String>();
List<Object> guesses = new ArrayList<Object>();
final List<DiagnosticPosition> positions = new ArrayList<DiagnosticPosition>();
for ( JCExpression arg : arguments ) {
JCAssign assign = (JCAssign) arg;
String mName = assign.lhs.toString();
if ( !mName.equals(name) ) continue;
JCExpression rhs = assign.rhs;
if ( rhs instanceof JCNewArray ) {
List<JCExpression> elems = ((JCNewArray)rhs).elems;
for ( JCExpression inner : elems ) {
raws.add(inner.toString());
guesses.add(calculateGuess(inner));
positions.add(inner.pos());
}
} else {
raws.add(rhs.toString());
guesses.add(calculateGuess(rhs));
positions.add(rhs.pos());
}
}
values.put(name, new AnnotationValue(node, raws, guesses) {
@Override public void setError(String message, int valueIdx) {
node.addError(message, positions.get(valueIdx));
}
});
}
return handler.handle(new AnnotationValues<T>(annotationClass, values, node), (JCAnnotation)node.get(), node);
return handler.handle(Javac.createAnnotation(annotationClass, node), (JCAnnotation)node.get(), node);
}
}
+102
View File
@@ -0,0 +1,102 @@
package lombok.javac;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.core.AnnotationValues;
import lombok.core.TypeLibrary;
import lombok.core.TypeResolver;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.javac.JavacAST.Node;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCNewArray;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
public class Javac {
private Javac() {
//prevent instantiation
}
public static boolean annotationTypeMatches(Class<? extends Annotation> type, Node node) {
if ( node.getKind() != Kind.ANNOTATION ) return false;
String typeName = ((JCAnnotation)node.get()).annotationType.toString();
TypeLibrary library = new TypeLibrary();
library.addType(type.getName());
TypeResolver resolver = new TypeResolver(library, node.getPackageDeclaration(), node.getImportStatements());
Collection<String> typeMatches = resolver.findTypeMatches(node, typeName);
for ( String match : typeMatches ) {
if ( match.equals(type.getName()) ) return true;
}
return false;
}
public static <A extends Annotation> AnnotationValues<A> createAnnotation(Class<A> type, final Node node) {
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
JCAnnotation anno = (JCAnnotation) node.get();
List<JCExpression> arguments = anno.getArguments();
for ( Method m : type.getDeclaredMethods() ) {
if ( !Modifier.isPublic(m.getModifiers()) ) continue;
String name = m.getName();
List<String> raws = new ArrayList<String>();
List<Object> guesses = new ArrayList<Object>();
final List<DiagnosticPosition> positions = new ArrayList<DiagnosticPosition>();
for ( JCExpression arg : arguments ) {
JCAssign assign = (JCAssign) arg;
String mName = assign.lhs.toString();
if ( !mName.equals(name) ) continue;
JCExpression rhs = assign.rhs;
if ( rhs instanceof JCNewArray ) {
List<JCExpression> elems = ((JCNewArray)rhs).elems;
for ( JCExpression inner : elems ) {
raws.add(inner.toString());
guesses.add(calculateGuess(inner));
positions.add(inner.pos());
}
} else {
raws.add(rhs.toString());
guesses.add(calculateGuess(rhs));
positions.add(rhs.pos());
}
}
values.put(name, new AnnotationValue(node, raws, guesses) {
@Override public void setError(String message, int valueIdx) {
node.addError(message, positions.get(valueIdx));
}
});
}
return new AnnotationValues<A>(type, values, node);
}
private static Object calculateGuess(JCExpression expr) {
if ( expr instanceof JCLiteral ) {
return ((JCLiteral)expr).value;
} else if ( expr instanceof JCIdent || expr instanceof JCFieldAccess ) {
String x = expr.toString();
if ( x.endsWith(".class") ) x = x.substring(0, x.length() - 6);
else {
int idx = x.lastIndexOf('.');
if ( idx > -1 ) x = x.substring(idx + 1);
}
return x;
} else return null;
}
}
+49
View File
@@ -0,0 +1,49 @@
package lombok.javac.handlers;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacAST.Node;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
@ProviderFor(JavacAnnotationHandler.class)
public class HandleData implements JavacAnnotationHandler<Data> {
@Override public boolean handle(AnnotationValues<Data> annotation, JCAnnotation ast, Node annotationNode) {
Node typeNode = annotationNode.up();
JCClassDecl typeDecl = null;
if ( typeNode.get() instanceof JCClassDecl ) typeDecl = (JCClassDecl)typeNode.get();
long flags = typeDecl.mods.flags;
boolean notAClass = (flags & (Flags.INTERFACE | Flags.ENUM | Flags.ANNOTATION)) != 0;
if ( typeDecl != null || notAClass ) {
annotationNode.addError("@Data is only supported on a class.");
return false;
}
List<Node> nodesForEquality = new ArrayList<Node>();
for ( Node child : typeNode.down() ) {
if ( child.getKind() != Kind.FIELD ) continue;
JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
long fieldFlags = fieldDecl.mods.flags;
//Skip static fields.
if ( (fieldFlags & Flags.STATIC) != 0 ) continue;
if ( (fieldFlags & Flags.TRANSIENT) == 0 ) nodesForEquality.add(child);
new HandleGetter().generateGetterForField(child, annotationNode.get());
if ( (fieldFlags & Flags.FINAL) == 0 )
new HandleSetter().generateSetterForField(child, annotationNode.get());
}
//TODO generate constructor, hashCode, equals, toString.
return true;
}
}
+40 -13
View File
@@ -2,14 +2,17 @@ package lombok.javac.handlers;
import static lombok.javac.handlers.PKG.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacAST;
import lombok.javac.JavacAST.Node;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -21,35 +24,59 @@ import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@ProviderFor(JavacAnnotationHandler.class)
public class HandleGetter implements JavacAnnotationHandler<Getter> {
@Override public boolean handle(AnnotationValues<Getter> annotation, JCAnnotation ast, JavacAST.Node annotationNode) {
if ( annotationNode.up().getKind() != Kind.FIELD ) {
annotationNode.addError("@Getter is only supported on a field.");
public void generateGetterForField(Node fieldNode, DiagnosticPosition pos) {
AccessLevel level = Getter.DEFAULT_ACCESS_LEVEL;
Node errorNode = fieldNode;
for ( Node child : fieldNode.down() ) {
if ( child.getKind() == Kind.ANNOTATION ) {
if ( Javac.annotationTypeMatches(Getter.class, child) ) {
level = Javac.createAnnotation(Getter.class, child).getInstance().value();
errorNode = child;
pos = child.get();
break;
}
}
}
createGetterForField(level, fieldNode, errorNode, pos);
}
@Override public boolean handle(AnnotationValues<Getter> annotation, JCAnnotation ast, Node annotationNode) {
Node fieldNode = annotationNode.up();
AccessLevel level = annotation.getInstance().value();
return createGetterForField(level, fieldNode, annotationNode, annotationNode.get());
}
private boolean createGetterForField(AccessLevel level, Node fieldNode, Node errorNode, DiagnosticPosition pos) {
if ( fieldNode.getKind() != Kind.FIELD ) {
errorNode.addError("@Getter is only supported on a field.");
return false;
}
String methodName = toGetterName((JCVariableDecl) annotationNode.up().get());
JCVariableDecl fieldDecl = (JCVariableDecl)fieldNode.get();
String methodName = toGetterName(fieldDecl);
if ( methodExists(methodName, annotationNode.up()) ) {
annotationNode.addWarning(
if ( methodExists(methodName, fieldNode) ) {
errorNode.addWarning(
String.format("Not generating %s(): A method with that name already exists", methodName));
return false;
}
Getter getter = annotation.getInstance();
JCClassDecl javacClassTree = (JCClassDecl) fieldNode.up().get();
JCClassDecl javacClassTree = (JCClassDecl) annotationNode.up().up().get();
long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC);
int access = toJavacModifier(getter.value());
JCMethodDecl getterMethod = createGetter(access, annotationNode.up(), annotationNode.getTreeMaker());
JCMethodDecl getterMethod = createGetter(access, fieldNode, fieldNode.getTreeMaker());
javacClassTree.defs = javacClassTree.defs.append(getterMethod);
return true;
}
private JCMethodDecl createGetter(int access, JavacAST.Node field, TreeMaker treeMaker) {
private JCMethodDecl createGetter(long access, Node field, TreeMaker treeMaker) {
JCVariableDecl fieldNode = (JCVariableDecl) field.get();
JCStatement returnStatement = treeMaker.Return(treeMaker.Ident(fieldNode.getName()));
+39 -13
View File
@@ -1,15 +1,18 @@
package lombok.javac.handlers;
import static lombok.javac.handlers.PKG.*;
import lombok.AccessLevel;
import lombok.Setter;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
import lombok.javac.Javac;
import lombok.javac.JavacAST;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacAST.Node;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
@@ -23,37 +26,60 @@ import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@ProviderFor(JavacAnnotationHandler.class)
public class HandleSetter implements JavacAnnotationHandler<Setter> {
public void generateSetterForField(Node fieldNode, DiagnosticPosition pos) {
AccessLevel level = Setter.DEFAULT_ACCESS_LEVEL;
Node errorNode = fieldNode;
for ( Node child : fieldNode.down() ) {
if ( child.getKind() == Kind.ANNOTATION ) {
if ( Javac.annotationTypeMatches(Setter.class, child) ) {
level = Javac.createAnnotation(Setter.class, child).getInstance().value();
errorNode = child;
pos = child.get();
break;
}
}
}
createSetterForField(level, fieldNode, errorNode, pos);
}
@Override public boolean handle(AnnotationValues<Setter> annotation, JCAnnotation ast, Node annotationNode) {
if ( annotationNode.up().getKind() != Kind.FIELD ) {
annotationNode.addError("@Setter is only supported on a field.");
Node fieldNode = annotationNode.up();
AccessLevel level = annotation.getInstance().value();
return createSetterForField(level, fieldNode, annotationNode, annotationNode.get());
}
private boolean createSetterForField(AccessLevel level, Node fieldNode, Node errorNode, DiagnosticPosition pos) {
if ( fieldNode.getKind() != Kind.FIELD ) {
fieldNode.addError("@Setter is only supported on a field.");
return false;
}
JCVariableDecl fieldNode = (JCVariableDecl) annotationNode.up().get();
String methodName = toSetterName(fieldNode);
JCVariableDecl fieldDecl = (JCVariableDecl)fieldNode.get();
String methodName = toSetterName(fieldDecl);
if ( methodExists(methodName, annotationNode.up()) ) {
annotationNode.addWarning(
if ( methodExists(methodName, fieldNode) ) {
errorNode.addWarning(
String.format("Not generating %s(%s %s): A method with that name already exists",
methodName, fieldNode.vartype, fieldNode.name));
methodName, fieldDecl.vartype, fieldDecl.name));
return false;
}
Setter setter = annotation.getInstance();
JCClassDecl javacClassTree = (JCClassDecl) fieldNode.up().get();
JCClassDecl javacClassTree = (JCClassDecl) annotationNode.up().up().get();
long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC);
int access = toJavacModifier(setter.value());
JCMethodDecl setterMethod = createSetter(access, annotationNode.up(), annotationNode.getTreeMaker());
JCMethodDecl setterMethod = createSetter(access, fieldNode, fieldNode.getTreeMaker());
javacClassTree.defs = javacClassTree.defs.append(setterMethod);
return true;
}
private JCMethodDecl createSetter(int access, JavacAST.Node field, TreeMaker treeMaker) {
private JCMethodDecl createSetter(long access, JavacAST.Node field, TreeMaker treeMaker) {
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
JCFieldAccess thisX = treeMaker.Select(treeMaker.Ident(field.toName("this")), fieldDecl.name);