blob: c29f7d9b310eec2e57d77f6defb9b7887074c233 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2019 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - initial API and implementation (inspired by Horacio Hoyos' prototype)
******************************************************************************/
package org.eclipse.qvtd.pivot.qvtimperative.evaluation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.ids.ClassId;
import org.eclipse.ocl.pivot.ids.IdManager;
import org.eclipse.ocl.pivot.ids.PropertyId;
import org.eclipse.ocl.pivot.ids.TypeId;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal.EnvironmentFactoryInternalExtension;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.MetamodelManager;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtimperative.EntryPoint;
import org.eclipse.qvtd.pivot.qvtimperative.ImperativeTransformation;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;
import org.eclipse.qvtd.runtime.evaluation.AbstractModelsManager;
import org.eclipse.qvtd.runtime.evaluation.AbstractTransformationInstance;
import org.eclipse.qvtd.runtime.evaluation.AbstractTypedModelInstance;
import org.eclipse.qvtd.runtime.evaluation.TransformationInstance;
import org.eclipse.qvtd.runtime.evaluation.TypedModelInstance;
import org.eclipse.qvtd.runtime.qvtruntimelibrary.QVTruntimeLibraryPackage;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
/**
* QVTiModelsManager manager the source, middle and target models during a QVTi transformation.
*
* FIXME The runtime ModelsManager is able to addInputURI without knowing the EntryPoint. It would be nice if this
* intrerpreted variant was the same.
*/
public class QVTiModelsManager extends AbstractModelsManager
{
@SuppressWarnings("null")
public static final @NonNull ClassId EXTENT_CLASSID = IdManager.getClassId(QVTruntimeLibraryPackage.Literals.EXTENT);
protected final @NonNull EntryPoint entryPoint;
protected final @NonNull EntryPointsAnalysis entryPointsAnalysis;
protected final @NonNull EnvironmentFactoryInternalExtension environmentFactory;
/**
* Array of caches for the un-navigable opposite of each used property.
* The array index is allocated by the QVTiTransformationAnalysis; it identifies the property
* of interest. Each cache is from the sourceObject to the un-navigable targetObject.
* <p>
* Keys and values cannot be null, since null cannot participate in a 'bidirectional' relationship.
*/
private final @NonNull Map<?, ?> unnavigableOpposites @NonNull [];
/**
* The run-time instance of the transformation.
*/
private /*@LazyNonNull*/ TransformationInstance transformationInstance = null;
/**
* The run-time instance of each TypedModel.
*
* More than four typedModelInstances is very unlikely so use of a Map to accelerate lookup is unjustified.
*/
private @NonNull List<@NonNull QVTiTypedModelInstance> typedModelInstances = new ArrayList<>();
/**
* Instantiates a new QVTi Domain Manager. Responsible for creating new
* instances of the middle model and the middle model EFactory.
*/
public QVTiModelsManager(@NonNull EntryPointAnalysis entryPointAnalysis) {
this.entryPoint = entryPointAnalysis.getEntryPoint();
this.entryPointsAnalysis = entryPointAnalysis.getEntryPointsAnalysis();
this.environmentFactory = (EnvironmentFactoryInternalExtension) entryPointsAnalysis.getEnvironmentFactory();
// this.allInstancesClasses = entryPointsAnalysis.getAllInstancesClasses();
int cacheIndexes = entryPointsAnalysis.getCacheIndexes();
this.unnavigableOpposites = new @NonNull Map<?, ?>[cacheIndexes];
for (int i = 0; i < cacheIndexes; i++) {
this.unnavigableOpposites[i] = new HashMap<>();
}
@NonNull PropertyId @NonNull [] propertyIndex2propertyId = entryPointsAnalysis.getPropertyIndex2propertyId();
for (@NonNull TypedModel typedModel : QVTimperativeUtil.getModelParameters(entryPointsAnalysis.getTransformation())) {
QVTiTypedModelInstance typedModelInstance = createTypedModelInstance(typedModel);
initTypedModelInstance(typedModelInstance);
}
initOpposites(propertyIndex2propertyId);
Set<@NonNull CompleteClass> allInstancesCompleteClasses = entryPointAnalysis.getAllInstancesCompleteClasses();
for (@NonNull QVTiTypedModelInstance typedModelInstance : getTypedModelInstances()) {
TypedModel typedModel = typedModelInstance.getTypedModel();
Set<@NonNull CompleteClass> usedCompleteClasses = new HashSet<>();
for (org.eclipse.ocl.pivot.@NonNull Class usedClass : QVTimperativeUtil.getUsedClasses(typedModel)) {
usedCompleteClasses.add(environmentFactory.getCompleteModel().getCompleteClass(usedClass));
}
usedCompleteClasses.retainAll(allInstancesCompleteClasses);
TypedModelAnalysis typedModelAnalysis = new TypedModelAnalysis(entryPointsAnalysis, typedModel, usedCompleteClasses);
@NonNull ClassId @NonNull [] classIndex2classId = typedModelAnalysis.getClassIndex2ClassId();
int @Nullable [] @NonNull [] classIndex2allClassIndexes = typedModelAnalysis.getClassIndex2allClassIndexes();
typedModelInstance.initClassIds(classIndex2classId, classIndex2allClassIndexes);
int extentClassIndex = typedModelAnalysis.getExtentClassIndex();
if (extentClassIndex >= 0) {
Map<@NonNull Object, Object> extentOpposites = getExtentOpposites();
typedModelInstance.initExtent(extentClassIndex, extentOpposites);
}
// typedModelAnalysis.toString();
}
}
public @Nullable Resource addInputURI(@NonNull String modelName, @NonNull URI modelURI) {
ResourceSet resourceSet = environmentFactory.getResourceSet(); // FIXME get package registrations in exteranl RespurcSet
PivotUtil.initializeLoadOptionsToSupportSelfReferences(resourceSet);
Resource inputResource = ClassUtil.nonNullState(resourceSet.getResource(modelURI, true));
TypedModelInstance typedModelInstance = getTypedModelInstance(modelName);
typedModelInstance.addInputResource(inputResource);
return inputResource;
}
public @NonNull Resource addOutputURI(@NonNull String modelName, @NonNull URI modelURI) {
ResourceSet resourceSet;
if (PivotUtilInternal.isASURI(modelURI)) {
resourceSet = environmentFactory.getMetamodelManager().getASResourceSet(); // Need PivotSave to allocate xmi:ids
}
else {
resourceSet = environmentFactory.getResourceSet();
}
TypedModelInstance typedModelInstance = getTypedModelInstance(modelName);
Resource outputResource = ClassUtil.nonNullState(resourceSet.createResource(modelURI));
typedModelInstance.addOutputResource(outputResource);
return outputResource;
}
protected @NonNull QVTiTypedModelInstance createTypedModelInstance(@NonNull TypedModel typedModel) {
return new QVTiTypedModelInstance(this, typedModel);
}
/**
* Dispose.
*/
public void dispose() {
for (@NonNull QVTiTypedModelInstance typedModelInstance : typedModelInstances) {
typedModelInstance.dispose();
}
for (Map<?, ?> unnavigableOpposite : unnavigableOpposites) {
unnavigableOpposite.clear();
}
}
/**
* The inherited 'allInstances' behavior is implemented as an accumulation of all instances from all input models.
*/
public @NonNull Set<@NonNull Object> get(org.eclipse.ocl.pivot.@NonNull Class type) {
Set<@NonNull Object> elements = new HashSet<>();
for (@NonNull TypedModel typedModel : QVTimperativeUtil.getInputTypedModels(entryPoint)) {
TypedModelInstance typedModelInstance = getTypedModelInstance(typedModel);
Iterables.addAll(elements, typedModelInstance.getObjectsOfKind(type));
}
return elements;
}
// private @NonNull EntryPoint getEntryPoint() {
// return getEntryPointAnalysis().getEntryPoint();
// }
// public @NonNull EntryPointAnalysis getEntryPointAnalysis() {
// if (entryPointAnalysis == null) {
// ImperativeTransformation transformation = entryPointsAnalysis.getTransformation();
// initEntryPoint(QVTimperativeUtil.getDefaultEntryPoint(transformation));
// }
// return ClassUtil.nonNullState(entryPointAnalysis);
// }
public @Nullable Map<@NonNull Object, Object> getExtentOpposites() {
int propertyIndex = getOppositePropertyIndex(QVTruntimeLibraryPackage.Literals.EXTENT__ELEMENTS);
return (Map<@NonNull Object, Object>)unnavigableOpposites[propertyIndex];
}
public @NonNull MetamodelManager getMetamodelManager() {
return environmentFactory.getMetamodelManager();
}
/**
* Gets the model (resource) for a given TypedModel.
*
* @param typedModel the typed model
* @return the resource
*/
// FIXME Change API for multiple extents
public Resource getModel(@NonNull TypedModel typedModel) {
QVTiTypedModelInstance typedModelInstance = getTypedModelInstance(typedModel);
return typedModelInstance.getModel();
}
public @NonNull Iterable<@NonNull Object> getOpposite(@NonNull Property target2sourceProperty, @NonNull Object sourceObject) {
// return modelsManager.getOpposite(target2sourceProperty, sourceObject);
throw new UnsupportedOperationException();
}
public @NonNull EntryPointsAnalysis getTransformationAnalysis() {
return entryPointsAnalysis;
}
/* public List<EObject> getTypeModelEObjectList(@NonNull ImperativeTypedModel model) {
if (modelElementsMap.containsKey(model)) {
return modelElementsMap.get(model);
} else {
return new ArrayList<>();
}
} */
public @NonNull TransformationInstance getTransformationInstance(@NonNull ImperativeTransformation transformation) {
TransformationInstance transformationInstance2 = transformationInstance;
if (transformationInstance2 == null) {
transformationInstance = transformationInstance2 = new QVTiTransformationInstance(this, transformation);
}
return transformationInstance2;
}
public @NonNull QVTiTypedModelInstance getTypedModelInstance(@NonNull TypedModel typedModel) {
String modelName = typedModel.getName();
assert modelName != null;
return getTypedModelInstance(modelName);
}
@Override
public @NonNull QVTiTypedModelInstance getTypedModelInstance(@NonNull String modelName) {
for (@NonNull QVTiTypedModelInstance typedModelInstance : typedModelInstances) {
if (modelName.equals(typedModelInstance.getName())) {
return typedModelInstance;
}
}
throw new IllegalStateException("Unknown model name '" + modelName + "'");
}
@Override
public @NonNull Iterable<@NonNull QVTiTypedModelInstance> getTypedModelInstances() {
return typedModelInstances;
}
// public @Nullable ImperativeTypedModel getTypedModel(@NonNull Resource resource) {
// return resource2typedModel.get(resource);
// }
/**
* Return the target object of the unnavigable property, associated with cacheIndex, navigation from sourceObject.
*/
public Object getUnnavigableOpposite(@NonNull Integer cacheIndex, @NonNull Object sourceObject) {
return unnavigableOpposites[cacheIndex].get(sourceObject);
}
public void initTypedModelInstance(/*int i,*/@NonNull QVTiTypedModelInstance model) {
typedModelInstances.add(model);
}
/**
* Register targetObject as the target of the unnavigable property, associated with cacheIndex, navigation from sourceObject.
*/
@SuppressWarnings("unchecked")
public void setUnnavigableOpposite(@NonNull Integer cacheIndex, @NonNull Object targetObject, Object sourceObject) {
((Map<Object, Object>)unnavigableOpposites[cacheIndex]).put(sourceObject, targetObject);
}
public static class QVTiTransformationInstance extends AbstractTransformationInstance
{
protected final @NonNull QVTiModelsManager modelManager;
protected final @NonNull ImperativeTransformation transformation;
public QVTiTransformationInstance(@NonNull QVTiModelsManager modelManager, @NonNull ImperativeTransformation transformation) {
this.modelManager = modelManager;
this.transformation = transformation;
}
public @NonNull QVTiModelsManager getModelManager() {
return modelManager;
}
@Override
public @Nullable String getName() {
return transformation.getName();
}
public @NonNull ImperativeTransformation getTransformation() {
return transformation;
}
}
public static class QVTiTypedModelInstance extends AbstractTypedModelInstance // FIXME reimplement using CG variant
{
protected final @NonNull EnvironmentFactoryInternalExtension environmentFactory;
protected final @NonNull TypedModel typedModel;
private @Nullable EClass extentEClass = null;
public QVTiTypedModelInstance(@NonNull QVTiModelsManager modelsManager, @NonNull TypedModel typedModel) {
super(modelsManager, QVTimperativeUtil.getName(typedModel));
this.environmentFactory = modelsManager.environmentFactory;
this.typedModel = typedModel;
}
@Override
protected void addExtent() {
EClass extentEClass2 = extentEClass;
assert extentEClass2 != null;
EObject extent = extentEClass2.getEPackage().getEFactoryInstance().create(extentEClass2);
EStructuralFeature elementsFeature = extentEClass2.getEStructuralFeature(QVTruntimeLibraryPackage.Literals.EXTENT__ELEMENTS.getFeatureID());
assert elementsFeature != null;
@SuppressWarnings("unchecked")
List<Object> elements = (List<Object>) extent.eGet(elementsFeature);
assert elements != null;
addExtent(extent, elements);
}
public void addInputResource(@NonNull URI modelURI, @Nullable String contentType) {
Resource resource;
ResourceSet resourceSet = environmentFactory.getResourceSet();
if (contentType == null) {
resource = resourceSet.getResource(modelURI, true);
}
else {
resource = resourceSet.createResource(modelURI, contentType);
try {
resource.load(null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (resource != null) {
addInputResource(resource);
}
}
public void addOutputResource(@NonNull URI modelURI, @Nullable String contentType) {
Resource resource = environmentFactory.getResourceSet().createResource(modelURI, null);
if (resource != null) {
addOutputResource(resource);
}
}
/**
* Return the elements of a root Extent object, iff rootObject is an Extent.
* Return null otherwise.
*/
@Override
protected @Nullable Iterable<@NonNull EObject> getExtentElements(@NonNull EObject rootObject) {
EClass eClass = rootObject.eClass();
assert eClass != null;
TypeId classId = IdManager.getClassId(eClass);
if (classId != EXTENT_CLASSID) {
return null;
}
EStructuralFeature elementsFeature = eClass.getEStructuralFeature(QVTruntimeLibraryPackage.Literals.EXTENT__ELEMENTS.getFeatureID());
assert elementsFeature != null;
@SuppressWarnings("unchecked")
List<@NonNull EObject> elements = (List<@NonNull EObject>) rootObject.eGet(elementsFeature);
return elements;
}
/**
* Gets the model (resource) for a given TypedModel.
*
* @param typedModel the typed model
* @return the resource
*/
// FIXME Change API for multiple extents
public @Nullable Resource getModel() {
List<@NonNull Resource> inputResources = basicGetInputResources();
return (inputResources != null) && !inputResources.isEmpty() ? inputResources.get(0) : null;
}
public @NonNull QVTiModelsManager getModelManager() {
return (QVTiModelsManager) modelsManager;
}
public @NonNull TypedModel getTypedModel() {
return typedModel;
}
@Override
public <K,V> void initExtent(int extentClassIndex, @Nullable Map<K, V> extentOpposites) {
super.initExtent(extentClassIndex, extentOpposites);
TypedModel typedModel = getTypedModel();
Set<org.eclipse.ocl.pivot.@NonNull Class> usedClasses = Sets.newHashSet(QVTimperativeUtil.getUsedClasses(typedModel));
ClassId[] classIndex2classId2 = classIndex2classId;
assert classIndex2classId2 != null;
ClassId extentClassId = classIndex2classId2[extentClassIndex];
for (org.eclipse.ocl.pivot.@NonNull Class usedClass : usedClasses) {
if (usedClass.getTypeId() == extentClassId) {
extentEClass = (EClass) usedClass.getESObject();
break;
}
}
assert extentEClass != null;
}
}
}