/*
* Copyright (c) 2015 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.core.inline;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import eu.esdihumboldt.hale.common.align.model.Alignment;
import eu.esdihumboldt.hale.common.align.model.Cell;
import eu.esdihumboldt.hale.common.align.model.MutableAlignment;
import eu.esdihumboldt.hale.common.align.model.MutableCell;
import eu.esdihumboldt.hale.common.align.model.TransformationMode;
import eu.esdihumboldt.hale.common.align.model.Type;
import eu.esdihumboldt.hale.common.align.model.impl.DefaultAlignment;
import eu.esdihumboldt.hale.common.align.model.impl.DefaultCell;
import eu.esdihumboldt.hale.common.align.model.impl.DefaultType;
import eu.esdihumboldt.hale.common.align.model.impl.PropertyEntityDefinition;
import eu.esdihumboldt.hale.common.align.model.impl.TypeEntityDefinition;
import eu.esdihumboldt.hale.common.align.transformation.engine.TransformationEngine;
import eu.esdihumboldt.hale.common.align.transformation.function.PropertyValue;
import eu.esdihumboldt.hale.common.align.transformation.function.TransformationException;
import eu.esdihumboldt.hale.common.align.transformation.function.impl.AbstractSingleTargetPropertyTransformation;
import eu.esdihumboldt.hale.common.align.transformation.function.impl.NoResultException;
import eu.esdihumboldt.hale.common.align.transformation.report.TransformationLog;
import eu.esdihumboldt.hale.common.align.transformation.report.TransformationReport;
import eu.esdihumboldt.hale.common.align.transformation.service.InstanceSink;
import eu.esdihumboldt.hale.common.align.transformation.service.TransformationService;
import eu.esdihumboldt.hale.common.align.transformation.service.impl.DefaultInstanceSink;
import eu.esdihumboldt.hale.common.align.transformation.service.impl.ThreadSafeInstanceSink;
import eu.esdihumboldt.hale.common.core.io.ProgressIndicator;
import eu.esdihumboldt.hale.common.core.io.impl.LogProgressIndicator;
import eu.esdihumboldt.hale.common.instance.model.Instance;
import eu.esdihumboldt.hale.common.instance.model.impl.DefaultInstanceCollection;
import eu.esdihumboldt.hale.common.schema.SchemaSpaceID;
import eu.esdihumboldt.hale.common.schema.model.TypeDefinition;
/**
* Transformation that runs a type transformation on a property.
*
* @author Simon Templer
*/
public class InlineTransformation extends
AbstractSingleTargetPropertyTransformation<TransformationEngine> {
@Override
protected Object evaluate(String transformationIdentifier, TransformationEngine engine,
ListMultimap<String, PropertyValue> variables, String resultName,
PropertyEntityDefinition resultProperty, Map<String, String> executionParameters,
TransformationLog log) throws TransformationException, NoResultException {
List<PropertyValue> sources = variables.get(null);
if (sources.isEmpty()) {
throw new NoResultException("No source available to transform");
}
PropertyValue source = sources.get(0);
Object sourceValue = source.getValue();
if (sourceValue == null) {
throw new NoResultException("Source value is null");
}
if (!(sourceValue instanceof Instance)) {
throw new TransformationException("Sources for inline transformation must be instances");
}
Instance sourceInstance = (Instance) sourceValue;
TypeDefinition sourceType = sourceInstance.getDefinition();
// get the original alignment
Alignment orgAlignment = getExecutionContext().getAlignment();
MutableAlignment alignment = new DefaultAlignment(orgAlignment);
// identify relevant type cell(s)
MutableCell queryCell = new DefaultCell();
ListMultimap<String, Type> sourceEntities = ArrayListMultimap.create();
sourceEntities.put(null, new DefaultType(new TypeEntityDefinition(sourceType,
SchemaSpaceID.SOURCE, null)));
queryCell.setSource(sourceEntities);
ListMultimap<String, Type> targetEntities = ArrayListMultimap.create();
targetEntities.put(null, new DefaultType(new TypeEntityDefinition(resultProperty
.getDefinition().getPropertyType(), SchemaSpaceID.TARGET, null)));
queryCell.setTarget(targetEntities);
Collection<? extends Cell> candidates = alignment.getTypeCells(queryCell);
if (candidates.isEmpty()) {
log.error(log.createMessage("No type transformations found for inline transformation",
null));
throw new NoResultException();
}
// filter alignment -> only keep relevant type relations
List<Cell> allTypeCells = new ArrayList<>(alignment.getTypeCells());
for (Cell cell : allTypeCells) {
// remove cell
alignment.removeCell(cell);
if (!cell.getTransformationMode().equals(TransformationMode.disabled)) {
// only readd if not disabled
MutableCell copy = new DefaultCell(cell);
if (candidates.contains(cell)) {
// readd as active
copy.setTransformationMode(TransformationMode.active);
}
else {
// readd as passive
copy.setTransformationMode(TransformationMode.passive);
}
alignment.addCell(copy);
}
}
// prepare transformation input/output
DefaultInstanceCollection sourceInstances = new DefaultInstanceCollection();
sourceInstances.add(sourceInstance);
DefaultInstanceSink target = new DefaultInstanceSink();
// run transformation
TransformationService ts = getExecutionContext().getService(TransformationService.class);
if (ts == null) {
throw new TransformationException(
"Transformation service not available for inline transformation");
}
ProgressIndicator progressIndicator = new LogProgressIndicator();
TransformationReport report = ts.transform(alignment, sourceInstances,
new ThreadSafeInstanceSink<InstanceSink>(target), getExecutionContext(),
progressIndicator);
// copy report messages
log.importMessages(report);
if (!report.isSuccess()) {
// copy report messages
log.importMessages(report);
throw new TransformationException("Inline transformation failed");
}
// extract result
List<Instance> targetList = target.getInstances();
if (targetList.isEmpty()) {
log.error(log.createMessage("Inline transformation yielded no result", null));
throw new NoResultException("No result from inline transformation");
}
if (targetList.size() > 1) {
log.error(log.createMessage(
"Inline transformation yielded multiple results, only first result is used",
null));
}
return targetList.get(0);
}
}