/* * Copyright (c) 2013 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.hale.ui.functions.groovy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.xml.namespace.QName; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.ui.PlatformUI; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ListMultimap; import eu.esdihumboldt.cst.functions.groovy.GroovyConstants; import eu.esdihumboldt.cst.functions.groovy.GroovyGreedyTransformation; import eu.esdihumboldt.cst.functions.groovy.GroovyTransformation; import eu.esdihumboldt.cst.functions.groovy.helper.HelperFunctionsService; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.align.model.CellUtil; import eu.esdihumboldt.hale.common.align.model.ChildContext; import eu.esdihumboldt.hale.common.align.model.Entity; import eu.esdihumboldt.hale.common.align.model.EntityDefinition; import eu.esdihumboldt.hale.common.align.model.Property; import eu.esdihumboldt.hale.common.align.model.impl.PropertyEntityDefinition; import eu.esdihumboldt.hale.common.align.transformation.function.ExecutionContext; import eu.esdihumboldt.hale.common.align.transformation.function.PropertyValue; import eu.esdihumboldt.hale.common.align.transformation.function.impl.NoResultException; import eu.esdihumboldt.hale.common.align.transformation.function.impl.PropertyValueImpl; import eu.esdihumboldt.hale.common.align.transformation.report.impl.CellLog; import eu.esdihumboldt.hale.common.align.transformation.report.impl.DefaultTransformationReporter; import eu.esdihumboldt.hale.common.core.io.Value; import eu.esdihumboldt.hale.common.instance.groovy.InstanceBuilder; import eu.esdihumboldt.hale.common.schema.SchemaSpaceID; import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.common.schema.model.constraint.property.Cardinality; import eu.esdihumboldt.hale.common.schema.model.constraint.type.Binding; import eu.esdihumboldt.hale.common.schema.model.constraint.type.HasValueFlag; import eu.esdihumboldt.hale.common.schema.model.impl.DefaultPropertyDefinition; import eu.esdihumboldt.hale.common.schema.model.impl.DefaultTypeDefinition; import eu.esdihumboldt.hale.ui.HaleUI; import eu.esdihumboldt.hale.ui.function.generic.AbstractGenericFunctionWizard; import eu.esdihumboldt.hale.ui.functions.groovy.internal.HelperFunctionsCompletions; import eu.esdihumboldt.hale.ui.functions.groovy.internal.InstanceBuilderCompletions; import eu.esdihumboldt.hale.ui.functions.groovy.internal.PageFunctions; import eu.esdihumboldt.hale.ui.functions.groovy.internal.PageHelp; import eu.esdihumboldt.hale.ui.functions.groovy.internal.TypeStructureTray; import eu.esdihumboldt.hale.ui.functions.groovy.internal.TypeStructureTray.TypeProvider; import eu.esdihumboldt.hale.ui.scripting.groovy.InstanceTestValues; import eu.esdihumboldt.hale.ui.scripting.groovy.TestValues; import eu.esdihumboldt.hale.ui.service.align.AlignmentService; import eu.esdihumboldt.hale.ui.util.groovy.SimpleGroovySourceViewerConfiguration; import eu.esdihumboldt.hale.ui.util.groovy.ast.GroovyAST; import eu.esdihumboldt.hale.ui.util.source.CompilingSourceViewer; import eu.esdihumboldt.util.groovy.sandbox.GroovyService; import groovy.lang.Script; /** * Configuration page for the Groovy property transformation script. * * @author Simon Templer */ public class GroovyTransformationPage extends GroovyScriptPage<AbstractGenericFunctionWizard<?, ?>> { private final TestValues testValues; /** * Default constructor. */ public GroovyTransformationPage() { super(); setTitle("Property transformation script"); setDescription("Specify a Groovy script to determine the target property value"); testValues = new InstanceTestValues(); } @Override protected SourceViewerConfiguration createConfiguration() { InstanceBuilderCompletions targetCompletions = new InstanceBuilderCompletions( definitionImages) { @Override protected TypeDefinition getTargetType() { Property targetProperty = (Property) CellUtil .getFirstEntity(getWizard().getUnfinishedCell().getTarget()); if (targetProperty != null) { return targetProperty.getDefinition().getDefinition().getPropertyType(); } return null; } }; HelperFunctionsCompletions functionCompletions = new HelperFunctionsCompletions( HaleUI.getServiceProvider().getService(HelperFunctionsService.class)); return new SimpleGroovySourceViewerConfiguration(colorManager, ImmutableList.of(BINDING_BUILDER, BINDING_TARGET, BINDING_SOURCE_TYPES, BINDING_TARGET_TYPE, BINDING_CELL, BINDING_LOG, BINDING_CELL_CONTEXT, BINDING_FUNCTION_CONTEXT, BINDING_TRANSFORMATION_CONTEXT, BINDING_HELPER_FUNCTIONS), ImmutableList.of(targetCompletions, functionCompletions)); } @Override protected void onShowPage(boolean firstShow) { super.onShowPage(firstShow); // variables and settings may have changed forceValidation(); } @Override protected boolean validate(String document) { super.validate(document); List<PropertyValue> values = new ArrayList<PropertyValue>(); for (EntityDefinition var : getVariables()) { if (var instanceof PropertyEntityDefinition) { PropertyEntityDefinition property = (PropertyEntityDefinition) var; values.add(new PropertyValueImpl(testValues.get(property), property)); } } Property targetProperty = (Property) CellUtil .getFirstEntity(getWizard().getUnfinishedCell().getTarget()); if (targetProperty == null) { // not yet selected (NewRelationWizard) return false; } InstanceBuilder builder = GroovyTransformation .createBuilder(targetProperty.getDefinition()); Cell cell = getWizard().getUnfinishedCell(); boolean useInstanceValues = CellUtil.getOptionalParameter(cell, GroovyTransformation.PARAM_INSTANCE_VARIABLES, Value.of(false)).as(Boolean.class); AlignmentService as = PlatformUI.getWorkbench().getService(AlignmentService.class); GroovyService gs = HaleUI.getServiceProvider().getService(GroovyService.class); Script script = null; try { Collection<? extends Cell> typeCells = as.getAlignment().getTypeCells(cell); // select one matching type cell, the script has to run for all // matching cells // if there is no matching cell it may produce a npe, which is okay Cell typeCell = null; if (!typeCells.isEmpty()) { typeCell = typeCells.iterator().next(); } CellLog log = new CellLog(new DefaultTransformationReporter("dummy", false), cell); ExecutionContext context = new DummyExecutionContext(HaleUI.getServiceProvider()); groovy.lang.Binding binding; if (cell.getTransformationIdentifier().equals(GroovyGreedyTransformation.ID)) { binding = GroovyGreedyTransformation.createGroovyBinding(values, null, cell, typeCell, builder, useInstanceValues, log, context, targetProperty.getDefinition().getDefinition().getPropertyType()); } else { binding = GroovyTransformation.createGroovyBinding(values, null, cell, typeCell, builder, useInstanceValues, log, context, targetProperty.getDefinition().getDefinition().getPropertyType()); } script = gs.parseScript(document, binding); GroovyTransformation.evaluate(script, builder, targetProperty.getDefinition().getDefinition().getPropertyType(), gs); } catch (NoResultException e) { // continue } catch (final Exception e) { return handleValidationResult(script, e); } return handleValidationResult(script, null); } /** * Get the list of source entities configured as variables. * * @return the source entities configured as variables */ protected List<EntityDefinition> getVariables() { ListMultimap<String, ? extends Entity> source = getWizard().getUnfinishedCell().getSource(); if (source != null) { List<EntityDefinition> result = new ArrayList<>(); for (Entity entity : source.get(GroovyConstants.ENTITY_VARIABLE)) { result.add(entity.getDefinition()); } return result; } return Collections.emptyList(); } /** * Get the variable name for an entity definition. * * @param variable the variable * @return the name to use as variable name */ protected String getVariableName(EntityDefinition variable) { if (variable.getPropertyPath() != null && !variable.getPropertyPath().isEmpty()) { List<String> names = new ArrayList<String>(); for (ChildContext context : variable.getPropertyPath()) { names.add(context.getChild().getName().getLocalPart()); } String longName = Joiner.on('_').join(names); return longName; } else return variable.getDefinition().getDisplayName(); } @Override protected void addActions(ToolBar toolbar, CompilingSourceViewer<GroovyAST> viewer) { super.addActions(toolbar, viewer); PageHelp.createToolItem(toolbar, this); TypeStructureTray.createToolItem(toolbar, this, SchemaSpaceID.SOURCE, new TypeProvider() { @Override public Collection<? extends TypeDefinition> getTypes() { // create a dummy type with the variables as children DefaultTypeDefinition dummy = new DefaultTypeDefinition( TypeStructureTray.VARIABLES_TYPE_NAME); Cell cell = getWizard().getUnfinishedCell(); boolean useInstanceValues = CellUtil.getOptionalParameter(cell, GroovyTransformation.PARAM_INSTANCE_VARIABLES, Value.of(false)) .as(Boolean.class); for (EntityDefinition variable : getVariables()) { if (variable.getDefinition() instanceof PropertyDefinition) { PropertyDefinition prop = (PropertyDefinition) variable.getDefinition(); TypeDefinition propertyType; if (useInstanceValues) { // use instance type propertyType = prop.getPropertyType(); } else { // use dummy type with only the // binding/HasValueFlag copied DefaultTypeDefinition crippledType = new DefaultTypeDefinition( prop.getPropertyType().getName()); crippledType.setConstraint( prop.getPropertyType().getConstraint(Binding.class)); crippledType.setConstraint( prop.getPropertyType().getConstraint(HasValueFlag.class)); propertyType = crippledType; } DefaultPropertyDefinition dummyProp = new DefaultPropertyDefinition( new QName(getVariableName(variable)), dummy, propertyType); // for greedy transformation the property can occur any // number of times if (cell.getTransformationIdentifier() .equals(GroovyGreedyTransformation.ID)) dummyProp.setConstraint(Cardinality.CC_ANY_NUMBER); } } return Collections.singleton(dummy); } }); TypeStructureTray.createToolItem(toolbar, this, SchemaSpaceID.TARGET, new TypeProvider() { @Override public Collection<? extends TypeDefinition> getTypes() { Property targetProperty = (Property) CellUtil .getFirstEntity(getWizard().getUnfinishedCell().getTarget()); if (targetProperty != null) { return Collections.singleton( targetProperty.getDefinition().getDefinition().getPropertyType()); } return Collections.emptyList(); } }); PageFunctions.createToolItem(toolbar, this); } @Override public String getHelpContext() { return "eu.esdihumboldt.cst.functions.groovy.script"; } }