/*
* Copyright (c) 2014 Data Harmonisation Panel
*
* 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:
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.cst.functions.groovy.internal;
import java.util.ArrayList;
import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.cst.MultiValue;
import eu.esdihumboldt.cst.functions.groovy.GroovyTransformation;
import eu.esdihumboldt.hale.common.align.transformation.function.TransformationException;
import eu.esdihumboldt.hale.common.instance.groovy.InstanceBuilder;
import eu.esdihumboldt.hale.common.instance.model.Instance;
import eu.esdihumboldt.hale.common.instance.model.MutableInstance;
import eu.esdihumboldt.hale.common.schema.model.TypeDefinition;
import eu.esdihumboldt.hale.common.schema.model.constraint.type.AugmentedValueFlag;
import eu.esdihumboldt.hale.common.schema.model.constraint.type.HasValueFlag;
import groovy.lang.Closure;
/**
* Target binding class for {@link GroovyTransformation}.
*
* @author Kai Schwierczek
*/
public class TargetCollector {
private static final ALogger log = ALoggerFactory.getLogger(TargetCollector.class);
private class TargetData {
private final Object value;
private final Instance instance;
private TargetData(Object value, Closure<?> closure) {
this.value = value;
if (closure != null) {
// the closure must be evaluated in the moment of the call
// otherwise the bindings may have changed
instance = builder.createInstance(typeDef, closure);
}
else {
instance = null;
}
}
}
private final ArrayList<TargetData> targetData = new ArrayList<>();
private boolean containsValues = false;
private boolean containsClosures = false;
private final InstanceBuilder builder;
private final TypeDefinition typeDef;
/**
* Create a new target collector.
*
* @param builder the builder used to create target instances
* @param typeDef the target type definition
*/
public TargetCollector(InstanceBuilder builder, TypeDefinition typeDef) {
this.builder = builder;
this.typeDef = typeDef;
}
/**
* Call method for easy access from Groovy.
*
* @param targetClosure the target closure
*/
public void call(Closure<?> targetClosure) {
targetData.add(new TargetData(null, targetClosure));
containsClosures = true;
}
/**
* Call method for easy access from Groovy.
*
* @param value the property value
*/
public void call(Object value) {
targetData.add(new TargetData(value, null));
containsValues = true;
}
/**
* Call method for easy access from Groovy.
*
* @param value the property value
* @param targetClosure the target closure
*/
public void call(Object value, Closure<?> targetClosure) {
targetData.add(new TargetData(value, targetClosure));
if (targetClosure != null)
containsClosures = true;
if (value != null)
containsValues = true;
}
/**
* Transforms the closures added to this collector to a {@link MultiValue}
* using the supplied builder.
*
* @param builder the instance builder for creating target instances
* @param type the type of the instance to create
* @return a result value for all closures added to this collector
* @throws TransformationException if some of the collected targets do not
* match the specified type
*/
public MultiValue toMultiValue(InstanceBuilder builder, TypeDefinition type)
throws TransformationException {
MultiValue result = new MultiValue(size());
// a) closures not allowed if the target is no instance
if (containsClosures && type.getChildren().isEmpty()) {
throw new TransformationException("An instance is not applicable for the target.");
}
// b) values not allowed if the target may not have a value
if (containsValues && !type.getConstraint(HasValueFlag.class).isEnabled()
&& !type.getConstraint(AugmentedValueFlag.class).isEnabled()) {
// throw new TransformationException("A value is not applicable for
// the target.");
// this may be desired, e.g. when producing geometries for GML
// so instead of a hard error, we just log a warning
log.warn(
"Value provided for target that does not allow a value according to the schema");
}
for (TargetData data : targetData) {
Object value;
if (data.instance != null) {
Instance instance = data.instance;
// value as instance value
if (data.value != null && instance instanceof MutableInstance) {
((MutableInstance) instance).setValue(data.value);
}
value = instance;
}
else {
value = data.value;
}
result.add(value);
}
return result;
}
/**
* Returns the number of collected targets.
*
* @return the number of collected targets
*/
public int size() {
return targetData.size();
}
}