package com.openMap1.mapper.core; import org.eclipse.emf.ecore.*; import com.openMap1.mapper.reader.XOReader; import com.openMap1.mapper.util.ModelUtil; /** * Class used to record discrepancies between translation * sources and results at the semantic level - i.e * discrepancies between Ecore model instances derived from * translation results and sources * * @author robert * */ public class SemanticMismatch extends TranslationIssue { public static int SEMANTIC_MISSING_CLASS = 0; public static int SEMANTIC_MISSING_LINK = 1; public static int SEMANTIC_MISSING_PROPERTY_VALUE = 2; public static int SEMANTIC_EXTRA_CLASS = 3; public static int SEMANTIC_EXTRA_LINK = 4; public static int SEMANTIC_EXTRA_PROPERTY_VALUE = 5; public static int SEMANTIC_INCORRECT_PROPERTY_VALUE = 6; public static int SEMANTIC_INCORRECT_TARGET_CLASS = 7; public static int SEMANTIC_INCORRECT_LINK_CARDINALITY = 8; // instance variables set in the constructor private EObject ownerObject; private EStructuralFeature feature; private EObject targetObject; // int nature, occurrences, String expected and String actual are inherited from TranslationIssue /** * @return Class involved - missing instances, extra instances, or * missing or incorrect property values */ public String className() {return ModelUtil.getQualifiedClassName(ownerObject.eClass());} /** * @return Property involved - in missing or incorrect property values */ public String featureName() { if (feature == null) return ""; return feature.getName(); } /** * when the 'expected ' and 'actual' fields record numbers of occurrences, * add one actual occurrence */ public void addOccurrence() {occurrences++;} /** * @return path of containment association names from root of the ECore instance * to the owner EObject */ public String path() {return ModelUtil.pathFromRoot(ownerObject);} private String sourceCode, resultCode; public String sourceCode() {return sourceCode;} /** * @param nature nature of the problem - unexpected name, missing, or repeated * @param expected expected value of some property * @param actual actual wrong value of that property */ public SemanticMismatch( int nature, String expected, String actual, EObject ownerObject, EStructuralFeature feature, EObject targetObject, String sourceCode,String resultCode) { super(nature,expected,actual); this.ownerObject = ownerObject; this.feature = feature; this.targetObject = targetObject; this.sourceCode = sourceCode; this.resultCode = resultCode; } public boolean missingFault() { return ((nature == SEMANTIC_MISSING_CLASS)| (nature == SEMANTIC_MISSING_LINK)| (nature == SEMANTIC_MISSING_PROPERTY_VALUE)); } public boolean extraFault() { return ((nature == SEMANTIC_EXTRA_CLASS)| (nature == SEMANTIC_EXTRA_LINK)| (nature == SEMANTIC_EXTRA_PROPERTY_VALUE)); } /** * @return description of the mismatch */ public String description() { String description = ""; if (nature == SEMANTIC_MISSING_CLASS) {description = "Missing object of class '" + className() + "'";} if (nature == SEMANTIC_MISSING_LINK) {description = "Missing link from object of class '" + className() + "' with role name '" + featureName() + "'";} if (nature == SEMANTIC_MISSING_PROPERTY_VALUE) {description = "Missing property '" + featureName() + "' in class '" + className() + "'";} if (nature == SEMANTIC_EXTRA_CLASS) {description = "Unexpected object of class '" + className() + "'";} if (nature == SEMANTIC_EXTRA_LINK) {description = "Unexpected link from object of class '" + className() + "' with role name '" + featureName() + "'";} if (nature == SEMANTIC_EXTRA_PROPERTY_VALUE) {description = "Unexpected property '" + featureName() + "' in class '" + className() + "'";} if (nature == SEMANTIC_INCORRECT_PROPERTY_VALUE) {description = "Incorrect value of property '" + featureName() + "' in class '" + className() + "'; should be '" + expected + "' but is '" + actual + "'" ;} if (nature == SEMANTIC_INCORRECT_TARGET_CLASS) {description = "Link '" + featureName() + " from class '" + className() + "' " + " has incorrect target class '" + actual + "' which should be '" + expected + "'";} if (nature == SEMANTIC_INCORRECT_LINK_CARDINALITY) {description = "Link '" + featureName() + "' from class '" + className() + "' " + " has incorrect cardinality " + actual + " which should be " + expected;} return description; } /** * * @param col column index 0..N * @return the entry for this translation issue in the column col */ public String cellContents(int col) { String cell = ""; if (col == CODE) cell = fileName; else if (col == TYPE) cell = "Semantic"; else if (col == OCCURRENCES) cell = new Integer(occurrences).toString(); else if (col == DESCRIPTION) cell = description(); else if (col == CAUSEORLOCATION) cell = path() + "; " + describeMissingMappings(); return cell; } /** * @return a key for storing SemanticMismatch object in a Hashtable, so that * further occurrences of the same fault do not lead to duplicate records */ public String key() { String key = "semantic_" + nature + "$" + className() + ":" + featureName() ; if (!missingFault() && !extraFault()) key = key + "(" + expected + "," + actual + ")"; return key; } //------------------------------------------------------------------------------------ // Tracing the origin of missing EObjects in missing mappings //------------------------------------------------------------------------------------ /** * check that there is a mapping to this class, property or link * in source and result mappings. * FIXME: does not take proper account of mappings to subclasses * * @param classObject the EClass of the object that is missing in the result, or which has * some feature missing * @param featureObject the EAttribute or EReference which is missing * @param sourceReader the XOReader for the source - which should have all the mappings * @param resultReader the XOReader for the result - which may have gaps (or some gaps may * come from earlier translations, if the results comes from a chain of more than one translation) */ public boolean checkMissingMapping(XOReader sourceReader, XOReader resultReader) throws MapperException { /* FIXME; this can be activeted to avoid the reader calls below, * which can be highly expensive when there are many missing mapping sets */ // if (true) return false; if (sourceReader == null) return false; if (resultReader == null) return false; missingMapping = false; /* it would be rather bizarre for the source mapping to be missing; but * sometimes apparently happens because the check does not take account of inheritance yet */ boolean missingSourceMapping = false; // not used missingMappingDescription = ""; if (nature == SEMANTIC_MISSING_CLASS) { missingMappingDescription = "object mapping for class '" + className() + "'"; if (!sourceReader.representsObject(className())) missingSourceMapping = true; if (!resultReader.representsObject(className())) missingMapping = true; } else if (nature == SEMANTIC_MISSING_PROPERTY_VALUE) { missingMappingDescription = "property mapping for '" + className() + ":" + featureName() + "'"; if (!sourceReader.representsProperty(className(), featureName())) missingSourceMapping = true; if (!resultReader.representsProperty(className(), featureName())) missingMapping = true; } else if (nature == SEMANTIC_MISSING_LINK) { String targetClassName = ModelUtil.getQualifiedClassName(targetObject.eClass()); missingMappingDescription = "association mapping for '" + className() + "." + featureName() + "." + targetClassName + "'"; if (!sourceReader.representsAssociationRole(className(), featureName(),targetClassName)) missingSourceMapping = true; if (!resultReader.representsAssociationRole(className(), featureName(),targetClassName)) missingMapping = true; } if (missingSourceMapping) {} return missingMapping; } private String describeMissingMappings() { String missing = ""; if (missingMapping) missing = "No " + missingMappingDescription + " in source " + resultCode; return missing; } }