Skip to content

Commit

Permalink
Bound properties
Browse files Browse the repository at this point in the history
Add basic support for bound properties
Implementation only handles non-final properties
See #78
  • Loading branch information
jodastephen committed May 17, 2015
1 parent e4842e0 commit ee302dc
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

<!-- types are add, fix, remove, update -->
<release version="1.6" date="SNAPSHOT" description="v1.6">
<action dev="jodastephen" type="add">
Add basic support for bound properties.
Implementation only handles non-final properties on mutable beans.
Fixes #78.
</action>
<action dev="jodastephen" type="add">
Support package, protected and public scoped immutable constructors.
Fixes #107.
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/joda/beans/PropertyDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
* <li>'set' - generates setXxx()
* <li>'setClearAddAll' - generates setXxx() using field.clear() and field.addAll(newData)
* <li>'setClearPutAll' - generates setXxx() using field.clear() and field.putAll(newData)
* <li>'bound' - generates a bound property with {@code PropertyChangeSupport}
* <li>'field' - generates direct access to the field, enabling a weird manual setter
* <li>'manual' - a method named setXxx() must be manually provided at package scope or greater
* <li>a pattern, see below
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/joda/beans/gen/BeanData.java
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,20 @@ public void setCacheHashCode(boolean cacheHashCode) {
this.cacheHashCode = cacheHashCode;
}

//-----------------------------------------------------------------------
/**
* Gets whether property change support is needed.
* @return the flag
*/
public boolean isPropertyChangeSupport() {
for (PropertyData prop : properties) {
if (prop.isBound()) {
return true;
}
}
return false;
}

//-----------------------------------------------------------------------
/**
* Gets whether the bean is immutable.
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/joda/beans/gen/BeanGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.joda.beans.gen;

import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
Expand Down Expand Up @@ -129,6 +130,7 @@ void process() {
insertRegion.add("\t///CLOVER:OFF");
generateMeta();
generateSerializationVersionId();
generatePropertyChangeSupportField();
generateHashCodeField();
generateImmutableBuilderMethod();
generateArgBasedConstructor();
Expand Down Expand Up @@ -401,6 +403,17 @@ private void generateSerializationVersionId() {
}
}

private void generatePropertyChangeSupportField() {
if (data.isPropertyChangeSupport()) {
data.ensureImport(PropertyChangeSupport.class);
insertRegion.add("\t/**");
insertRegion.add("\t * The property change support field.");
insertRegion.add("\t */");
insertRegion.add("\tprivate PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);");
insertRegion.add("");
}
}

private void generateHashCodeField() {
if (data.isCacheHashCode()) {
insertRegion.add("\t/**");
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/org/joda/beans/gen/PropertyData.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class PropertyData {
private GetterGen getterGen;
/** The setter generator. */
private SetterGen setterGen;
/** The flag for bound properties. */
private boolean bound;
/** The copy generator. */
private CopyGen copyGen;
/** The builder generator. */
Expand Down Expand Up @@ -632,6 +634,13 @@ public void resolveSetterGen(File file, int lineIndex) {
setterGen = new SetterGen.PatternSetterGen("$field.clear();\n$field.addAll($value);");
} else if (style.equals("setClearPutAll")) {
setterGen = new SetterGen.PatternSetterGen("$field.clear();\n$field.putAll($value);");
} else if (style.equals("bound")) {
if (isFinal()) {
throw new IllegalArgumentException("Final field must not have a bound setter");
} else {
setterGen = SetterGen.ObservableSetterGen.PUBLIC;
bound = true;
}
} else if (style.equals("smart")) {
if (isDerived()) {
setterGen = SetterGen.NoSetterGen.INSTANCE;
Expand Down Expand Up @@ -683,6 +692,14 @@ public String getSetterScope() {
return "public";
}

/**
* Gets whether the property is bound.
* @return true if bound
*/
public boolean isBound() {
return bound;
}

//-----------------------------------------------------------------------
/**
* Resolves the copy generator.
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/org/joda/beans/gen/SetterGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,41 @@ String generateSetInvoke(PropertyData prop, String newValue) {
}
}

static class ObservableSetterGen extends SetterGen {
static final SetterGen PUBLIC = new ObservableSetterGen();
@Override
boolean isSetterGenerated(PropertyData prop) {
return true;
}
@Override
List<String> generateSetter(String indent, PropertyData prop) {
List<String> list = new ArrayList<String>();
list.add("\t/**");
list.add("\t * Sets " + prop.getFirstComment());
for (String comment : prop.getComments()) {
list.add("\t * " + comment);
}
list.add("\t * @param " + prop.getPropertyName() + " the new value of the property" + prop.getNotNullJavadoc());
list.add("\t */");
if (prop.isOverrideSet()) {
list.add("\t@Override");
}
if (prop.isDeprecated()) {
list.add("\t@Deprecated");
}
list.add("\tpublic void set" + prop.getUpperName() + "(" + prop.getType() + " " + prop.getPropertyName() + ") {");
if (prop.isValidated()) {
list.add("\t\t" + prop.getValidationMethodName() + "(" + prop.getPropertyName() + ", \"" + prop.getPropertyName() + "\");");
}
String old = "old" + prop.getUpperName();
list.add("\t\t" + prop.getFieldType() + " " + old + " = this." + prop.getFieldName() + ";");
list.add("\t\tthis." + prop.getFieldName() + " = " + prop.getPropertyName() + ";");
list.add("\t\tthis.propertyChangeSupport.firePropertyChange(\"" +
prop.getPropertyName() + "\", " + old + ", " + prop.getPropertyName() + ");");
list.add("\t}");
list.add("");
return list;
}
}

}
5 changes: 5 additions & 0 deletions src/site/markdown/userguide-codegen.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ In most cases, the default "smart" setting is sufficient:
* "set" - generates `setXxx()`
* "setClearAddAll" - generates `setXxx()` using `field.clear()` and `field.addAll(newData)`
* "setClearPutAll" - generates `setXxx()` using `field.clear()` and `field.putAll(newData)`
* "bound" - generates a bound property with {@code PropertyChangeSupport}
* "field" - generates direct access to the field, enabling a weird manual setter
* "manual" - a method named `setXxx()` must be manually provided at package scope or greater
* a pattern, see [Javadoc](apidocs/org/joda/beans/PropertyDefinition.html#set--)
Expand All @@ -172,6 +173,10 @@ For example, to have a private setter:
}
```

Note that support for bound properties is very basic. It only handles simple mutable properties.
For more complex cases, it is necessary to write the setter manually.
The PropertyChangeSupport field is generated, but not the methods to add listeners.

Validation can be specified using the annotation parameter "validate":

* "" - do not generate any form of validation
Expand Down
10 changes: 9 additions & 1 deletion src/test/java/org/joda/beans/gen/CompanyAddress.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.joda.beans.gen;

import java.beans.PropertyChangeSupport;
import java.util.Map;

import org.joda.beans.Bean;
Expand All @@ -37,7 +38,7 @@
public class CompanyAddress extends Address {

/** The company name. */
@PropertyDefinition
@PropertyDefinition(set = "bound")
private String companyName;

//------------------------- AUTOGENERATED START -------------------------
Expand All @@ -54,6 +55,11 @@ public static CompanyAddress.Meta meta() {
JodaBeanUtils.registerMetaBean(CompanyAddress.Meta.INSTANCE);
}

/**
* The property change support field.
*/
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

@Override
public CompanyAddress.Meta metaBean() {
return CompanyAddress.Meta.INSTANCE;
Expand All @@ -73,7 +79,9 @@ public String getCompanyName() {
* @param companyName the new value of the property
*/
public void setCompanyName(String companyName) {
String oldCompanyName = this.companyName;
this.companyName = companyName;
this.propertyChangeSupport.firePropertyChange("companyName", oldCompanyName, companyName);
}

/**
Expand Down

0 comments on commit ee302dc

Please sign in to comment.