/******************************************************************************* * Copyright (c) 2010 Michal Antkiewicz. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Michal Antkiewicz - initial API and implementation ******************************************************************************/ package ca.uwaterloo.gsd.fsml.core; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.provider.ItemPropertyDescriptor; import org.eclipse.jdt.core.JavaModelException; import ca.uwaterloo.gsd.fsml.ecore.FSMLEcoreUtil; import ca.uwaterloo.gsd.fsml.ecore.FSMLEcoreUtil.NavigationResult; import ca.uwaterloo.gsd.fsml.fsml.Model; import ca.uwaterloo.gsd.fsml.stats.Measurements; import ca.uwaterloo.gsd.fsml.stats.Stats; import ca.uwaterloo.gsd.fsml.sync.ClassSyncItem; import ca.uwaterloo.gsd.fsml.sync.RA; import ca.uwaterloo.gsd.fsml.sync.StructuralFeatureSyncItem; import ca.uwaterloo.gsd.fsml.sync.SyncItem; /** * @author Michal Antkiewicz <mantkiew@gsd.uwaterloo.ca> * * A singleton class. Implements a generic FSML reverse engineering algorithm and supports incremental forward engineering. * Supports custom FSML annotation interpreters. * * TODO: need to distinguish between incremental and non-incremental forward and reverse. * Currently reverse is batch and forward is incremental. */ public class Queries { /** * Annotation used on non-containment references to indicate the base concept reference */ public static final String BASE_CONCEPT = "baseConcept"; /** * Constants for constraint mappings */ public static final String DETAIL_INSTANCE_OF = "instanceOf"; public static final String CONSTRAINT_AND = "and"; public static final String CONSTRAINT_AND_PARENT_IS = "andParentIs"; public static final String DETAIL_CONTAINS = "contains"; public static final String DETAIL_EQUALS_TO = "equalsTo"; public static final String DETAIL_ATTRIBUTE = "attribute"; public static final String DETAIL_IN = "in"; public static final String CONSTRAINT_WHERE = "where"; public static final String CONSTRAINT_CONCATENATE = "concatenate"; public static final String DETAIL_STRING = "string"; public static final String DETAIL_ELEMENT = "element"; public static final String DETAIL_POSITION = "position"; public static final String CONSTRAINT_VALUE_OF = "valueOf"; public static final String DETAIL_CLASS = "class"; public static final String DETAIL_VALUE_BEFORE = "before"; public static final String DETAIL_VALUE_AFTER = "after"; /** * <constraint feature: ../../x requires: ../y > * <constraint feature: ../../x excludes: ../y > */ public static final String CONSTRAINT_REQUIRES_EXCLUDES = "constraint"; public static final String DETAIL_FEATURE = "feature"; public static final String DETAIL_REQUIRES = "requires"; public static final String DETAIL_EXCLUDES = "excludes"; /** * some keys for performance measurements */ public static final String ANALYSIS_KEY = "Queries.Analysis"; public static Queries INSTANCE = new Queries(); /** * The project being processed */ protected IProject project; private HashMap<EObject, IProject> feature2IProject; private Queries() { } /** * @param project * @param natureId * @return true iff project has nature with natureId. */ public static boolean hasNature(IProject project, String natureId) { try { String[] natures = project.getDescription().getNatureIds(); for (int i = 0; i < natures.length; i++) if (natures[i].compareTo(natureId) == 0) return true; } catch (CoreException e) { } return false; } /** * This clears all cache data and configures Queries with given project. If * project doesn't have required nature, all cache data is reset and null * project is set. * * @param project */ public void setProject(IProject project) { this.project = project; Markers.INSTANCE.setProject(project); reset(); } public IProject getProject() { return project; } /** * clear cache for annotation interpreters and registers new JavaAnnotationInterpreter (which * effectively clears the cache of the interpreter too). */ public void reset() { // clear cache mappingInterpreters = new ArrayList<FSMLMappingInterpreter>(); query2interpreter = new HashMap<String, FSMLMappingInterpreter>(); context2interpreters = new HashMap<String, Collection<FSMLMappingInterpreter>>(); } /** * This method MUST be called by any project specific analysis method * because it sets up annotation cache and initializes context element maps. * This method is not intended to be overriden. * Override doPerformAnalysis instead. * * @param model * @param progressMonitor * @return */ public final Model performAnalysis(Model model, IProgressMonitor progressMonitor) { Stats.INSTANCE.reset(); Markers.INSTANCE.removeMarkers(); Stats.INSTANCE.printMessage("Analysis results and statistics for the project '" + project.getName() + "'\n"); Measurements.instance(ANALYSIS_KEY).start(); // initialize all registered mapping interpreters if (!initialize(project, model)) // at least one of the interpreters failed to initialize return model; try { try { if (reverseFeatureRepresentedAsClass(model, progressMonitor)) Stats.INSTANCE.logFeatureInstance(model, null, model.eClass().getEAnnotation("project")); } catch (FSMLMappingException e) { e.printStackTrace(); } // process model queries to set values of non-containment and non-alias references try { processRegularReferences(model); } catch (FSMLMappingException e) { e.printStackTrace(); } } // need to finalize regardless of what exception was thrown finally { // notify mapping interpreters that the analysis has ended terminate(progressMonitor); } Measurements.instance(ANALYSIS_KEY).stop(); Measurements.instance(ANALYSIS_KEY).printAndReset("Time of reverse engineering : "); Stats.INSTANCE.printAll(); return model; } protected void processRegularReferences(EObject element) throws FSMLMappingException { if (element == null) return; for (Object aux : element.eClass().getEAllReferences()) { EReference feature = (EReference) aux; if (!feature.isContainment() && feature.getEAnnotation(BASE_CONCEPT) == null) { EAnnotation annotation = feature.getEAnnotation(CONSTRAINT_WHERE); if (annotation != null) { // find potential targets Collection<EObject> targets = ItemPropertyDescriptor.getReachableObjectsOfType( element, feature.getEReferenceType()); String attributePath = (String) annotation.getDetails().get(DETAIL_ATTRIBUTE); if (attributePath == null) continue; String equalsToPath = (String) annotation.getDetails().get(DETAIL_EQUALS_TO); String containsPath = (String) annotation.getDetails().get(DETAIL_CONTAINS); String inPath = (String) annotation.getDetails().get(DETAIL_IN); if (equalsToPath == null && containsPath == null && inPath == null) continue; FSMLEcoreUtil.NavigationResult target = null; String equalsToValue = null; EList equalsToList = null; if (equalsToPath != null){ target = FSMLEcoreUtil.navigateToEObject(element, equalsToPath); equalsToValue = (String) target.eObject.eGet(target.eAttribute); } else if (containsPath != null){ target = FSMLEcoreUtil.navigateToEObject(element, containsPath); equalsToValue = (String) target.eObject.eGet(target.eAttribute); } else if (inPath != null){ target = FSMLEcoreUtil.navigateToEObject(element, inPath); equalsToList = (EList)target.eObject.eGet(target.eAttribute); } else{ continue; } if (equalsToValue == null && (equalsToList == null|| equalsToList.isEmpty())){ continue; } assert (attributePath != null); // check for an additional 'andWhere' clause EAnnotation and = feature.getEAnnotation(CONSTRAINT_AND); String attribute2Path = null; String equalsTo2Path = null; String equalsTo2Value = null; if (and != null) { attribute2Path = (String) and.getDetails().get(DETAIL_ATTRIBUTE); equalsTo2Path = (String) and.getDetails().get(DETAIL_EQUALS_TO); FSMLEcoreUtil.NavigationResult target2 = FSMLEcoreUtil.navigateToEObject(element, equalsTo2Path); equalsTo2Value = (String) target2.eObject.eGet(target2.eAttribute); } EAnnotation andParentIs = feature.getEAnnotation(CONSTRAINT_AND_PARENT_IS); String parentClassName = null; if (andParentIs != null) parentClassName = (String) andParentIs.getDetails().get(DETAIL_INSTANCE_OF); // find the first target satisfying the constraint for (Object object : targets) { if (object instanceof EObject) { EObject eObject = (EObject) object; EClass eClass = eObject.eClass(); FSMLEcoreUtil.NavigationResult attributeTarget = FSMLEcoreUtil.navigateToEObject(eObject, attributePath); if (attributeTarget.errorMessage != null) throw new FSMLMappingException(Cause.MODEL_NAVIGATION_ERROR, feature, attributeTarget.errorMessage); EAttribute eAttribute = attributeTarget.eAttribute; if (eAttribute==null || attributeTarget.eObject==null){ continue; } if (equalsToPath != null) { // handle only single-valued attributes String auxValue = (String) attributeTarget.eObject.eGet(eAttribute); if (FSMLEcoreUtil.valuesEqual(equalsToValue, auxValue)) { if (and != null) { FSMLEcoreUtil.NavigationResult target3 = FSMLEcoreUtil.navigateToEObject(eObject, attribute2Path); if (target3.eObject == null || target3.eAttribute == null) continue; //FSMLEcoreUtil.NavigationResult attribute2Target = FSMLEcoreUtil.navigateToEObject(element, attribute2Path); EAttribute eAttribute2 = target3.eAttribute; String auxValue2 = (String) target3.eObject.eGet(eAttribute2); if (!FSMLEcoreUtil.valuesEqual(equalsTo2Value, auxValue2)) continue; } if (andParentIs != null) { if (!eObject.eContainer().eClass().getName().equals(parentClassName)) continue; } if (feature.isMany()) ((EList) element.eGet(feature)).add(eObject); else { element.eSet(feature, eObject); break; } } } else if (containsPath != null) { // handle only multi-valued attributes if (attributeTarget.errorMessage != null) throw new FSMLMappingException(Cause.MODEL_NAVIGATION_ERROR, attributeTarget.errorMessage); else { EList auxValues = (EList) attributeTarget.eObject.eGet(attributeTarget.eAttribute); if (auxValues.contains(equalsToValue)) { if (feature.isMany()) ((EList) element.eGet(feature)).add(eObject); else { element.eSet(feature, eObject); break; } } } } else if (inPath != null){ // handle only single-valued attributes contained in list. String auxValue = (String) attributeTarget.eObject.eGet(eAttribute); if (equalsToList.contains(auxValue)){ if (feature.isMany()) ((EList) element.eGet(feature)).add(eObject); else { element.eSet(feature, eObject); break; } } } } } } } else { // recursively process children if (feature.isMany()) { for (Object child : (EList) element.eGet(feature)) { processRegularReferences((EObject) child); } } else processRegularReferences((EObject) element.eGet(feature)); } } } public boolean createChildrenForBaseConceptReference(EObject element, EReference feature, EClass concreteChildType, EReference baseConceptReference, IProgressMonitor progressMonitor) throws FSMLMappingException { if (concreteChildType == null || baseConceptReference == null) return false; // find potential targets Collection<EObject> targets = ItemPropertyDescriptor.getReachableObjectsOfType(element, baseConceptReference.getEReferenceType()); if (feature.isMany()) { EList children = (EList) element.eGet(feature); for (Object target : targets) { EObject child = EcoreUtil.create(concreteChildType); children.add(child); // assuming that the reference is not multivalue child.eSet(baseConceptReference, target); EAnnotation annotation = baseConceptReference.getEAnnotation(BASE_CONCEPT); //Stats.INSTANCE.logFeatureInstance(concreteChildType, baseConceptReference, "(target set)"); boolean aliased = false; for (Object aux : FSMLEcoreUtil.getContextAnnotations(concreteChildType)) { String name = ((EAnnotation) aux).getSource(); Collection<FSMLMappingInterpreter> interpreters = context2interpreters.get(name); if (interpreters != null && !interpreters.isEmpty()) { for (FSMLMappingInterpreter interpreter : interpreters) if (interpreter.aliasContext((EObject) target, child)) aliased = true; } } if (aliased) { if (reverseFeatureRepresentedAsClass(child, progressMonitor)) { Stats.INSTANCE.logFeatureInstance(child, baseConceptReference, annotation); Stats.INSTANCE.logFeatureInstance(element, feature, null); } } } } return (feature.isMany() ? !((EList) element.eGet(feature)).isEmpty() : element.eGet(feature) != null); } Collection<FSMLMappingInterpreter> mappingInterpreters = new ArrayList<FSMLMappingInterpreter>(); HashMap<String, FSMLMappingInterpreter> query2interpreter = new HashMap<String, FSMLMappingInterpreter>(); HashMap<String, Collection<FSMLMappingInterpreter>> context2interpreters = new HashMap<String, Collection<FSMLMappingInterpreter>>(); public void registerCustomInterpreter(FSMLMappingInterpreter interpreter) { if (interpreter != null) { mappingInterpreters.add(interpreter); for (String context : interpreter.getContextAnnotations()) { Collection<FSMLMappingInterpreter> interpreters = context2interpreters.get(context); if (interpreters == null) { interpreters = new ArrayList<FSMLMappingInterpreter>(); context2interpreters.put(context, interpreters); } interpreters.add(interpreter); } for (String query : interpreter.getQueryAnnotations()) { FSMLMappingInterpreter existingInterpreter = query2interpreter.get(query); if (existingInterpreter == null || interpreter.getDescription().equals(existingInterpreter.getDescription())) { query2interpreter.put(query, interpreter); } else Stats.INSTANCE.logError("Critical error: at most one interpreter for query annotation! Tried to register query '" + query + "' of " + interpreter.getDescription() + " "); } } } public boolean initialize(IProject project, Model model) { feature2IProject = new HashMap<EObject, IProject>(); feature2IProject.put(model, project); for (FSMLMappingInterpreter interpreter : mappingInterpreters) if (!interpreter.initialize(project, model)) { Stats.INSTANCE.logError("FSML annotation interpreter failed to initialize: " + interpreter.getDescription()); return false; } return true; } public void terminate(IProgressMonitor progressMonitor) { for (FSMLMappingInterpreter interpreter : mappingInterpreters) interpreter.terminate(progressMonitor); } /** * @param eObject * @param contextClass * @param required TODO * @return context object returned by the first interpreter that can handle given contextClass type. */ public Object getContext(EObject eObject, Class contextClass, boolean required) { EObject element = eObject; if (eObject instanceof SyncItem) { SyncItem syncItem = (SyncItem) eObject; element = (syncItem.getCode() != null ? syncItem.getCode() : syncItem.getModel()); } if (IProject.class.equals(contextClass)) return feature2IProject.get(element); for (FSMLMappingInterpreter interpreter : mappingInterpreters) { if (interpreter.canHandleContext(contextClass)) { Object context = interpreter.getContext(element, contextClass, required); if (context != null) return context; // keep looking } } return null; } /** * Associate given context with eObject using every interpreter which can handle the context. * @param eObject * @param context */ public void associateContext(EObject eObject, Object context) { if (context instanceof IProject) feature2IProject.put(eObject, (IProject) context); for (FSMLMappingInterpreter interpreter : mappingInterpreters) { if (interpreter.canHandleContext(context.getClass())) interpreter.associateContext(eObject, context); } } public void associateContext(int start, int end, EObject eObject) { for (FSMLMappingInterpreter interpreter : mappingInterpreters) { if (interpreter.canAssociateContext(eObject)) interpreter.associateContext(start,end,eObject); } } /** * @param element * @param progressMonitor * @return true iff element is correct * @throws JavaModelException * @throws FSMLMappingException */ public boolean reverseFeatureRepresentedAsClass(EObject element, IProgressMonitor progressMonitor) throws FSMLMappingException { // Stats.INSTANCE.printMessage("Reversing: " + element.eClass().getName()); for (Object aux : element.eClass().getEAllStructuralFeatures()) { EStructuralFeature feature = (EStructuralFeature) aux; boolean featureIsEssential = FSMLEcoreUtil.isEssential(feature); // break the loop is an essential feature was not found if (feature instanceof EAttribute) { if (!reverseFeatureRepresentedAsAttribute(element, (EAttribute) feature, progressMonitor)) { FSMLEcoreUtil.clearFeature(element, feature); if (featureIsEssential) break; } } else if (feature instanceof EReference && feature.getEAnnotation(Queries.BASE_CONCEPT) == null) { if (!reverseFeatureRepresentedAsReference(element, (EReference) feature, progressMonitor)) { FSMLEcoreUtil.clearFeature(element, feature); if (featureIsEssential) break; } } } // remove the element if essential feature missing if (!FSMLEcoreUtil.checkMandatory(element, true)) { removeContext(element); EcoreUtil.remove(element); return false; } // set the marker only if the child has not been removed for (FSMLMappingInterpreter interpreter : mappingInterpreters) { if (interpreter.canCreateMarkerForElementRepresentedAsClass(element.eClass())) interpreter.createMarkerForFeatureRepresentedAsClass(element); } return true; } public void removeContext(EObject element) { for (FSMLMappingInterpreter interpreter : mappingInterpreters) interpreter.removeContext(element); } /** * @return true iff feature is present * @throws FSMLMappingException */ public boolean reverseFeatureRepresentedAsAttribute(EObject element, EAttribute feature, IProgressMonitor progressMonitor) throws FSMLMappingException { // Stats.INSTANCE.printMessage("Reversing: " + element.eClass().getName() + "::" + feature.getName()); EAnnotation annotation = feature.getEAnnotation(CONSTRAINT_VALUE_OF); EAnnotation whereAnnotation = feature.getEAnnotation(CONSTRAINT_WHERE); if (annotation != null && whereAnnotation != null) { // value of this attribute is the same as a value of another one. // Example: // [0..1] name : String <valueOf attribute: input class: ActionDecl> <where attribute: type equalsTo: ../../qualifiedName> String detailAttribute = FSMLEcoreUtil.retrieveParameterValue(annotation, DETAIL_ATTRIBUTE, true); String detailClass = FSMLEcoreUtil.retrieveParameterValue(annotation, DETAIL_CLASS, true); EClass elementClass = FSMLEcoreUtil.getEClassByName(detailClass, element.eClass().getEPackage()); String detailWhereAttribute = FSMLEcoreUtil.retrieveParameterValue(whereAnnotation, DETAIL_ATTRIBUTE, true); String detailWhereEqualsTo = FSMLEcoreUtil.retrieveParameterValue(whereAnnotation, DETAIL_EQUALS_TO, true); NavigationResult requiredValueTarget = FSMLEcoreUtil.navigateToEObject(element, detailWhereEqualsTo); if (requiredValueTarget.errorMessage != null) throw new FSMLMappingException(Cause.MODEL_NAVIGATION_ERROR, feature, requiredValueTarget.errorMessage); Object requiredValue = requiredValueTarget.eObject.eGet(requiredValueTarget.eAttribute); // find potential targets Collection<EObject> targets = ItemPropertyDescriptor.getReachableObjectsOfType(element, elementClass); for (EObject target : targets) { EAttribute whereAttribute = (EAttribute) FSMLEcoreUtil.getStructuralFeatureByName(target.eClass(), detailWhereAttribute); Object whereAttributeValue = target.eGet(whereAttribute); if (FSMLEcoreUtil.valuesEqual(requiredValue, whereAttributeValue)) { EAttribute valueAttribute = (EAttribute) FSMLEcoreUtil.getStructuralFeatureByName(target.eClass(), detailAttribute); Object value = target.eGet(valueAttribute); element.eSet(feature, value); Stats.INSTANCE.logFeatureInstance(element, feature, annotation); return true; } } // did not find any matches return false; } annotation = feature.getEAnnotation(CONSTRAINT_CONCATENATE); if (annotation != null && feature.getEType().getName().equalsIgnoreCase("EString")){ String detailElement = FSMLEcoreUtil.retrieveParameterValue(annotation, DETAIL_ELEMENT, true); String detailString = FSMLEcoreUtil.retrieveParameterValue(annotation, DETAIL_STRING, true); String detailPosition = FSMLEcoreUtil.retrieveParameterValue(annotation, DETAIL_POSITION, false); NavigationResult requiredValueTarget = FSMLEcoreUtil.navigateToEObject(element, detailElement); if (requiredValueTarget.errorMessage != null) throw new FSMLMappingException(Cause.MODEL_NAVIGATION_ERROR, feature, requiredValueTarget.errorMessage); Object requiredValue = requiredValueTarget.eObject.eGet(requiredValueTarget.eAttribute); if (requiredValue instanceof String){ StringBuffer sb = new StringBuffer(); if (DETAIL_VALUE_BEFORE.equalsIgnoreCase(detailPosition)){ sb.append(detailString); sb.append(requiredValue.toString()); } else if (DETAIL_VALUE_AFTER.equalsIgnoreCase(detailPosition)){ sb.append(requiredValue.toString()); sb.append(detailString); } else return false; element.eSet(feature, sb.toString()); Stats.INSTANCE.logFeatureInstance(element, feature, annotation); return true; } else return false; } annotation = feature.getEAnnotation(CONSTRAINT_REQUIRES_EXCLUDES); if (annotation != null && feature.getEType().getName().equalsIgnoreCase("EBoolean")){ String detailFeature = FSMLEcoreUtil.retrieveParameterValue(annotation, DETAIL_FEATURE, true); String detailRequires = FSMLEcoreUtil.retrieveParameterValue(annotation, DETAIL_REQUIRES, false); String detailExcludes = FSMLEcoreUtil.retrieveParameterValue(annotation, DETAIL_EXCLUDES, false); if (detailRequires == null && detailExcludes == null || detailRequires != null && detailExcludes != null) throw new FSMLMappingException(Cause.MISSING_PARAMETER, feature, "Exactly one of " + DETAIL_REQUIRES + " or " + DETAIL_EXCLUDES + " is required"); NavigationResult requireingOrExcludingFeature = FSMLEcoreUtil.navigateToEObject(element, detailFeature); if (!requireingOrExcludingFeature.isPresent()) // the feature is not instantiated and therefore the implies/excludes constraint is satisfied (false -> x is true for every x) return true; if (detailRequires != null) { NavigationResult requiredFeature = FSMLEcoreUtil.navigateToEObject(element, detailRequires); // true if present return requiredFeature.isPresent(); } else { // detailExcludes must not be null NavigationResult excludedFeature = FSMLEcoreUtil.navigateToEObject(element, detailExcludes); // true if missing return !excludedFeature.isPresent(); } } // delegate to interpreters annotation = FSMLEcoreUtil.getQueryAnnotation(feature); if (annotation != null) { FSMLMappingInterpreter interpreter = query2interpreter.get(annotation.getSource()); if (interpreter != null) { return interpreter.reverseFeatureRepresentedAsAttribute(element, feature, progressMonitor); } else Stats.INSTANCE.logError("None of registered mapping interpreters supports annotation: " + annotation.getSource()); } // features without annotations should not influence the analysis (i.e., // are always present) return true; } /** * @return true iff feature is present * @throws FSMLMappingException */ public boolean reverseFeatureRepresentedAsReference(EObject element, EReference feature, IProgressMonitor progressMonitor) throws FSMLMappingException { // Stats.INSTANCE.printMessage("Reversing: " + element.eClass().getName() + "::" // + feature.getName()); if (!feature.isContainment()) return true; // clean the list only containment feature in order no to clean context // injection refs if (feature.isChangeable()) element.eUnset(feature); else // we assume that non-changeable features are always present return true; EAnnotation annotation = FSMLEcoreUtil.getQueryAnnotation(feature); Collection<EClass> concreteClasses = FSMLEcoreUtil.getSubclassesOfEClass(feature.getEReferenceType(), true); ConcreteTypeLoop: for (EClass concreteChildType : concreteClasses) { if (annotation != null) { FSMLMappingInterpreter interpreter = query2interpreter.get(annotation.getSource()); interpreter.reverseFeatureRepresentedAsReference(element, feature, concreteChildType, progressMonitor); continue ConcreteTypeLoop; } // so far, the feature did not have a supported annotation // children specified by default query { for (FSMLMappingInterpreter interpreter : mappingInterpreters) { if (interpreter.canCreateChildrenForDefaultQuery(concreteChildType)) { interpreter.createChildrenForDefaultQuery(element, feature, concreteChildType, progressMonitor); continue ConcreteTypeLoop; } } } // base concept references { EReference baseConceptReference = FSMLEcoreUtil.findBaseConceptReference(concreteChildType); if (baseConceptReference != null) { createChildrenForBaseConceptReference(element, feature, concreteChildType, baseConceptReference, progressMonitor); continue ConcreteTypeLoop; } } // no base reference, check for query features { FSMLMappingInterpreter interpreter = null; EStructuralFeature queryFeature = null; for (FSMLMappingInterpreter aux : mappingInterpreters) { queryFeature = aux.findQueryFeature(concreteChildType); if (queryFeature != null) { interpreter = aux; break; } else // reset the interpreter because it is not the correct // one interpreter = null; } if (queryFeature != null) { if (interpreter != null) interpreter.createChildrenForQueryFeature(element, feature, concreteChildType, queryFeature, progressMonitor); continue ConcreteTypeLoop; } } if (feature.isMany()) for (int i = 0; i < feature.getLowerBound(); i++) { EObject child = EcoreUtil.create(concreteChildType); ((EList) element.eGet(feature)).add(child); if (reverseFeatureRepresentedAsClass(child, progressMonitor)) Stats.INSTANCE.logFeatureInstance(element, feature, null); } else { EObject child = EcoreUtil.create(concreteChildType); element.eSet(feature, child); if (reverseFeatureRepresentedAsClass(child, progressMonitor)) Stats.INSTANCE.logFeatureInstance(element, feature, null); } } // features without annotations should not influence the analysis (i.e., // are always present) if (feature.getEAnnotations().isEmpty()) return true; if (feature.isMany()) return !((EList) element.eGet(feature)).isEmpty(); else return element.eGet(feature) != null; } /** * Incremental - recursion is controlled by the reconcile action * @throws FSMLMappingException */ public boolean forwardFeatureRepresentedAsClass(ClassSyncItem classSyncItem, IProgressMonitor progressMonitor) throws FSMLMappingException { EObject element = classSyncItem.getModel() != null ? classSyncItem.getModel() : classSyncItem.getCode(); EClass eClass = element.eClass(); EReference eReference = element.eContainmentFeature(); EReference baseReference = FSMLEcoreUtil.findBaseConceptReference(eClass); if (baseReference != null) return true; // forward specified by a default query or a context annotation { for (FSMLMappingInterpreter interpreter : mappingInterpreters) { boolean forwardUsingQuery = interpreter.canForwardObjectUsingQueryMapping(eReference); boolean forwardUsingContext = interpreter.canForwardObjectUsingContextMapping(eClass); // check the SUBSUMED_BY constraint if (forwardUsingQuery && FSMLEcoreUtil.isSubsumingFeaturePresent(element, eReference)) // abort if the subsuming feature is present continue; if(forwardUsingContext && FSMLEcoreUtil.isSubsumingFeaturePresent(element, eClass)) // abort if the subsuming feature is present continue; if (forwardUsingQuery && forwardUsingContext) { // at least one must succeed boolean querySuccess = interpreter.forwardFeatureRepresentedAsReference(classSyncItem, progressMonitor); boolean contextSuccess = interpreter.forwardFeatureRepresentedAsClass(classSyncItem, progressMonitor); return querySuccess || contextSuccess; } if (forwardUsingContext) return interpreter.forwardFeatureRepresentedAsClass(classSyncItem, progressMonitor); if (forwardUsingQuery) return interpreter.forwardFeatureRepresentedAsReference(classSyncItem, progressMonitor); } } return false; } /** * Incremental - recursion is controlled by reconcile action * @throws FSMLMappingException */ public boolean forwardFeatureRepresentedAsAttribute(StructuralFeatureSyncItem featureSyncItem, IProgressMonitor progressMonitor) throws FSMLMappingException { EAnnotation annotation = FSMLEcoreUtil.getQueryAnnotation(featureSyncItem.getStructuralFeature()); if (annotation != null) { FSMLMappingInterpreter interpreter = query2interpreter.get(annotation.getSource()); if (interpreter != null) return interpreter.forwardFeatureRepresentedAsAttribute(featureSyncItem, progressMonitor); else Stats.INSTANCE.logError("None of the registered mapping interpreters supports annotation: " + annotation.getSource()); } return false; } /** * Incremental - recursion is controlled by reconcile action */ public boolean forwardFeatureRepresentedAsReference(EObject modelObject, EReference feature, RA action, IProgressMonitor progressMonitor) { // a containment reference is never processed directly because each of its children is processed recursively // a non-containment reference is ignored during forward engineering since it expresses a referential integrity constraint // a base element reference is ignored during forward engineering because forward for its target is already performed return true; } }