allow property edit validation

This commit is contained in:
bancika
2011-01-30 17:21:42 +00:00
parent a8cc661d09
commit bc154608f4
14 changed files with 153 additions and 18 deletions
@@ -3,6 +3,8 @@ package org.diylc.common;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.diylc.core.IPropertyValidator;
/** /**
* Entity class for editable properties extracted from component objects. * Entity class for editable properties extracted from component objects.
* Represents a single editable property together with it's current value. * Represents a single editable property together with it's current value.
@@ -17,19 +19,21 @@ public class PropertyWrapper implements Cloneable {
private Method setter; private Method setter;
private Method getter; private Method getter;
private boolean defaultable; private boolean defaultable;
private IPropertyValidator validator;
public PropertyWrapper(String name, Class<?> type, Method getter, Method setter, public PropertyWrapper(String name, Class<?> type, Method getter, Method setter,
boolean defaultable) { boolean defaultable, IPropertyValidator validator) {
super(); super();
this.name = name; this.name = name;
this.type = type; this.type = type;
this.getter = getter; this.getter = getter;
this.setter = setter; this.setter = setter;
this.defaultable = defaultable; this.defaultable = defaultable;
this.validator = validator;
} }
public void readFrom(Object object) throws IllegalArgumentException, public void readFrom(Object object) throws IllegalArgumentException, IllegalAccessException,
IllegalAccessException, InvocationTargetException { InvocationTargetException {
this.value = getter.invoke(object); this.value = getter.invoke(object);
} }
@@ -42,8 +46,8 @@ public class PropertyWrapper implements Cloneable {
// } // }
// } // }
public void writeTo(Object object) throws IllegalArgumentException, public void writeTo(Object object) throws IllegalArgumentException, IllegalAccessException,
IllegalAccessException, InvocationTargetException { InvocationTargetException {
setter.invoke(object, value); setter.invoke(object, value);
} }
@@ -67,8 +71,8 @@ public class PropertyWrapper implements Cloneable {
return defaultable; return defaultable;
} }
public void setDefaultable(boolean defaultable) { public IPropertyValidator getValidator() {
this.defaultable = defaultable; return validator;
} }
// @Override // @Override
@@ -0,0 +1,13 @@
package org.diylc.core;
/**
* Validator that allows all values.
*
* @author Branislav Stojkovic
*/
public class AllowAllValidator implements IPropertyValidator {
@Override
public void validate(Object value) throws ValidationException {
}
}
@@ -0,0 +1,16 @@
package org.diylc.core;
/**
* Interface for validating property value changes.
*
* @author Branislav Stojkovic
*/
public interface IPropertyValidator {
/**
* @param value
* @throws ValidationException
* if validation fails. Message will contain the reason.
*/
void validate(Object value) throws ValidationException;
}
@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import org.diylc.core.annotations.EditableProperty; import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.annotations.PositiveMeasureValidator;
import org.diylc.core.measures.Size; import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit; import org.diylc.core.measures.SizeUnit;
@@ -92,7 +93,7 @@ public class Project implements Serializable {
this.height = height; this.height = height;
} }
@EditableProperty(name = "Grid spacing") @EditableProperty(name = "Grid spacing", validatorClass = SpacingValidator.class)
public Size getGridSpacing() { public Size getGridSpacing() {
return gridSpacing; return gridSpacing;
} }
@@ -183,6 +184,21 @@ public class Project implements Serializable {
return title; return title;
} }
public static class SpacingValidator extends PositiveMeasureValidator {
@Override
public void validate(Object value) throws ValidationException {
super.validate(value);
Size size = (Size) value;
if (size.compareTo(new Size(1d, SizeUnit.mm)) < 0) {
throw new ValidationException("must be greater than 1mm");
}
if (size.compareTo(new Size(1d, SizeUnit.in)) > 0) {
throw new ValidationException("must be less than 1in");
}
}
}
// @Override // @Override
// public Project clone() throws CloneNotSupportedException { // public Project clone() throws CloneNotSupportedException {
// List<IDIYComponent> clonedComponents = new // List<IDIYComponent> clonedComponents = new
@@ -0,0 +1,11 @@
package org.diylc.core;
public class ValidationException extends Exception {
private static final long serialVersionUID = 1L;
public ValidationException(String message) {
super(message);
}
}
@@ -3,6 +3,9 @@ package org.diylc.core.annotations;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import org.diylc.core.AllowAllValidator;
import org.diylc.core.IPropertyValidator;
/** /**
* Used to annotate editable component property. Editable property XYZ must have * Used to annotate editable component property. Editable property XYZ must have
* both getter and setter named <code>getXYZ</code> and <code>setXYZ</code> * both getter and setter named <code>getXYZ</code> and <code>setXYZ</code>
@@ -30,4 +33,10 @@ public @interface EditableProperty {
* @return * @return
*/ */
boolean defaultable() default true; boolean defaultable() default true;
/**
* Class of validator to use to
* @return
*/
Class<? extends IPropertyValidator> validatorClass() default AllowAllValidator.class;
} }
@@ -0,0 +1,20 @@
package org.diylc.core.annotations;
import org.diylc.core.IPropertyValidator;
import org.diylc.core.ValidationException;
import org.diylc.core.measures.AbstractMeasure;
public class PositiveMeasureValidator implements IPropertyValidator {
@Override
public void validate(Object value) throws ValidationException {
if (value instanceof AbstractMeasure) {
AbstractMeasure measure = (AbstractMeasure) value;
if (measure.getValue() <= 0) {
throw new ValidationException("must be greater than zero.");
}
} else {
throw new ValidationException("wrong data type, measure expected.");
}
}
}
@@ -2,7 +2,7 @@ package org.diylc.core.measures;
import org.diylc.utils.Constants; import org.diylc.utils.Constants;
public class Size extends AbstractMeasure<SizeUnit> { public class Size extends AbstractMeasure<SizeUnit> implements Comparable<Size> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -39,4 +39,9 @@ public class Size extends AbstractMeasure<SizeUnit> {
} }
throw new IllegalArgumentException("Could not parse size: " + value); throw new IllegalArgumentException("Could not parse size: " + value);
} }
@Override
public int compareTo(Size o) {
return new Double(value * unit.getFactor()).compareTo(o.getValue() * o.getUnit().getFactor());
}
} }
@@ -19,6 +19,7 @@ import org.diylc.common.ComponentType;
import org.diylc.common.PropertyWrapper; import org.diylc.common.PropertyWrapper;
import org.diylc.core.CreationMethod; import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.IPropertyValidator;
import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty; import org.diylc.core.annotations.EditableProperty;
@@ -35,7 +36,8 @@ public class ComponentProcessor {
private static ComponentProcessor instance; private static ComponentProcessor instance;
private Map<Class<?>, List<PropertyWrapper>> propertyCache; private Map<String, List<PropertyWrapper>> propertyCache;
private Map<String, IPropertyValidator> propertyValidatorCache;
private Map<String, ComponentType> componentTypeMap; private Map<String, ComponentType> componentTypeMap;
private Cloner cloner; private Cloner cloner;
@@ -50,8 +52,9 @@ public class ComponentProcessor {
private ComponentProcessor() { private ComponentProcessor() {
super(); super();
this.cloner = new Cloner(); this.cloner = new Cloner();
this.propertyCache = new HashMap<Class<?>, List<PropertyWrapper>>(); this.propertyCache = new HashMap<String, List<PropertyWrapper>>();
this.componentTypeMap = new HashMap<String, ComponentType>(); this.componentTypeMap = new HashMap<String, ComponentType>();
this.propertyValidatorCache = new HashMap<String, IPropertyValidator>();
} }
public ComponentType extractComponentTypeFrom(Class<? extends IDIYComponent<?>> clazz) { public ComponentType extractComponentTypeFrom(Class<? extends IDIYComponent<?>> clazz) {
@@ -117,8 +120,8 @@ public class ComponentProcessor {
* @return * @return
*/ */
public List<PropertyWrapper> extractProperties(Class<?> clazz) { public List<PropertyWrapper> extractProperties(Class<?> clazz) {
if (propertyCache.containsKey(clazz)) { if (propertyCache.containsKey(clazz.getName())) {
return cloner.deepClone(propertyCache.get(clazz)); return cloner.deepClone(propertyCache.get(clazz.getName()));
} }
List<PropertyWrapper> properties = new ArrayList<PropertyWrapper>(); List<PropertyWrapper> properties = new ArrayList<PropertyWrapper>();
for (Method method : clazz.getMethods()) { for (Method method : clazz.getMethods()) {
@@ -133,10 +136,13 @@ public class ComponentProcessor {
} else { } else {
name = annotation.name(); name = annotation.name();
} }
IPropertyValidator validator = getPropertyValidator(annotation
.validatorClass());
Method setter = clazz.getMethod("set" + method.getName().substring(3), Method setter = clazz.getMethod("set" + method.getName().substring(3),
method.getReturnType()); method.getReturnType());
PropertyWrapper property = new PropertyWrapper(name, PropertyWrapper property = new PropertyWrapper(name,
method.getReturnType(), method, setter, annotation.defaultable()); method.getReturnType(), method, setter, annotation.defaultable(),
validator);
properties.add(property); properties.add(property);
} }
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
@@ -146,7 +152,7 @@ public class ComponentProcessor {
} }
} }
propertyCache.put(clazz, properties); propertyCache.put(clazz.getName(), properties);
return cloner.deepClone(properties); return cloner.deepClone(properties);
} }
@@ -187,4 +193,19 @@ public class ComponentProcessor {
Collections.sort(properties, ComparatorFactory.getInstance().getPropertyNameComparator()); Collections.sort(properties, ComparatorFactory.getInstance().getPropertyNameComparator());
return properties; return properties;
} }
private IPropertyValidator getPropertyValidator(Class<? extends IPropertyValidator> clazz) {
if (propertyValidatorCache.containsKey(clazz.getName())) {
return propertyValidatorCache.get(clazz.getName());
}
IPropertyValidator validator;
try {
validator = clazz.newInstance();
} catch (Exception e) {
LOG.error("Could not instantiate validator for " + clazz.getName(), e);
return null;
}
propertyValidatorCache.put(clazz.getName(), validator);
return validator;
}
} }
@@ -18,10 +18,12 @@ import javax.swing.JCheckBox;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.diylc.common.PropertyWrapper; import org.diylc.common.PropertyWrapper;
import org.diylc.core.ValidationException;
import com.diyfever.gui.ButtonDialog; import com.diyfever.gui.ButtonDialog;
@@ -65,6 +67,23 @@ public class PropertyEditorDialog extends ButtonDialog {
// setLocationRelativeTo(owner); // setLocationRelativeTo(owner);
} }
@Override
protected boolean validateInput(String button) {
if (button.equals(ButtonDialog.OK)) {
for (PropertyWrapper property : properties) {
try {
property.getValidator().validate(property.getValue());
} catch (ValidationException ve) {
JOptionPane.showMessageDialog(PropertyEditorDialog.this, "Input error for \""
+ property.getName() + "\": " + ve.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
return true;
}
private JPanel createEditorFields() { private JPanel createEditorFields() {
JPanel editorPanel = new JPanel(new GridBagLayout()); JPanel editorPanel = new JPanel(new GridBagLayout());
editorPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); editorPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
@@ -12,6 +12,7 @@ import org.diylc.core.CreationMethod;
import org.diylc.core.IDIYComponent; import org.diylc.core.IDIYComponent;
import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty; import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.annotations.PositiveMeasureValidator;
import org.diylc.core.measures.Resistance; import org.diylc.core.measures.Resistance;
import org.diylc.core.measures.ResistanceUnit; import org.diylc.core.measures.ResistanceUnit;
import org.diylc.core.measures.Size; import org.diylc.core.measures.Size;
@@ -36,7 +37,7 @@ public class Resistor extends AbstractLeadedComponent<Resistance> {
this.borderColor = BORDER_COLOR; this.borderColor = BORDER_COLOR;
} }
@EditableProperty @EditableProperty(validatorClass = PositiveMeasureValidator.class)
public Resistance getValue() { public Resistance getValue() {
return value; return value;
} }