/*
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.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import nl.strohalm.cyclos.utils.ClassHelper;
import nl.strohalm.cyclos.utils.PropertyHelper;
import org.apache.commons.collections.IteratorUtils;
/**
* A binder for collections of beans
* @author luis
*/
public class BeanCollectionBinder<T> extends DataBinder<Collection<T>> {
private static final long serialVersionUID = -2345152330629764092L;
public static <T> BeanCollectionBinder<T> instance(final BeanBinder<T> elementBinder) {
return instance(elementBinder, null, null);
}
public static <T> BeanCollectionBinder<T> instance(final BeanBinder<T> elementBinder, final Class<?> collectionType) {
return instance(elementBinder, collectionType, null);
}
public static <T> BeanCollectionBinder<T> instance(final BeanBinder<T> elementBinder, final Class<?> collectionType, final String path) {
final BeanCollectionBinder<T> binder = new BeanCollectionBinder<T>(elementBinder);
binder.setPath(path);
binder.setCollectionType(collectionType);
return binder;
}
public static <T> BeanCollectionBinder<T> instance(final BeanBinder<T> elementBinder, final String path) {
return instance(elementBinder, null, path);
}
private BeanBinder<T> elementBinder;
private Class<?> collectionType = ArrayList.class;
public BeanCollectionBinder() {
this(null);
}
public BeanCollectionBinder(final BeanBinder<T> elementBinder) {
setElementBinder(elementBinder);
}
public Class<?> getCollectionType() {
return collectionType;
}
public DataBinder<T> getElementBinder() {
return elementBinder;
}
@Override
public Class<Collection<T>> getType() {
final Class<Collection<T>> clazz = ClassHelper.cast(Collection.class);
return clazz;
}
@Override
public Collection<T> read(final Object object) {
final Collection<T> ret = instantiateCollection();
readInto(ret, object, false);
return ret;
}
@Override
public String readAsString(final Object object) {
validateElementBinder();
final StringBuilder sb = new StringBuilder();
sb.append('[');
for (final Iterator<?> it = IteratorUtils.getIterator(object); it.hasNext();) {
final Object value = it.next();
sb.append(elementBinder.readAsString(value));
if (it.hasNext()) {
sb.append(',');
}
}
sb.append(']');
return sb.toString();
}
@Override
public Collection<T> readFromString(final Object object) {
validateElementBinder();
final Collection<T> ret = instantiateCollection();
readInto(ret, object, true);
return ret;
}
public void readInto(final Collection<T> collection, final Object object, final boolean asString) {
validateElementBinder();
final Object bean = PropertyHelper.get(object, getPath());
// We have separate properties, each one with a collection of values of a single property.
final Map<String, DataBinder<?>> mappings = elementBinder.getMappings();
if (mappings.isEmpty() || bean == null) {
return;
}
// Get each iterator
final Map<String, Iterator<?>> iterators = new LinkedHashMap<String, Iterator<?>>();
for (final Map.Entry<String, DataBinder<?>> entry : mappings.entrySet()) {
final String name = entry.getKey();
final Iterator<?> iterator = IteratorUtils.getIterator(PropertyHelper.get(bean, name));
iterators.put(name, iterator);
}
// Iterate the lists
while (true) {
boolean hasData = true;
final Map<String, Object> current = new HashMap<String, Object>();
hasData = false;
for (final Map.Entry<String, Iterator<?>> entry : iterators.entrySet()) {
final String name = entry.getKey();
final Iterator<?> iterator = entry.getValue();
if (iterator.hasNext()) {
hasData = true;
current.put(name, iterator.next());
}
}
if (hasData) {
// Read the current element
collection.add(asString ? elementBinder.readFromString(current) : elementBinder.read(current));
} else {
// Our iteration has ended
break;
}
}
}
public void setCollectionType(final Class<?> collectionType) {
this.collectionType = collectionType == null ? ArrayList.class : collectionType;
}
public void setElementBinder(final BeanBinder<T> elementBinder) {
this.elementBinder = elementBinder;
}
@Override
public void write(final Object object, final Collection<T> values) {
validateElementBinder();
doWrite(object, values, false);
}
@Override
public void writeAsString(final Object object, final Object value) {
validateElementBinder();
final Collection<String> values = instantiateCollection();
for (final Iterator<?> it = IteratorUtils.getIterator(object); it.hasNext();) {
final Object current = it.next();
values.add(elementBinder.readAsString(current));
}
doWrite(object, values, true);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void doWrite(final Object object, final Collection<?> values, final boolean asString) {
// We have separate properties, each one with a collection of values of a single property.
final Map<String, DataBinder<?>> mappings = ((BeanBinder) elementBinder).getMappings();
if (mappings.isEmpty()) {
return;
}
final Map<String, List<Object>> map = new HashMap<String, List<Object>>();
for (final Object value : values) {
for (final Map.Entry<String, DataBinder<?>> entry : mappings.entrySet()) {
final String name = entry.getKey();
final DataBinder<?> nestedBinder = entry.getValue();
final String path = nestedBinder.getPath();
List<Object> propValues = map.get(path);
if (propValues == null) {
propValues = new ArrayList<Object>();
map.put(path, propValues);
}
propValues.add(PropertyHelper.get(value, name));
}
}
for (final Map.Entry<String, List<Object>> entry : map.entrySet()) {
PropertyHelper.set(object, entry.getKey(), entry.getValue());
}
}
@SuppressWarnings("unchecked")
private <E> Collection<E> instantiateCollection() {
return (Collection<E>) ClassHelper.instantiate(collectionType);
}
private void validateElementBinder() {
if (this.elementBinder == null) {
throw new IllegalStateException("null.element-binder");
}
}
}