package com.towel.bind;
import java.awt.Component;
import java.awt.Container;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.text.JTextComponent;
import com.towel.bean.Formatter;
import com.towel.cfg.StringConfiguration;
import com.towel.collections.CollectionsUtil;
import com.towel.collections.filter.Filter;
import com.towel.el.FieldResolver;
import com.towel.exc.ExceptionCollecter;
/**
* A component binder for JTextComponents. The Binder look for the parameters in
* the getName method of each JTextComponent of the Container.
*
* Use the setName in the components you want. You need at least the name of the
* field you want to bind.
*
* jTextComponent.setName("[name:attrName]")
*
* If the field need a formatter you pass it as "[fmt:fmtName]" The fmtName must
* match with one formatter passed in the constructor.
*
* If you want a default value if in the object are blank you can use
* "[dflt:Text]" if the value in the object is null or empty this text will
* assume in the component
*
* Binder is typed. And the Class argument need to be the class of the binded
* objects will be.
*
* To update the view you need to call the method updateView(Object obj) and to
* update the model updateModel(Object target).
*
* If you need more than one Binder in the Container maybe you need use the
* prefix to shows to the binder the fields must be to each class. If you
* setName with "[pfx:cads]" you need pass cads to map only the cads fields.
*
* Example: jTextField.setName("[name:personName]")
* jTextField2.setName("[name:age][fmt:int]"); binder = new Binder(container,
* Person.class,new IntFormatter()); binder.updateView(personObject); //change
* the fields values binder.updateModel(personObject);
*
* jTextField.setName("[pfx:person][name:personName]")
* jTextField2.setName("[pfx:cia][name:name]");
*
* personBinder = new Binder("person",container,Person.class); ciaBinder = new
* Binder("cia",container, Cia.class);
*
* @author Marcos Vasconcelos
*/
public class NamedBinder implements Binder {
private Map<JTextComponent, FieldResolver> binds;
private Map<JTextComponent, Default> defaultValues;
public NamedBinder(Container comp, Class<?> clazz, Formatter... formatters) {
binds = new HashMap<JTextComponent, FieldResolver>();
defaultValues = new HashMap<JTextComponent, Default>();
for (Entry<JTextComponent, String> ent : mapComps(comp, null)
.entrySet()) {
String value = ent.getValue();
StringConfiguration str = new StringConfiguration(value);
final String formatter = str.getAttribute(FORMATTER);
String name = str.getAttribute(NAME);
FieldResolver resolver = new FieldResolver(clazz, name);
if (!formatter.isEmpty()) {
int fmtIdx = CollectionsUtil.firstIndexOf(formatters,
new Filter<Formatter>() {
@Override
public boolean accept(Formatter obj) {
return obj.getName().equals(formatter);
}
});
if (fmtIdx == -1)
throw new RuntimeException("Formatter name not found:"
+ formatter);
resolver.setFormatter(formatters[fmtIdx]);
}
binds.put(ent.getKey(), resolver);
if (str.hasAttribute(DEFAULT)) {
defaultValues
.put(ent.getKey(), new Default(str
.getAttribute(DEFAULT), str
.getAttribute(DEFAULT_COND)));
}
}
}
public NamedBinder(String prefix, Container comp, Class<?> clazz,
Formatter... formatters) {
binds = new HashMap<JTextComponent, FieldResolver>();
defaultValues = new HashMap<JTextComponent, Default>();
for (Entry<JTextComponent, String> ent : mapComps(comp, null)
.entrySet()) {
String value = ent.getValue();
StringConfiguration str = new StringConfiguration(value);
if (str.getAttribute(PREFIX).equals(prefix)) {
final String formatter = str.getAttribute(FORMATTER);
String name = str.getAttribute(NAME);
FieldResolver resolver = new FieldResolver(clazz, name);
if (!formatter.isEmpty()) {
int fmtIdx = CollectionsUtil.firstIndexOf(formatters,
new Filter<Formatter>() {
@Override
public boolean accept(Formatter obj) {
return obj.getName().equals(formatter);
}
});
if (fmtIdx == -1)
throw new RuntimeException("Formatter name not found:"
+ formatter);
resolver.setFormatter(formatters[fmtIdx]);
}
binds.put(ent.getKey(), resolver);
if (str.hasAttribute(DEFAULT)) {
defaultValues.put(ent.getKey(), new Default(str
.getAttribute(DEFAULT), str
.getAttribute(DEFAULT_COND)));
}
}
}
}
private Map<JTextComponent, String> mapComps(Container cont,
Map<JTextComponent, String> mapped) {
if (mapped == null)
mapped = new HashMap<JTextComponent, String>();
for (Component comp : cont.getComponents()) {
if (comp instanceof JTextComponent)
if (comp.getName() != null && comp.getName().length() != 0)
if (!comp.getName().startsWith("null"))
mapped.put((JTextComponent) comp, comp.getName());
if (comp instanceof Container)
mapComps((Container) comp, mapped);
}
return mapped;
}
public void updateView(Object obj) {
JTextComponent comp;
FieldResolver field;
ExceptionCollecter collecter = new ExceptionCollecter();
for (Entry<JTextComponent, FieldResolver> ent : binds.entrySet()) {
try {
comp = ent.getKey();
field = ent.getValue();
String fieldValue = (String) field.getValue(obj);
if (defaultValues.containsKey(comp)) {
Default defaultValue = defaultValues.get(comp);
fieldValue = defaultValue.getDefaultValue(fieldValue);
}
comp.setText(fieldValue);
} catch (Throwable e) {
collecter.collect(e);
}
}
// if (!collecter.isEmpty())
// collecter.throwException();
}
public void updateModel(Object obj) {
JTextComponent comp;
FieldResolver field;
ExceptionCollecter collecter = new ExceptionCollecter();
for (Entry<JTextComponent, FieldResolver> ent : binds.entrySet()) {
comp = ent.getKey();
field = ent.getValue();
try {
field.setValue(obj, comp.getText());
} catch (Throwable e) {
collecter.collect(e);
}
}
// if (!collecter.isEmpty())
// collecter.throwException();
}
public final static String FORMATTER = "fmt";
public final static String DEFAULT = "dflt";
public final static String DEFAULT_COND = "dflt_con";
public final static String NAME = "name";
public final static String PREFIX = "pfx";
private static class Default {
private String defaultValue = "";
private String condition = "";
private String conditionParam = "";
public Default(String value, String condition) {
defaultValue = value;
if (!condition.isEmpty()) {
this.condition = condition.substring(0, 1);
conditionParam = condition.substring(1);
}
}
public String getDefaultValue(String current) {
if (current == null || current.isEmpty())
return defaultValue;
if (condition.equals("=")) {
if (current.equals(conditionParam))
return defaultValue;
}
return current;
}
}
}