diff options
author | Reinier Zwitserloot <reinier@tipit.to> | 2009-08-27 23:16:47 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@tipit.to> | 2009-08-27 23:16:47 +0200 |
commit | 4e152f2f1485f904feb45ae614236d4aa4b6edc9 (patch) | |
tree | aa7de35a215a5d7077e27e0ef4c1699e9c56779b /src/lombok/eclipse | |
parent | 0221e460b2e648b142284c6c462d5798f33a3ff7 (diff) | |
parent | fe0da3f53f1e88b704e21463cc5fea3d998e394a (diff) | |
download | lombok-4e152f2f1485f904feb45ae614236d4aa4b6edc9.tar.gz lombok-4e152f2f1485f904feb45ae614236d4aa4b6edc9.tar.bz2 lombok-4e152f2f1485f904feb45ae614236d4aa4b6edc9.zip |
Merge branch 'nonnull'
Conflicts:
src/lombok/eclipse/handlers/HandleData.java
src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
src/lombok/eclipse/handlers/HandleSetter.java
src/lombok/javac/handlers/HandleData.java
src/lombok/javac/handlers/HandleEqualsAndHashCode.java
src/lombok/javac/handlers/HandleSetter.java
Diffstat (limited to 'src/lombok/eclipse')
-rw-r--r-- | src/lombok/eclipse/Eclipse.java | 52 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleData.java | 21 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java | 8 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleGetter.java | 15 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/HandleSetter.java | 20 | ||||
-rw-r--r-- | src/lombok/eclipse/handlers/PKG.java | 47 |
6 files changed, 145 insertions, 18 deletions
diff --git a/src/lombok/eclipse/Eclipse.java b/src/lombok/eclipse/Eclipse.java index cba2bd05..017affa1 100644 --- a/src/lombok/eclipse/Eclipse.java +++ b/src/lombok/eclipse/Eclipse.java @@ -49,11 +49,14 @@ 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.MarkerAnnotation; import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; @@ -117,7 +120,7 @@ public class Eclipse { * For 'speed' reasons, Eclipse works a lot with char arrays. I have my doubts this was a fruitful exercise, * but we need to deal with it. This turns [[java][lang][String]] into "java.lang.String". */ - static String toQualifiedName(char[][] typeName) { + public static String toQualifiedName(char[][] typeName) { StringBuilder sb = new StringBuilder(); boolean first = true; for ( char[] c : typeName ) { @@ -127,6 +130,16 @@ public class Eclipse { return sb.toString(); } + public static char[][] fromQualifiedName(String typeName) { + String[] split = typeName.split("\\."); + char[][] result = new char[split.length][]; + for (int i = 0; i < split.length; i++) { + result[i] = split[i].toCharArray(); + } + return result; + } + + /** * You can't share TypeParameter objects or bad things happen; for example, one 'T' resolves differently * from another 'T', even for the same T in a single class file. Unfortunately the TypeParameter type hierarchy @@ -241,6 +254,43 @@ public class Eclipse { return ref; } + public static Annotation[] copyAnnotations(Annotation[] annotations) { + return copyAnnotations(annotations, null); + } + + public static Annotation[] copyAnnotations(Annotation[] annotations1, Annotation[] annotations2) { + if (annotations1 == null && annotations2 == null) return null; + if (annotations1 == null) annotations1 = new Annotation[0]; + if (annotations2 == null) annotations2 = new Annotation[0]; + Annotation[] outs = new Annotation[annotations1.length + annotations2.length]; + int idx = 0; + for ( Annotation annotation : annotations1 ) { + outs[idx++] = copyAnnotation(annotation); + } + for ( Annotation annotation : annotations2 ) { + outs[idx++] = copyAnnotation(annotation); + } + return outs; + } + + public static Annotation copyAnnotation(Annotation annotation) { + if (annotation instanceof MarkerAnnotation) { + return new MarkerAnnotation(copyType(annotation.type), annotation.sourceStart); + } + + if (annotation instanceof SingleMemberAnnotation) { + SingleMemberAnnotation result = new SingleMemberAnnotation(copyType(annotation.type), annotation.sourceStart); + result.memberValue = ((SingleMemberAnnotation)annotation).memberValue; + } + + if (annotation instanceof NormalAnnotation) { + NormalAnnotation result = new NormalAnnotation(copyType(annotation.type), annotation.sourceStart); + result.memberValuePairs = ((NormalAnnotation)annotation).memberValuePairs; + } + + return annotation; + } + /** * Checks if the provided annotation type is likely to be the intended type for the given annotation node. * diff --git a/src/lombok/eclipse/handlers/HandleData.java b/src/lombok/eclipse/handlers/HandleData.java index b598313b..f072ea64 100644 --- a/src/lombok/eclipse/handlers/HandleData.java +++ b/src/lombok/eclipse/handlers/HandleData.java @@ -89,7 +89,8 @@ public class HandleData implements EclipseAnnotationHandler<Data> { //Skip static fields. if ( (fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0 ) continue; boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; - if ( isFinal && fieldDecl.initialization == null ) nodesForConstructor.add(child); + boolean isNonNull = findAnnotations(fieldDecl, NON_NULL_PATTERN).length != 0; + if ( (isFinal || isNonNull) && fieldDecl.initialization == null ) nodesForConstructor.add(child); new HandleGetter().generateGetterForField(child, annotationNode.get()); if ( !isFinal ) new HandleSetter().generateSetterForField(child, annotationNode.get()); } @@ -139,18 +140,27 @@ public class HandleData implements EclipseAnnotationHandler<Data> { List<Argument> args = new ArrayList<Argument>(); List<Statement> assigns = new ArrayList<Statement>(); + List<Statement> nullChecks = new ArrayList<Statement>(); for ( Node fieldNode : fields ) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); FieldReference thisX = new FieldReference(("this." + new String(field.name)).toCharArray(), p); thisX.receiver = new ThisReference((int)(p >> 32), (int)p); thisX.token = field.name; + assigns.add(new Assignment(thisX, new SingleNameReference(field.name, p), (int)p)); long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd; - args.add(new Argument(field.name, fieldPos, copyType(field.type), Modifier.FINAL)); + Argument argument = new Argument(field.name, fieldPos, copyType(field.type), Modifier.FINAL); + Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN); + Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN); + if (nonNulls.length != 0) nullChecks.add(generateNullCheck(field)); + Annotation[] copiedAnnotations = copyAnnotations(nonNulls, nullables); + if (copiedAnnotations.length != 0) argument.annotations = copiedAnnotations; + args.add(argument); } - constructor.statements = assigns.isEmpty() ? null : assigns.toArray(new Statement[assigns.size()]); + nullChecks.addAll(assigns); + constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]); constructor.arguments = args.isEmpty() ? null : args.toArray(new Argument[args.size()]); return constructor; } @@ -188,6 +198,11 @@ public class HandleData implements EclipseAnnotationHandler<Data> { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd; assigns.add(new SingleNameReference(field.name, fieldPos)); + + Argument argument = new Argument(field.name, fieldPos, copyType(field.type), 0); + Annotation[] copiedAnnotations = copyAnnotations( + findAnnotations(field, NON_NULL_PATTERN), findAnnotations(field, NULLABLE_PATTERN)); + if (copiedAnnotations.length != 0) argument.annotations = copiedAnnotations; args.add(new Argument(field.name, fieldPos, copyType(field.type), Modifier.FINAL)); } diff --git a/src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index b8916d46..1adbe781 100644 --- a/src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -154,19 +154,19 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA return false; } - boolean isDirectDescendentOfObject = true; + boolean isDirectDescendantOfObject = true; if ( typeDecl.superclass != null ) { String p = typeDecl.superclass.toString(); - isDirectDescendentOfObject = p.equals("Object") || p.equals("java.lang.Object"); + isDirectDescendantOfObject = p.equals("Object") || p.equals("java.lang.Object"); } - if ( isDirectDescendentOfObject && callSuper ) { + if ( isDirectDescendantOfObject && callSuper ) { errorNode.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless."); return true; } - if ( !isDirectDescendentOfObject && !callSuper && implicit ) { + if ( !isDirectDescendantOfObject && !callSuper && implicit ) { errorNode.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type."); } diff --git a/src/lombok/eclipse/handlers/HandleGetter.java b/src/lombok/eclipse/handlers/HandleGetter.java index 982ccc97..5fb7876a 100644 --- a/src/lombok/eclipse/handlers/HandleGetter.java +++ b/src/lombok/eclipse/handlers/HandleGetter.java @@ -27,7 +27,7 @@ import lombok.Getter; import lombok.core.AnnotationValues; import lombok.core.TransformationsUtil; import lombok.core.AST.Kind; -import lombok.eclipse.Eclipse; +import static lombok.eclipse.Eclipse.*; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseAST.Node; @@ -64,7 +64,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { public void generateGetterForField(Node fieldNode, ASTNode pos) { for ( Node child : fieldNode.down() ) { if ( child.getKind() == Kind.ANNOTATION ) { - if ( Eclipse.annotationTypeMatches(Getter.class, child) ) { + if ( annotationTypeMatches(Getter.class, child) ) { //The annotation will make it happen, so we can skip it. return; } @@ -87,7 +87,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { } FieldDeclaration field = (FieldDeclaration) fieldNode.get(); - TypeReference fieldType = Eclipse.copyType(field.type); + TypeReference fieldType = copyType(field.type); String fieldName = new String(field.name); boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0; String getterName = TransformationsUtil.toGetterName(fieldName, isBoolean); @@ -113,6 +113,11 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { } MethodDeclaration method = generateGetter((TypeDeclaration) fieldNode.up().get(), field, getterName, modifier, pos); + Annotation[] copiedAnnotations = copyAnnotations( + findAnnotations(field, NON_NULL_PATTERN), findAnnotations(field, NULLABLE_PATTERN)); + if (copiedAnnotations.length != 0) { + method.annotations = copiedAnnotations; + } injectMethod(fieldNode.up(), method); @@ -123,14 +128,14 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { int modifier, ASTNode pos) { MethodDeclaration method = new MethodDeclaration(parent.compilationResult); method.modifiers = modifier; - method.returnType = Eclipse.copyType(field.type); + method.returnType = copyType(field.type); method.annotations = null; method.arguments = null; method.selector = name.toCharArray(); method.binding = null; method.thrownExceptions = null; method.typeParameters = null; - method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; 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 = pos.sourceStart; diff --git a/src/lombok/eclipse/handlers/HandleSetter.java b/src/lombok/eclipse/handlers/HandleSetter.java index 1e301c29..5ad9b193 100644 --- a/src/lombok/eclipse/handlers/HandleSetter.java +++ b/src/lombok/eclipse/handlers/HandleSetter.java @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.PKG.*; import java.lang.reflect.Modifier; @@ -30,7 +31,6 @@ 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; @@ -71,7 +71,7 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { public void generateSetterForField(Node fieldNode, ASTNode pos) { for ( Node child : fieldNode.down() ) { if ( child.getKind() == Kind.ANNOTATION ) { - if ( Eclipse.annotationTypeMatches(Setter.class, child) ) { + if ( annotationTypeMatches(Setter.class, child) ) { //The annotation will make it happen, so we can skip it. return; } @@ -122,25 +122,35 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { private MethodDeclaration generateSetter(TypeDeclaration parent, FieldDeclaration field, String name, int modifier, ASTNode ast) { + long pos = (((long)ast.sourceStart) << 32) | ast.sourceEnd; MethodDeclaration method = new MethodDeclaration(parent.compilationResult); method.modifiers = modifier; method.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); method.annotations = null; - Argument param = new Argument(field.name, pos, Eclipse.copyType(field.type), Modifier.FINAL); + Argument param = new Argument(field.name, pos, copyType(field.type), Modifier.FINAL); method.arguments = new Argument[] { param }; method.selector = name.toCharArray(); method.binding = null; method.thrownExceptions = null; method.typeParameters = null; method.scope = parent.scope == null ? null : new MethodScope(parent.scope, method, false); - method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; FieldReference thisX = new FieldReference(field.name, pos); thisX.receiver = new ThisReference(ast.sourceStart, ast.sourceEnd); Assignment assignment = new Assignment(thisX, new SingleNameReference(field.name, pos), (int)pos); method.bodyStart = method.declarationSourceStart = method.sourceStart = ast.sourceStart; method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = ast.sourceEnd; - method.statements = new Statement[] { assignment }; + + Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN); + Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN); + if (nonNulls.length == 0) { + method.statements = new Statement[] { assignment }; + } else { + method.statements = new Statement[] { generateNullCheck(field), assignment }; + } + Annotation[] copiedAnnotations = copyAnnotations(nonNulls, nullables); + if (copiedAnnotations.length != 0) param.annotations = copiedAnnotations; return method; } } diff --git a/src/lombok/eclipse/handlers/PKG.java b/src/lombok/eclipse/handlers/PKG.java index 7fdf7afd..17096b70 100644 --- a/src/lombok/eclipse/handlers/PKG.java +++ b/src/lombok/eclipse/handlers/PKG.java @@ -21,7 +21,12 @@ */ package lombok.eclipse.handlers; +import static lombok.eclipse.Eclipse.fromQualifiedName; + import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; import lombok.AccessLevel; import lombok.core.AST.Kind; @@ -29,9 +34,23 @@ import lombok.eclipse.EclipseAST; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; +import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.IfStatement; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; +import org.eclipse.jdt.internal.compiler.ast.OperatorIds; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; +import org.eclipse.jdt.internal.compiler.ast.ThrowStatement; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; class PKG { private PKG() {} @@ -212,4 +231,32 @@ class PKG { type.add(method, Kind.METHOD).recursiveSetHandled(); } + + static final Pattern NON_NULL_PATTERN = Pattern.compile("^no[tn]null$", Pattern.CASE_INSENSITIVE); + static final Pattern NULLABLE_PATTERN = Pattern.compile("^nullable$", Pattern.CASE_INSENSITIVE); + + static Annotation[] findAnnotations(FieldDeclaration field, Pattern namePattern) { + List<Annotation> result = new ArrayList<Annotation>(); + for (Annotation annotation : field.annotations) { + TypeReference typeRef = annotation.type; + if ( typeRef != null && typeRef.getTypeName()!= null ) { + char[][] typeName = typeRef.getTypeName(); + String suspect = new String(typeName[typeName.length - 1]); + if ( namePattern.matcher(suspect).matches() ) { + result.add(annotation); + } + } + } + return result.toArray(new Annotation[0]); + } + + static Statement generateNullCheck(AbstractVariableDeclaration variable) { + AllocationExpression exception = new AllocationExpression(); + exception.type = new QualifiedTypeReference(fromQualifiedName("java.lang.NullPointerException"), new long[]{0, 0, 0}); + exception.arguments = new Expression[] { new StringLiteral(variable.name, 0, variable.name.length - 1, 0)}; + ThrowStatement throwStatement = new ThrowStatement(exception, 0, 0); + + return new IfStatement(new EqualExpression(new SingleNameReference(variable.name, 0), + new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL), throwStatement, 0, 0); + } } |