/*
* Copyright (c) 2016 wetransform GmbH
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* wetransform GmbH <http://www.wetransform.to>
*/
package eu.esdihumboldt.cst.functions.groovy.helpers.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import groovy.lang.Closure;
import groovy.lang.GroovyObjectSupport;
/**
* Thread-safe helper for collecting values.
*
* @author Simon Templer
*/
public class Collector extends GroovyObjectSupport {
private final Map<Object, Collector> properties = new HashMap<>();
private final List<Object> values = new ArrayList<>();
@Override
public Collector getProperty(String property) {
return getAt(property);
}
@Override
public void setProperty(String property, Object newValue) {
getProperty(property).set(newValue);
}
/**
* Set the collector's value. Clears all previous values and sets the given
* value as the single value.
*
* @param value the value to set
*/
public void set(Object value) {
synchronized (values) {
values.clear();
values.add(value);
}
}
/**
* Convert the collector to the given type.
*
* @param clazz the class to convert to
* @return the converted object
*/
public Object asType(Class<?> clazz) {
if (List.class.equals(clazz)) {
return values();
}
else if (Map.class.equals(clazz)) {
synchronized (properties) {
return new HashMap<>(properties);
}
}
throw new IllegalArgumentException("Conversion to" + clazz.getName() + " not supported.");
}
/**
* Add a value to the collector.
*
* @param value the value to add
*/
public void leftShift(Object value) {
add(value);
}
/**
* Add a value to the collector.
*
* @param value the value to add
*/
public void add(Object value) {
synchronized (values) {
values.add(value);
}
}
/**
* Get the first value of the collector.
*
* @return the first value or <code>null</code>
*/
public Object value() {
synchronized (values) {
return values.isEmpty() ? null : values.get(0);
}
}
/**
* Get the list of values of the collector.
*
* @return a copy of the list of values
*/
public List<Object> values() {
synchronized (values) {
return new ArrayList<>(values);
}
}
/**
* Clear the values and return them.
*
* @return the value list
*/
public List<Object> clear() {
synchronized (values) {
List<Object> copy = new ArrayList<>(values);
values.clear();
return copy;
}
}
/**
* Get the collector with the given name.
*
* @param property the collector name
* @return the child collector
*/
public Collector getAt(Object property) {
synchronized (properties) {
Collector child = properties.get(property);
if (child == null) {
child = new Collector();
properties.put(property, child);
}
return child;
}
}
/**
* Set the value for the collector with the given name.
*
* @param property the child collector name
* @param value the value to set on the child collector
*/
public void putAt(Object property, Object value) {
getAt(property).set(value);
}
/**
* Iterate over the values (one argument) or over the child collectors and
* values (two arguments).
*
* @param closure the closure called for each value or key/values pair
*/
public void each(Closure<?> closure) {
if (closure.getMaximumNumberOfParameters() >= 2) {
// iterate map
Map<Object, Collector> props;
synchronized (properties) {
props = new HashMap<>(properties);
}
props.forEach((key, collector) -> {
closure.call(key, collector.values());
});
}
else {
// iterate values
values().forEach(value -> closure.call(value));
}
}
/**
* Iterate over the child collectors.
*
* @param closure the closure called for each collector key and collector
* (two arguments) or only the collector (one argument)
*/
public void eachCollector(Closure<?> closure) {
// iterate map
Map<Object, Collector> props;
synchronized (properties) {
props = new HashMap<>(properties);
}
props.forEach((key, collector) -> {
if (closure.getMaximumNumberOfParameters() >= 2) {
closure.call(key, collector);
}
else {
closure.call(collector);
}
});
}
/**
* Iterate over the values (one argument) or over the child collectors and
* values (two arguments) and resets the respective values.
*
* @param closure the closure called for each value or key/values pair
*/
public void consume(Closure<?> closure) {
if (closure.getMaximumNumberOfParameters() >= 2) {
// iterate map
Map<Object, Collector> props;
synchronized (properties) {
props = new HashMap<>(properties);
}
props.forEach((key, collector) -> {
closure.call(key, collector.clear());
});
}
else {
// iterate values
List<Object> values = clear();
values.forEach(value -> closure.call(value));
}
}
}