/*
* Copyright (c) 2012 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:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.cst.internal;
import eu.esdihumboldt.cst.MultiValue;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.GroupNode;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.TargetNode;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.TransformationTree;
import eu.esdihumboldt.hale.common.align.transformation.report.TransformationLog;
import eu.esdihumboldt.hale.common.instance.model.Instance;
import eu.esdihumboldt.hale.common.instance.model.MutableGroup;
import eu.esdihumboldt.hale.common.instance.model.MutableInstance;
import eu.esdihumboldt.hale.common.instance.model.impl.DefaultGroup;
import eu.esdihumboldt.hale.common.instance.model.impl.DefaultInstance;
import eu.esdihumboldt.hale.common.schema.model.DefinitionUtil;
import eu.esdihumboldt.hale.common.schema.model.constraint.property.Cardinality;
/**
* Populates an instance from a transformation tree.
*
* @author Simon Templer
*/
public class InstanceBuilder {
/**
* Represents no object.
*/
private static enum NoObject {
NONE;
}
/**
* Populate the given instance from a transformation tree.
*
* @param target the target instance
* @param tree the transformation tree
* @param typeLog the type transformation log
*/
public void populate(MutableInstance target, TransformationTree tree, TransformationLog typeLog) {
populateGroup(target, tree, typeLog);
}
/**
* Get the value for a target node.
*
* @param node the target node
* @param typeLog the type transformation log
* @return the value or {@link NoObject#NONE} representing no value
*/
private Object getValue(TargetNode node, TransformationLog typeLog) {
if (node.getChildren(true).isEmpty()) {
// simple leaf
if (node.isDefined()) {
// XXX case where an Instance should be returned? XXX according
// to the definition?!
// XXX this is done in FunctionExecutor
return node.getResult();
}
else {
return NoObject.NONE;
}
}
boolean isProperty = node.getDefinition().asProperty() != null;
boolean isGroup = node.getDefinition().asGroup() != null;
if (isProperty && node.isDefined()) {
// it's a property and we have a value/values
Object nodeValue = node.getResult();
if (!(nodeValue instanceof MultiValue)) {
// pack single value into multivalue
MultiValue nodeMultiValue = new MultiValue();
nodeMultiValue.add(nodeValue);
nodeValue = nodeMultiValue;
}
MultiValue nodeMultiValue = (MultiValue) nodeValue;
if (!nodeMultiValue.isEmpty()) {
// Create n instances
MultiValue resultMultiValue = new MultiValue(nodeMultiValue.size());
for (Object value : nodeMultiValue) {
// the value may have been wrapped in an Instance
if (value instanceof Instance) {
value = ((Instance) value).getValue();
}
MutableInstance instance = new DefaultInstance(node.getDefinition()
.asProperty().getPropertyType(), null);
instance.setValue(value);
// XXX since this is the same for all instances maybe do
// this on a dummy and only copy properties for each?
// XXX MultiValue w/ target node children => strange results
populateGroup(instance, node, typeLog);
resultMultiValue.add(instance);
}
return resultMultiValue;
}
// if nodeMultiValue is empty fall through to below
// it the instance could still have children even without a value
}
// it's a property or group with no value
MutableGroup group;
if (isGroup) {
group = new DefaultGroup(node.getDefinition().asGroup());
}
else if (isProperty) {
group = new DefaultInstance(node.getDefinition().asProperty().getPropertyType(), null);
}
else {
throw new IllegalStateException("Illegal child definition");
}
// populate with children
if (populateGroup(group, node, typeLog)) {
return group;
}
else {
return NoObject.NONE;
}
}
/**
* Populates a group with values from its children.
*
* @param group the group
* @param node the node associated with the group
* @param typeLog the type transformation log
* @return if any values were added to the group
*/
private boolean populateGroup(MutableGroup group, GroupNode node, TransformationLog typeLog) {
boolean anyValue = false;
for (TargetNode child : node.getChildren(true)) {
Object value = getValue(child, typeLog);
if (value != NoObject.NONE) {
// add value to group
if (value instanceof MultiValue) {
MultiValue multiValue = (MultiValue) value;
int toAdd = multiValue.size();
// check cardinality
Cardinality card = DefinitionUtil.getCardinality(child.getDefinition());
if (card.getMaxOccurs() != Cardinality.UNBOUNDED && card.getMaxOccurs() < toAdd) {
toAdd = (int) card.getMaxOccurs();
typeLog.warn(typeLog.createMessage("Too many values present for "
+ child.getDefinition().getDisplayName()
+ ". Limiting to match cardinality.", null));
}
// add properties
for (int i = 0; i < toAdd; i++) {
group.addProperty(child.getDefinition().getName(), multiValue.get(i));
}
if (toAdd > 0) {
anyValue = true;
}
}
else {
group.addProperty(child.getDefinition().getName(), value);
anyValue = true;
}
}
}
return anyValue;
}
}