Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boundsetter v1.12.6 support #1292

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/core/lombok/core/util/Names.java
@@ -0,0 +1,27 @@
package lombok.core.util;

import static java.lang.Character.*;

public class Names {

private Names() {
};

public static String camelCaseToConstant(final String fieldName) {
if (fieldName == null || fieldName.isEmpty()) return "";
char[] chars = fieldName.toCharArray();
StringBuilder b = new StringBuilder();
b.append(toUpperCase(chars[0]));
for (int i = 1, iend = chars.length; i < iend; i++) {
char c = chars[i];
if (isUpperCase(c)) {
b.append('_');
} else {
c = toUpperCase(c);
}
b.append(c);
}
return b.toString();
}

}
22 changes: 22 additions & 0 deletions src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
Expand Up @@ -1010,6 +1010,28 @@ public static String toWitherName(EclipseNode field, boolean isBoolean) {
return HandlerUtil.toWitherName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean);
}

public static boolean shouldAddPropertyNameConstant(EclipseNode field) {
if ((((FieldDeclaration) field.get()).modifiers & ClassFileConstants.AccStatic) != 0) return false;
AnnotationValues<Accessors> accessors = EclipseHandlerUtil.getAccessorsForField(field);
Accessors instance = accessors.getInstance();
return instance.propertyNameConstant() || instance.bound();
}

public static boolean shouldAddBoundProperty(EclipseNode field) {
if ((((FieldDeclaration) field.get()).modifiers & ClassFileConstants.AccStatic) != 0) return false;

AnnotationValues<Accessors> accessors = EclipseHandlerUtil.getAccessorsForField(field);

Accessors instance = accessors.getInstance();
return instance.bound();
}

public static String propertyChangeSupportFieldName(EclipseNode field) {
AnnotationValues<Accessors> accessors = EclipseHandlerUtil.getAccessorsForField(field);
Accessors instance = accessors.getInstance();
return instance.propertyChangeSupportFieldName();
}

/**
* When generating a setter, the setter either returns void (beanspec) or Self (fluent).
* This method scans for the {@code Accessors} annotation and associated config properties to figure that out.
Expand Down
127 changes: 117 additions & 10 deletions src/core/lombok/eclipse/handlers/HandleSetter.java
Expand Up @@ -24,40 +24,50 @@
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.toAllSetterNames;
import static lombok.eclipse.handlers.EclipseHandlerUtil.toSetterName;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.Setter;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;

import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
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.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.TypeIds;
import org.mangosdk.spi.ProviderFor;

import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.Setter;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.util.Names;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult;

/**
* Handles the {@code lombok.Setter} annotation for eclipse.
*/
Expand Down Expand Up @@ -167,11 +177,19 @@ public void createSetterForField(
String setterName = toSetterName(fieldNode, isBoolean);
boolean shouldReturnThis = shouldReturnThis(fieldNode);

boolean bound = shouldAddBoundProperty(fieldNode);
String propertyChangeSupportFieldName = null;
if (bound) {
propertyChangeSupportFieldName = propertyChangeSupportFieldName(fieldNode);
}

if (setterName == null) {
fieldNode.addWarning("Not generating setter for this field: It does not fit your @Accessors prefix list.");
return;
}

createPropertyNameConstantForField(fieldNode, source);

int modifier = toEclipseModifier(level) | (field.modifiers & ClassFileConstants.AccStatic);

for (String altName : toAllSetterNames(fieldNode, isBoolean)) {
Expand All @@ -192,11 +210,15 @@ public void createSetterForField(
}
}

MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), fieldNode, setterName, shouldReturnThis, modifier, sourceNode, onMethod, onParam);
MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), fieldNode, setterName, shouldReturnThis, modifier, sourceNode, onMethod, onParam, bound, propertyChangeSupportFieldName);
injectMethod(fieldNode.up(), method);
}

static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) {
return createSetter(parent, fieldNode, name, shouldReturnThis, modifier, sourceNode, onMethod, onParam, false, null);
}

static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam, boolean bound, String propertyChangeSupportFieldName) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
ASTNode source = sourceNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
Expand Down Expand Up @@ -235,6 +257,17 @@ static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldN
Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
List<Statement> statements = new ArrayList<Statement>(5);

LocalDeclaration oldValueVarDecl = null;
if (bound) {
oldValueVarDecl = new LocalDeclaration("old".toCharArray(), 0, -1);
oldValueVarDecl.modifiers = Modifier.FINAL;
oldValueVarDecl.type = copyType(field.type, source);
oldValueVarDecl.initialization = fieldRef;
oldValueVarDecl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
statements.add(oldValueVarDecl);
}

if (nonNulls.length == 0) {
statements.add(assignment);
} else {
Expand All @@ -243,6 +276,29 @@ static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldN
statements.add(assignment);
}

if (bound) {
MessageSend firePropChangeMethodCall = new MessageSend();

FieldReference propChangeFieldRef = new FieldReference(propertyChangeSupportFieldName.toCharArray(), p);
propChangeFieldRef.receiver = new ThisReference((int) (p >> 32), (int) p);
firePropChangeMethodCall.receiver = propChangeFieldRef;
firePropChangeMethodCall.selector = "firePropertyChange".toCharArray();

Expression propNameParam = new SingleNameReference(createPropConstantName(fieldNode.getName()).toCharArray(), p);
Expression oldValueParam = new SingleNameReference(oldValueVarDecl.name, p);
Expression newValueParam = createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
;

firePropChangeMethodCall.arguments = new Expression[] {propNameParam, oldValueParam, newValueParam};
firePropChangeMethodCall.nameSourcePosition = p;
firePropChangeMethodCall.sourceStart = pS;
firePropChangeMethodCall.sourceEnd = firePropChangeMethodCall.statementEnd = pE;
firePropChangeMethodCall.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;

statements.add(firePropChangeMethodCall);
}


if (shouldReturnThis) {
ThisReference thisRef = new ThisReference(pS, pE);
ReturnStatement returnThis = new ReturnStatement(thisRef, pS, pE);
Expand All @@ -254,4 +310,55 @@ static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldN
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
return method;
}

private static void createPropertyNameConstantForField(EclipseNode fieldNode, ASTNode source) {
boolean propConstant = shouldAddPropertyNameConstant(fieldNode);
if( propConstant && MemberExistsResult.NOT_EXISTS.equals(fieldExists(createPropConstantName(fieldNode.getName()), fieldNode))) {
FieldDeclaration propConstantFieldDecl = createPropertyNameConstant(fieldNode, source);
injectField(fieldNode.up(), propConstantFieldDecl);
}
}

private static FieldDeclaration createPropertyNameConstant(EclipseNode fieldNode, ASTNode source) {
String constantValue = fieldNode.getName();
String constantName = createPropConstantName(constantValue);
FieldDeclaration propConstantFieldDecl = createStringConstant(source, constantName, constantValue);
return propConstantFieldDecl;
}

private static FieldDeclaration createStringConstant(ASTNode source, String constantName, String constantValue) {
int pS = source.sourceStart, pE = source.sourceEnd;
FieldDeclaration propConstantFieldDecl = new FieldDeclaration(constantName.toCharArray(), 0, -1);
setGeneratedBy(propConstantFieldDecl, source);
propConstantFieldDecl.declarationSourceEnd = -1;
propConstantFieldDecl.modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;
propConstantFieldDecl.type = createTypeReference("java.lang.String", source);
propConstantFieldDecl.initialization = new StringLiteral(constantValue.toCharArray(), pS, pE, 0);
propConstantFieldDecl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
return propConstantFieldDecl;
}

private static String createPropConstantName(String fieldNodeName) {
return "PROP_" + Names.camelCaseToConstant(fieldNodeName);
}

public static TypeReference createTypeReference(String typeName, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;

TypeReference typeReference;
if (typeName.contains(".")) {

char[][] typeNameTokens = fromQualifiedName(typeName);
long[] pos = new long[typeNameTokens.length];
Arrays.fill(pos, p);

typeReference = new QualifiedTypeReference(typeNameTokens, pos);
} else {
typeReference = null;
}

setGeneratedBy(typeReference, source);
return typeReference;
}
}
18 changes: 18 additions & 0 deletions src/core/lombok/experimental/Accessors.java
Expand Up @@ -57,4 +57,22 @@
* all turn into the same name when the prefix is stripped, an error will be generated.
*/
String[] prefix() default {};

/**
* If true, a propertyName constant is generated (e. g. 'public final String
* PROP_FOO = "foo";' for the property foo).
*/
boolean propertyNameConstant() default false;

/**
* If true, property change support is added to the setter implementation.
* This will also cause the generation of propertyNameConstant(s).
*/
boolean bound() default false;

/**
* field name to use for bound setters to call firePropertyChange on -
* instance type must be java.beans.PropertyChangeSupport.
*/
String propertyChangeSupportFieldName() default "propertySupport";
}