diff options
-rw-r--r-- | src/lombok/Data.java | 13 | ||||
-rw-r--r-- | src/lombok/Getter.java | 4 | ||||
-rw-r--r-- | src/lombok/Setter.java | 4 | ||||
-rw-r--r-- | src/lombok/eclipse/Eclipse.java | 128 | ||||
-rw-r--r-- | src/lombok/eclipse/HandlerLibrary.java | 90 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleData.java | 49 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleGetter.java | 50 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleSetter.java | 43 | ||||
-rw-r--r-- | src/lombok/javac/HandlerLibrary.java | 64 | ||||
-rw-r--r-- | src/lombok/javac/Javac.java | 102 | ||||
-rw-r--r-- | src/lombok/javac/handlers/HandleData.java | 49 | ||||
-rw-r--r-- | src/lombok/javac/handlers/HandleGetter.java | 53 | ||||
-rw-r--r-- | src/lombok/javac/handlers/HandleSetter.java | 52 |
13 files changed, 506 insertions, 195 deletions
diff --git a/src/lombok/Data.java b/src/lombok/Data.java new file mode 100644 index 00000000..e985a28a --- /dev/null +++ b/src/lombok/Data.java @@ -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; +} diff --git a/src/lombok/Getter.java b/src/lombok/Getter.java index a714ead4..7510bf24 100644 --- a/src/lombok/Getter.java +++ b/src/lombok/Getter.java @@ -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; } diff --git a/src/lombok/Setter.java b/src/lombok/Setter.java index fc6a311b..acd1d180 100644 --- a/src/lombok/Setter.java +++ b/src/lombok/Setter.java @@ -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; } diff --git a/src/lombok/eclipse/Eclipse.java b/src/lombok/eclipse/Eclipse.java index 961b9536..baac26a9 100644 --- a/src/lombok/eclipse/Eclipse.java +++ b/src/lombok/eclipse/Eclipse.java @@ -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; + } } diff --git a/src/lombok/eclipse/HandlerLibrary.java b/src/lombok/eclipse/HandlerLibrary.java index 86efd53e..39864e8e 100644 --- a/src/lombok/eclipse/HandlerLibrary.java +++ b/src/lombok/eclipse/HandlerLibrary.java @@ -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(); diff --git a/src/lombok/eclipse/handlers/HandleData.java b/src/lombok/eclipse/handlers/HandleData.java new file mode 100644 index 00000000..64540e95 --- /dev/null +++ b/src/lombok/eclipse/handlers/HandleData.java @@ -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; + } +} diff --git a/src/lombok/eclipse/handlers/HandleGetter.java b/src/lombok/eclipse/handlers/HandleGetter.java index ca561a48..798705da 100644 --- a/src/lombok/eclipse/handlers/HandleGetter.java +++ b/src/lombok/eclipse/handlers/HandleGetter.java @@ -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; } diff --git a/src/lombok/eclipse/handlers/HandleSetter.java b/src/lombok/eclipse/handlers/HandleSetter.java index 692061e4..d0d0d902 100644 --- a/src/lombok/eclipse/handlers/HandleSetter.java +++ b/src/lombok/eclipse/handlers/HandleSetter.java @@ -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; diff --git a/src/lombok/javac/HandlerLibrary.java b/src/lombok/javac/HandlerLibrary.java index 05aebf20..f9404668 100644 --- a/src/lombok/javac/HandlerLibrary.java +++ b/src/lombok/javac/HandlerLibrary.java @@ -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); } } diff --git a/src/lombok/javac/Javac.java b/src/lombok/javac/Javac.java new file mode 100644 index 00000000..801e7a23 --- /dev/null +++ b/src/lombok/javac/Javac.java @@ -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; + } +} diff --git a/src/lombok/javac/handlers/HandleData.java b/src/lombok/javac/handlers/HandleData.java new file mode 100644 index 00000000..48eb7f76 --- /dev/null +++ b/src/lombok/javac/handlers/HandleData.java @@ -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; + } +} diff --git a/src/lombok/javac/handlers/HandleGetter.java b/src/lombok/javac/handlers/HandleGetter.java index 5cc2c108..651bc018 100644 --- a/src/lombok/javac/handlers/HandleGetter.java +++ b/src/lombok/javac/handlers/HandleGetter.java @@ -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) annotationNode.up().up().get(); + JCClassDecl javacClassTree = (JCClassDecl) fieldNode.up().get(); - int access = toJavacModifier(getter.value()); + long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC); - 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())); diff --git a/src/lombok/javac/handlers/HandleSetter.java b/src/lombok/javac/handlers/HandleSetter.java index da4e9ff0..f1e73489 100644 --- a/src/lombok/javac/handlers/HandleSetter.java +++ b/src/lombok/javac/handlers/HandleSetter.java @@ -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) annotationNode.up().up().get(); + JCClassDecl javacClassTree = (JCClassDecl) fieldNode.up().get(); - int access = toJavacModifier(setter.value()); + long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC); - 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); |