use of lombok.eclipse.EclipseNode in project lombok by rzwitserloot.
the class HandleData method handle.
@Override
public void handle(AnnotationValues<Data> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data");
Data ann = annotation.getInstance();
EclipseNode 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;
}
//Careful: Generate the public static constructor (if there is one) LAST, so that any attempt to
//'find callers' on the annotation node will find callers of the constructor, which is by far the
//most useful of the many methods built by @Data. This trick won't work for the non-static constructor,
//for whatever reason, though you can find callers of that one by focusing on the class name itself
//and hitting 'find callers'.
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES, Collections.<Annotation>emptyList(), annotationNode);
}
use of lombok.eclipse.EclipseNode in project lombok by rzwitserloot.
the class HandleEqualsAndHashCode method createTypeReference.
/**
* @param type Type to 'copy' into a typeref
* @param p position
* @param addWildcards If false, all generics are cut off. If true, replaces all genericparams with a ?.
* @return
*/
public TypeReference createTypeReference(EclipseNode type, long p, ASTNode source, boolean addWildcards) {
int pS = source.sourceStart;
int pE = source.sourceEnd;
List<String> list = new ArrayList<String>();
list.add(type.getName());
EclipseNode tNode = type.up();
while (tNode != null && tNode.getKind() == Kind.TYPE) {
list.add(tNode.getName());
tNode = tNode.up();
}
Collections.reverse(list);
TypeDeclaration typeDecl = (TypeDeclaration) type.get();
int typeParamCount = typeDecl.typeParameters == null ? 0 : typeDecl.typeParameters.length;
if (typeParamCount == 0)
addWildcards = false;
TypeReference[] typeArgs = null;
if (addWildcards) {
typeArgs = new TypeReference[typeParamCount];
for (int i = 0; i < typeParamCount; i++) {
typeArgs[i] = new Wildcard(Wildcard.UNBOUND);
typeArgs[i].sourceStart = pS;
typeArgs[i].sourceEnd = pE;
setGeneratedBy(typeArgs[i], source);
}
}
if (list.size() == 1) {
if (addWildcards) {
return new ParameterizedSingleTypeReference(list.get(0).toCharArray(), typeArgs, 0, p);
} else {
return new SingleTypeReference(list.get(0).toCharArray(), p);
}
}
long[] ps = new long[list.size()];
char[][] tokens = new char[list.size()][];
for (int i = 0; i < list.size(); i++) {
ps[i] = p;
tokens[i] = list.get(i).toCharArray();
}
if (addWildcards) {
TypeReference[][] typeArgs2 = new TypeReference[tokens.length][];
typeArgs2[typeArgs2.length - 1] = typeArgs;
return new ParameterizedQualifiedTypeReference(tokens, typeArgs2, 0, ps);
} else {
return new QualifiedTypeReference(tokens, ps);
}
}
use of lombok.eclipse.EclipseNode in project lombok by rzwitserloot.
the class HandleEqualsAndHashCode method generateMethods.
public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
assert excludes == null || includes == null;
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) {
errorNode.addError("@EqualsAndHashCode is only supported on a class.");
return;
}
boolean implicitCallSuper = callSuper == null;
if (callSuper == null) {
try {
callSuper = ((Boolean) EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {
throw new InternalError("Lombok bug - this cannot happen - can't find callSuper field in EqualsAndHashCode annotation.");
}
}
boolean isDirectDescendantOfObject = true;
if (typeDecl.superclass != null) {
String p = typeDecl.superclass.toString();
isDirectDescendantOfObject = p.equals("Object") || p.equals("java.lang.Object");
}
if (isDirectDescendantOfObject && callSuper) {
errorNode.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless.");
return;
}
if (implicitCallSuper && !isDirectDescendantOfObject) {
CallSuperType cst = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_CALL_SUPER);
if (cst == null)
cst = CallSuperType.WARN;
switch(cst) {
default:
case WARN:
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.");
callSuper = false;
break;
case SKIP:
callSuper = false;
break;
case CALL:
callSuper = true;
break;
}
}
List<EclipseNode> nodesForEquality = new ArrayList<EclipseNode>();
if (includes != null) {
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD)
continue;
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
if (includes.contains(new String(fieldDecl.name)))
nodesForEquality.add(child);
}
} else {
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD)
continue;
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
if (!filterField(fieldDecl))
continue;
//Skip transient fields.
if ((fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0)
continue;
//Skip excluded fields.
if (excludes != null && excludes.contains(new String(fieldDecl.name)))
continue;
nodesForEquality.add(child);
}
}
boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
MemberExistsResult canEqualExists = methodExists("canEqual", typeNode, 1);
switch(Collections.max(Arrays.asList(equalsExists, hashCodeExists))) {
case EXISTS_BY_LOMBOK:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
String msg = "Not generating equals and hashCode: A method with one of those names already exists. (Either both or none of these methods will be generated).";
errorNode.addWarning(msg);
} else if (equalsExists == MemberExistsResult.NOT_EXISTS || hashCodeExists == MemberExistsResult.NOT_EXISTS) {
// This means equals OR hashCode exists and not both.
// Even though we should suppress the message about not generating these, this is such a weird and surprising situation we should ALWAYS generate a warning.
// The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 2 methods are
// all inter-related and should be written by the same entity.
String msg = String.format("Not generating %s: One of equals or hashCode exists. " + "You should either write both of these or none of these (in the latter case, lombok generates them).", equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
errorNode.addWarning(msg);
}
return;
case NOT_EXISTS:
default:
}
MethodDeclaration equalsMethod = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam);
equalsMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration) typeNode.get()).scope);
injectMethod(typeNode, equalsMethod);
if (needsCanEqual && canEqualExists == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration canEqualMethod = createCanEqual(typeNode, errorNode.get(), onParam);
canEqualMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration) typeNode.get()).scope);
injectMethod(typeNode, canEqualMethod);
}
MethodDeclaration hashCodeMethod = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess);
hashCodeMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration) typeNode.get()).scope);
injectMethod(typeNode, hashCodeMethod);
}
use of lombok.eclipse.EclipseNode in project lombok by rzwitserloot.
the class HandleEqualsAndHashCode method handle.
@Override
public void handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
EqualsAndHashCode ann = annotation.getInstance();
List<String> excludes = Arrays.asList(ann.exclude());
List<String> includes = Arrays.asList(ann.of());
EclipseNode typeNode = annotationNode.up();
List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
checkForBogusFieldNames(typeNode, annotation);
Boolean callSuper = ann.callSuper();
if (!annotation.isExplicit("callSuper"))
callSuper = null;
if (!annotation.isExplicit("exclude"))
excludes = null;
if (!annotation.isExplicit("of"))
includes = null;
if (excludes != null && includes != null) {
excludes = null;
annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
}
Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess, onParam);
}
use of lombok.eclipse.EclipseNode in project lombok by rzwitserloot.
the class HandleEqualsAndHashCode method createHashCode.
public MethodDeclaration createHashCode(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;
MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
setGeneratedBy(method, source);
method.modifiers = toEclipseModifier(AccessLevel.PUBLIC);
method.returnType = TypeReference.baseTypeReference(TypeIds.T_int, 0);
setGeneratedBy(method.returnType, source);
method.annotations = new Annotation[] { makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source) };
method.selector = "hashCode".toCharArray();
method.thrownExceptions = null;
method.typeParameters = null;
method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
method.arguments = null;
List<Statement> statements = new ArrayList<Statement>();
final boolean isEmpty = fields.isEmpty();
/* final int PRIME = X; */
{
/* Without fields, PRIME isn't used, and that would trigger a 'local variable not used' warning. */
if (!isEmpty || callSuper) {
LocalDeclaration primeDecl = new LocalDeclaration(PRIME, pS, pE);
setGeneratedBy(primeDecl, source);
primeDecl.modifiers |= Modifier.FINAL;
primeDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
primeDecl.type.sourceStart = pS;
primeDecl.type.sourceEnd = pE;
setGeneratedBy(primeDecl.type, source);
primeDecl.initialization = makeIntLiteral(String.valueOf(HandlerUtil.primeForHashcode()).toCharArray(), source);
statements.add(primeDecl);
}
}
/* int result = 1; */
{
LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE);
setGeneratedBy(resultDecl, source);
resultDecl.initialization = makeIntLiteral("1".toCharArray(), source);
resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
resultDecl.type.sourceStart = pS;
resultDecl.type.sourceEnd = pE;
setGeneratedBy(resultDecl.type, source);
statements.add(resultDecl);
}
if (callSuper) {
MessageSend callToSuper = new MessageSend();
setGeneratedBy(callToSuper, source);
callToSuper.sourceStart = pS;
callToSuper.sourceEnd = pE;
callToSuper.receiver = new SuperReference(pS, pE);
setGeneratedBy(callToSuper.receiver, source);
callToSuper.selector = "hashCode".toCharArray();
statements.add(createResultCalculation(source, callToSuper));
}
for (EclipseNode field : fields) {
TypeReference fType = getFieldType(field, fieldAccess);
char[] dollarFieldName = ("$" + field.getName()).toCharArray();
char[] token = fType.getLastToken();
Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
if (fType.dimensions() == 0 && token != null) {
if (Arrays.equals(TypeConstants.BOOLEAN, token)) {
/* booleanField ? X : Y */
IntLiteral intTrue = makeIntLiteral(String.valueOf(HandlerUtil.primeForTrue()).toCharArray(), source);
IntLiteral intFalse = makeIntLiteral(String.valueOf(HandlerUtil.primeForFalse()).toCharArray(), source);
ConditionalExpression intForBool = new ConditionalExpression(fieldAccessor, intTrue, intFalse);
setGeneratedBy(intForBool, source);
statements.add(createResultCalculation(source, intForBool));
} else if (Arrays.equals(TypeConstants.LONG, token)) {
statements.add(createLocalDeclaration(source, dollarFieldName, TypeReference.baseTypeReference(TypeIds.T_long, 0), fieldAccessor));
SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p);
setGeneratedBy(copy1, source);
SingleNameReference copy2 = new SingleNameReference(dollarFieldName, p);
setGeneratedBy(copy2, source);
statements.add(createResultCalculation(source, longToIntForHashCode(copy1, copy2, source)));
} else if (Arrays.equals(TypeConstants.FLOAT, token)) {
/* Float.floatToIntBits(fieldName) */
MessageSend floatToIntBits = new MessageSend();
floatToIntBits.sourceStart = pS;
floatToIntBits.sourceEnd = pE;
setGeneratedBy(floatToIntBits, source);
floatToIntBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_FLOAT);
floatToIntBits.selector = "floatToIntBits".toCharArray();
floatToIntBits.arguments = new Expression[] { fieldAccessor };
statements.add(createResultCalculation(source, floatToIntBits));
} else if (Arrays.equals(TypeConstants.DOUBLE, token)) {
/* longToIntForHashCode(Double.doubleToLongBits(fieldName)) */
MessageSend doubleToLongBits = new MessageSend();
doubleToLongBits.sourceStart = pS;
doubleToLongBits.sourceEnd = pE;
setGeneratedBy(doubleToLongBits, source);
doubleToLongBits.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_DOUBLE);
doubleToLongBits.selector = "doubleToLongBits".toCharArray();
doubleToLongBits.arguments = new Expression[] { fieldAccessor };
statements.add(createLocalDeclaration(source, dollarFieldName, TypeReference.baseTypeReference(TypeIds.T_long, 0), doubleToLongBits));
SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p);
setGeneratedBy(copy1, source);
SingleNameReference copy2 = new SingleNameReference(dollarFieldName, p);
setGeneratedBy(copy2, source);
statements.add(createResultCalculation(source, longToIntForHashCode(copy1, copy2, source)));
} else if (BUILT_IN_TYPES.contains(new String(token))) {
statements.add(createResultCalculation(source, fieldAccessor));
} else /* objects */
{
/* final java.lang.Object $fieldName = this.fieldName; */
/* $fieldName == null ? NULL_PRIME : $fieldName.hashCode() */
statements.add(createLocalDeclaration(source, dollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), fieldAccessor));
SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p);
setGeneratedBy(copy1, source);
SingleNameReference copy2 = new SingleNameReference(dollarFieldName, p);
setGeneratedBy(copy2, source);
MessageSend hashCodeCall = new MessageSend();
hashCodeCall.sourceStart = pS;
hashCodeCall.sourceEnd = pE;
setGeneratedBy(hashCodeCall, source);
hashCodeCall.receiver = copy1;
hashCodeCall.selector = "hashCode".toCharArray();
NullLiteral nullLiteral = new NullLiteral(pS, pE);
setGeneratedBy(nullLiteral, source);
EqualExpression objIsNull = new EqualExpression(copy2, nullLiteral, OperatorIds.EQUAL_EQUAL);
setGeneratedBy(objIsNull, source);
IntLiteral intMagic = makeIntLiteral(String.valueOf(HandlerUtil.primeForNull()).toCharArray(), source);
ConditionalExpression nullOrHashCode = new ConditionalExpression(objIsNull, intMagic, hashCodeCall);
nullOrHashCode.sourceStart = pS;
nullOrHashCode.sourceEnd = pE;
setGeneratedBy(nullOrHashCode, source);
statements.add(createResultCalculation(source, nullOrHashCode));
}
} else if (fType.dimensions() > 0 && token != null) {
/* Arrays.deepHashCode(array) //just hashCode for simple arrays */
MessageSend arraysHashCodeCall = new MessageSend();
arraysHashCodeCall.sourceStart = pS;
arraysHashCodeCall.sourceEnd = pE;
setGeneratedBy(arraysHashCodeCall, source);
arraysHashCodeCall.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray());
if (fType.dimensions() > 1 || !BUILT_IN_TYPES.contains(new String(token))) {
arraysHashCodeCall.selector = "deepHashCode".toCharArray();
} else {
arraysHashCodeCall.selector = "hashCode".toCharArray();
}
arraysHashCodeCall.arguments = new Expression[] { fieldAccessor };
statements.add(createResultCalculation(source, arraysHashCodeCall));
}
}
/* return result; */
{
SingleNameReference resultRef = new SingleNameReference(RESULT, p);
setGeneratedBy(resultRef, source);
ReturnStatement returnStatement = new ReturnStatement(resultRef, pS, pE);
setGeneratedBy(returnStatement, source);
statements.add(returnStatement);
}
method.statements = statements.toArray(new Statement[statements.size()]);
return method;
}
Aggregations