/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.utils.binding; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import nl.strohalm.cyclos.utils.ClassHelper; import nl.strohalm.cyclos.utils.PropertyHelper; import nl.strohalm.cyclos.utils.conversion.CoercionHelper; import org.apache.commons.lang.StringUtils; import org.json.simple.JSONObject; /** * A property binder that can convert a whole bean from / to a set of properties * @author luis */ public class BeanBinder<T> extends DataBinder<T> { private static final long serialVersionUID = 6669808609710330346L; public static <T> BeanBinder<T> instance(final Class<T> beanClass) { return instance(beanClass, null); } public static <T> BeanBinder<T> instance(final Class<T> beanClass, final String path) { final BeanBinder<T> binder = new BeanBinder<T>(); binder.setType(beanClass); binder.setPath(path); return binder; } private Map<String, DataBinder<?>> mappings = new LinkedHashMap<String, DataBinder<?>>(); public Map<String, DataBinder<?>> getMappings() { return mappings; } @Override public T read(final Object object) { if (object == null) { return null; } final T bean = ClassHelper.instantiate(getType()); readInto(bean, object, false); return bean; } /** * Returns the object as a JSON string */ @Override public String readAsString(final Object object) { if (object == null) { return "null"; } final JSONObject json = new JSONObject(); writeAsString(json, object); return json.toJSONString(); } @Override public T readFromString(final Object object) { if (object == null) { return null; } final T bean = ClassHelper.instantiate(getType()); readInto(bean, object, true); return bean; } public void readInto(final Object bean, Object object, final boolean fromString) { object = PropertyHelper.get(object, getPath()); for (final Map.Entry<String, DataBinder<?>> entry : mappings.entrySet()) { final String beanProperty = entry.getKey(); final DataBinder<?> nestedBinder = entry.getValue(); Object resolvedObject = object; if (StringUtils.isEmpty(nestedBinder.getPath())) { resolvedObject = PropertyHelper.get(object, nestedBinder.getPath()); } final Object value = fromString ? nestedBinder.readFromString(resolvedObject) : nestedBinder.read(resolvedObject); PropertyHelper.set(bean, beanProperty, CoercionHelper.coerce(nestedBinder.getType(), value)); } } public void registerBinder(final String property, final DataBinder<?> binder) { mappings.put(property, binder); } public void setMappings(final Map<String, DataBinder<?>> mappings) { this.mappings = mappings; } @Override public void write(final Object object, final T value) { if (object == null) { return; } doWrite(object, value, false); } @Override public void writeAsString(final Object object, Object value) { if (object == null) { return; } if ("".equals(value)) { value = null; } doWrite(object, value, true); } @SuppressWarnings({ "unchecked", "rawtypes" }) private void doWrite(final Object bean, final Object value, final boolean asString) { // Resolve the bean Object resolved = PropertyHelper.get(bean, getPath()); if (resolved == null) { // Was null - instantiate a new one resolved = ClassHelper.instantiate(getType()); PropertyHelper.set(bean, getPath(), resolved); } for (final Map.Entry<String, DataBinder<?>> entry : mappings.entrySet()) { final String beanProperty = entry.getKey(); final DataBinder<Object> nestedBinder = (DataBinder<Object>) entry.getValue(); Object current = null; if (asString) { if (nestedBinder instanceof PropertyBinder) { current = nestedBinder.readAsString(value); } else { final Object nestedValue = PropertyHelper.get(value, beanProperty); Object nestedBean = PropertyHelper.get(resolved, beanProperty); if (nestedBean == null) { nestedBean = nestedBinder instanceof BeanBinder ? new HashMap() : ClassHelper.instantiate(nestedBinder.getType()); } if (nestedValue != null) { nestedBinder.writeAsString(resolved, nestedValue); } current = nestedBean; } } else { current = nestedBinder.read(value); } PropertyHelper.set(resolved, beanProperty, current); } } }