/* * 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: * 1Spatial PLC <http://www.1spatial.com> * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package com.onespatial.jrc.tns.oml_to_rif.translate; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.ComparisonType.NUMBER_EQUALS; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.ComparisonType.NUMBER_GREATER_THAN; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.ComparisonType.NUMBER_LESS_THAN; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.ComparisonType.STRING_CONTAINS; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.ComparisonType.STRING_EQUALS; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.NodeType.AND_NODE; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.NodeType.EQUAL_TO_NODE; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.NodeType.GREATER_THAN_NODE; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.NodeType.LESS_THAN_NODE; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.NodeType.LIKE_NODE; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.NodeType.NOT_NODE; import static com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.NodeType.OR_NODE; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import org.opengis.feature.type.Name; import com.onespatial.jrc.tns.oml_to_rif.api.AbstractFollowableTranslator; import com.onespatial.jrc.tns.oml_to_rif.api.TranslationException; import com.onespatial.jrc.tns.oml_to_rif.model.alignment.ModelAlignment; import com.onespatial.jrc.tns.oml_to_rif.model.alignment.ModelAttributeMappingCell; import com.onespatial.jrc.tns.oml_to_rif.model.alignment.ModelClassMappingCell; import com.onespatial.jrc.tns.oml_to_rif.model.alignment.ModelMappingCondition; import com.onespatial.jrc.tns.oml_to_rif.model.alignment.ModelStaticAssignmentCell; import com.onespatial.jrc.tns.oml_to_rif.model.rif.LogicalType; import com.onespatial.jrc.tns.oml_to_rif.model.rif.ModelRifDocument; import com.onespatial.jrc.tns.oml_to_rif.model.rif.ModelRifMappingCondition; import com.onespatial.jrc.tns.oml_to_rif.model.rif.ModelSentence; import com.onespatial.jrc.tns.oml_to_rif.model.rif.PropertyMapping; import com.onespatial.jrc.tns.oml_to_rif.model.rif.StaticAssignment; import com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.AbstractFilterNode; import com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.FilterNode; import com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.comparison.AbstractComparisonNode; import com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.nonterminal.comparison.EqualToNode; import com.onespatial.jrc.tns.oml_to_rif.model.rif.filter.terminal.LeafNode; import com.onespatial.jrc.tns.oml_to_rif.schema.GmlAttribute; import com.onespatial.jrc.tns.oml_to_rif.schema.GmlAttributePath; import com.onespatial.jrc.tns.oml_to_rif.translate.context.RifVariable; import com.onespatial.jrc.tns.oml_to_rif.translate.context.RifVariable.Type; import eu.esdihumboldt.hale.schemaprovider.model.SchemaElement; import eu.esdihumboldt.hale.schemaprovider.model.TypeDefinition; /** * @author Simon Payne (Simon.Payne@1spatial.com) / 1Spatial Group Ltd. * @author Richard Sunderland (Richard.Sunderland@1spatial.com) / 1Spatial Group Ltd. */ public class ModelAlignmentToModelRifTranslator extends AbstractFollowableTranslator<ModelAlignment, ModelRifDocument> { /** * @see com.onespatial.jrc.tns.oml_to_rif.api.Translator#translate(Object) * which this implements. * @param alignment * {@link ModelAlignment} * @return {@link ModelRifDocument} * @throws TranslationException * if any exceptions are thrown during translation */ @Override public ModelRifDocument translate(ModelAlignment alignment) throws TranslationException { ModelRifDocument result = new ModelRifDocument(); // loop over class mappings, & for each of them see if each of their // attribute mappings is found in the attribute mappings that are // possible for the given elementdecl for the source class for (ModelClassMappingCell c : alignment.getClassMappings()) { result.getSentences().add(translateClassMapping(alignment, c)); } return result; } private ModelSentence translateClassMapping(ModelAlignment alignment, ModelClassMappingCell classMapping) { // determine which attribute mappings are applicable to this class // mapping. List<ModelAttributeMappingCell> applicableAttributeMappings = filter(alignment .getAttributeMappings(), classMapping.getSourceClass(), classMapping .getTargetClass()); List<ModelStaticAssignmentCell> applicableStaticAssignments = filter(alignment .getStaticAssignments(), classMapping.getTargetClass()); return buildModelSentance(classMapping, applicableAttributeMappings, applicableStaticAssignments); } private ModelSentence buildModelSentance(ModelClassMappingCell classMapping, List<ModelAttributeMappingCell> attributeMappings, List<ModelStaticAssignmentCell> staticAssignments) { ModelSentence sentence = new ModelSentence(); sentence.setSourceClass( classMapping.getSourceClass().getElementName().getLocalPart().toLowerCase() + "-instance", //$NON-NLS-1$ getName(classMapping.getSourceClass().getElementName())); sentence.setTargetClass( classMapping.getTargetClass().getElementName().getLocalPart().toLowerCase() + "-instance", //$NON-NLS-1$ getName(classMapping.getTargetClass().getElementName())); for (ModelMappingCondition condition : classMapping.getMappingConditions()) { sentence.addMappingCondition(buildRifMappingCondition(sentence, (AbstractFilterNode) condition.getRoot())); } for (ModelAttributeMappingCell attributeMapping : attributeMappings) { buildPropertyMapping(sentence, attributeMapping); } for (ModelStaticAssignmentCell staticAssigment : staticAssignments) { buildStaticAssignment(sentence, staticAssigment); } return sentence; } private void buildStaticAssignment(ModelSentence sentence, ModelStaticAssignmentCell staticAssigment) { RifVariable targetVariable = descendGmlAttributePath(sentence, staticAssigment.getTarget(), false); sentence.addStaticAssigment(new StaticAssignment(targetVariable, staticAssigment .getContent())); } private void buildPropertyMapping(ModelSentence sentence, ModelAttributeMappingCell attributeMapping) { RifVariable sourceVariable = descendGmlAttributePath(sentence, attributeMapping .getSourceAttribute(), true); RifVariable targetVariable = descendGmlAttributePath(sentence, attributeMapping .getTargetAttribute(), false); sentence.addPropertyMapping(new PropertyMapping(sourceVariable, targetVariable)); } private ModelRifMappingCondition buildRifMappingCondition(ModelSentence sentence, AbstractFilterNode node) { ModelRifMappingCondition rifCondition = new ModelRifMappingCondition(); if (node != null) { // is it logical, comparison or geometric? // logical ones if (node.isLogical()) { for (FilterNode child : node.getChildren()) { AbstractFilterNode childNode = (AbstractFilterNode) child; rifCondition.addChild(buildRifMappingCondition(sentence, childNode)); } if (node.getNodeType().equals(AND_NODE)) { rifCondition.setLogicalType(LogicalType.AND); } else if (node.getNodeType().equals(OR_NODE)) { rifCondition.setLogicalType(LogicalType.OR); } else if (node.getNodeType().equals(NOT_NODE)) { rifCondition.setLogicalType(LogicalType.NOT); rifCondition.setNegated(true); } } // comparison ones else if (node.isComparison()) { AbstractComparisonNode cnode = (AbstractComparisonNode) node; if (node.getNodeType().equals(EQUAL_TO_NODE)) { EqualToNode equalNode = (EqualToNode) node; // work out if it's a string or a numeric equality test rifCondition.setOperator(STRING_EQUALS); if (equalNode.getRight().isNumeric()) { rifCondition.setOperator(NUMBER_EQUALS); } } // we assume numeric comparison for the greater-than and // less-than comparisons else if (node.getNodeType().equals(GREATER_THAN_NODE)) { rifCondition.setOperator(NUMBER_GREATER_THAN); } else if (node.getNodeType().equals(LESS_THAN_NODE)) { rifCondition.setOperator(NUMBER_LESS_THAN); } else if (node.getNodeType().equals(LIKE_NODE)) { rifCondition.setOperator(STRING_CONTAINS); } rifCondition.setLeft(getContents(sentence, cnode.getLeft())); rifCondition.setLiteralClass(cnode.getRight().getLiteralValue().getValueClass()); rifCondition.setLiteralValue(cnode.getRight().getLiteralValue().toString()); rifCondition.setRight(getContents(sentence, cnode.getRight())); } // geometric ones else if (node.isGeometric()) { // TODO add this } } return rifCondition; } private RifVariable getContents(ModelSentence sentence, LeafNode leaf) { // it's either a property or a literal if (leaf.getPropertyName() == null) { // we don't need a variable return null; } RifVariable contextVariable = sentence.getSourceClass(); String className = contextVariable.getClassName(); String variableName = className.substring(className.lastIndexOf(':') + 1, className .length()) + "-" + leaf.getPropertyName().toLowerCase() + "-filter"; //$NON-NLS-1$ //$NON-NLS-2$ RifVariable variable = sentence.createVariable(variableName); variable.setContextVariable(sentence.getSourceClass()); variable.setName(variableName.toLowerCase()); variable.setPropertyName(className.substring(0, className.lastIndexOf(':') + 1) + leaf.getPropertyName()); variable.setType(Type.ATTRIBUTE); return variable; } private RifVariable descendGmlAttributePath(ModelSentence sentence, GmlAttributePath gmlAttributePath, boolean isSource) { RifVariable variable; if (isSource) { variable = sentence.getSourceClass(); } else { variable = sentence.getTargetClass(); } for (GmlAttribute fragment : gmlAttributePath) { variable = lazyCreate(variable, fragment, sentence, isSource); } return variable; } private RifVariable lazyCreate(RifVariable current, GmlAttribute fragment, ModelSentence sentence, boolean isSource) { String propertyName = fragment.getDefinition().getName(); //XXX what name to use here? //getName(fragment.getAttributeElement()); RifVariable child = sentence.findChildAttribute(current, propertyName); if (child == null) { String variableName = fragment.getDefinition().getDeclaringType().getName().getLocalPart() + "-" //$NON-NLS-1$ + fragment.getDefinition().getName(); variableName = variableName.toLowerCase(); if (isSource) { child = sentence.createVariable(variableName); } else { child = sentence.createActionVariable(variableName, false); } child.setType(Type.ATTRIBUTE); child.setPropertyName(propertyName); child.setContextVariable(current); } return child; } /** * Filter {@link ModelStaticAssignmentCell}s to leave only those that can be * applied to the specified target class. * * @param staticAssignments * the assignments to filter. * @param targetClass * target class that assignment must apply to. * @return filtered list of assignments. */ private List<ModelStaticAssignmentCell> filter( List<ModelStaticAssignmentCell> staticAssignments, SchemaElement targetClass) { List<ModelStaticAssignmentCell> applicableAssigments = new ArrayList<ModelStaticAssignmentCell>(); for (ModelStaticAssignmentCell candidate : staticAssignments) { TypeDefinition targetElement = candidate.getTarget().get(0).getDefinition().getDeclaringType(); if (canBeSubstitutedBy(targetElement, targetClass)) { applicableAssigments.add(candidate); } // also test attributes. } return applicableAssigments; } /** * Filter {@link ModelAttributeMappingCell}s to leave only those that can * target the specified source and target classes. * * @param attributeMappings * cells to filter. * @param sourceClass * source class that mapping must apply to. * @param targetClass * target class that mapping must apply to. * @return filtered list of mappings cells. */ private static List<ModelAttributeMappingCell> filter( List<ModelAttributeMappingCell> attributeMappings, SchemaElement sourceClass, SchemaElement targetClass) { List<ModelAttributeMappingCell> applicableMappings = new ArrayList<ModelAttributeMappingCell>(); for (ModelAttributeMappingCell candidate : attributeMappings) { TypeDefinition sourceElement = candidate.getSourceAttribute().get(0).getDefinition().getDeclaringType(); TypeDefinition targetElement = candidate.getTargetAttribute().get(0).getDefinition().getDeclaringType(); if (canBeSubstitutedBy(sourceElement, sourceClass) && canBeSubstitutedBy(targetElement, targetClass)) { applicableMappings.add(candidate); } // also test attributes. } return applicableMappings; } /** * Determines if the given type can be substituted by the given element * * @param type the type definition * @param element the element * @return if the type can be substituted by the given element */ private static boolean canBeSubstitutedBy(TypeDefinition type, SchemaElement element) { Queue<TypeDefinition> toTest = new LinkedList<TypeDefinition>(); toTest.add(type); while (!toTest.isEmpty()) { TypeDefinition test = toTest.poll(); if (test.getDeclaringElements().contains(element)) return true; toTest.addAll(test.getSubTypes()); } return false; } private String getName(Name element) { return element.getNamespaceURI() + ":" + element.getLocalPart(); //$NON-NLS-1$ } }