blob: 9ffb9dab3699d2277bc6b8f8c0f4cfefdc4f1963 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012,2013 The University of York, Willink Transformations and others.
* 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:
* Horacio Hoyos - initial API and implementation
******************************************************************************/
package org.eclipse.qvtd.pivot.qvtimperative.evaluation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
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.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.PivotPackage;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.evaluation.ModelManager;
import org.eclipse.ocl.pivot.ids.IdResolver;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.MetamodelManager;
import org.eclipse.ocl.pivot.utilities.ParserException;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.evaluation.TransformationInstance;
import org.eclipse.qvtd.pivot.qvtbase.evaluation.TypedModelInstance;
/**
* QVTc Domain Manager is the class responsible for managing the QVTc virtual
* machine meta-models and models.
* A QVTc Domain Manager object encapsulates the domain information need to
* modify the domains's models.
*/
public class QVTiModelManager implements ModelManager
{
protected final @NonNull QVTiTransformationAnalysis transformationAnalysis;
protected final @NonNull MetamodelManager metamodelManager;
// TODO how to manage aliases?
/** Map a typed model to its resource (model). */
private @NonNull Map<TypedModel, Resource> modelResourceMap = new HashMap<TypedModel, Resource>();
private @NonNull Map<TypedModel, List<EObject>> modelElementsMap = new HashMap<TypedModel, List<EObject>>();
/**
* The types upon which execution of the transformation may invoke allInstances().
*/
private @NonNull Set<org.eclipse.ocl.pivot.Class> allInstancesClasses;
/**
* 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.
*/
private @NonNull Map<?, ?> unnavigableOpposites[];
/**
* Instantiates a new QVTi Domain Manager. Responsible for creating new
* instances of the middle model and the middle model EFactory.
*/
public QVTiModelManager(@NonNull QVTiTransformationAnalysis transformationAnalysis) {
this.transformationAnalysis = transformationAnalysis;
this.metamodelManager = transformationAnalysis.getMetamodelManager();
this.allInstancesClasses = transformationAnalysis.getAllInstancesClasses();
int cacheIndexes = transformationAnalysis.getCacheIndexes();
this.unnavigableOpposites = new Map<?, ?>[cacheIndexes];
for (int i = 0; i < cacheIndexes; i++) {
this.unnavigableOpposites[i] = new HashMap<Object, Object>();
}
}
/**
* Adds the model to the list of models managed by this domain manager. The
* domain manager supports only one root resource per typed model, this means that
* if a model was already binded to the TypedModel it will be replaced.
*
* @param typedModel the type model associated to the model
* @param model the resource
*/
// TODO support multiple model instances by alias
public void addModel(@NonNull TypedModel typedModel, @NonNull Resource model) {
modelResourceMap.put(typedModel, model);
}
/**
* Adds the model element to the resource of the given TypeModel
*
* @param tm the TypeModel
* @param element the element
*/
public void addModelElement(@NonNull TypedModel model, @NonNull Object element) {
List<EObject> elements = modelElementsMap.get(model);
if (elements == null) {
elements = new ArrayList<EObject>(modelResourceMap.get(model).getContents());
modelElementsMap.put(model, elements);
}
elements.add((EObject) element);
}
/**
* Dispose.
*/
public void dispose() {
modelElementsMap.clear();
modelResourceMap.clear();
allInstancesClasses.clear();
for (Map<?, ?> unnavigableOpposite : unnavigableOpposites) {
unnavigableOpposite.clear();
}
}
@Override
public @NonNull Set<EObject> get(@NonNull org.eclipse.ocl.pivot.Class type) {
Set<EObject> elements = new HashSet<EObject>();
// Find the typed model for the type
org.eclipse.ocl.pivot.Package p = type.getOwningPackage();
for (TypedModel d : modelResourceMap.keySet()) {
for (org.eclipse.ocl.pivot.Package up : d.getUsedPackage()) {
if (up.equals(p)) {
for (Object o : getElementsByType(d, type)) {
elements.add((EObject) o);
}
}
}
}
return elements;
}
public @NonNull MetamodelManager getMetamodelManager() {
return metamodelManager;
}
/**
* Gets the model (resource) for a given TypedModel.
*
* @param typedModel the typed model
* @return the resource
*/
public Resource getModel(@NonNull TypedModel typedModel) {
return modelResourceMap.get(typedModel);
}
/**
* Gets the resources for all the models.
*
* @return a collection of all the resources
*/
public Collection<Resource> getAllModelResources() {
return modelResourceMap.values();
}
/**
* Gets the all the instances of the specified Type in the given TypeModel
*
* @param tm the TypeModel (can be null if it is the middle model)
* @param type the type of the elements that are retrieved
* @return the instances
*/
public List<Object> getElementsByType(@Nullable TypedModel model, @NonNull Type type) {
List<Object> elements = new ArrayList<Object>();
// Is the TypedModel the middle or output, hence we have elements in the elementsMap
if (modelElementsMap.containsKey(model)) {
for (EObject root : modelElementsMap.get(model)) {
if (root != null) {
//if (root.eClass().getName().equals(type.getName())) {
if (isInstance(type, root)) {
elements.add(root);
}
for (TreeIterator<EObject> contents = root.eAllContents(); contents
.hasNext();) {
EObject element = contents.next();
if ((element != null) && isInstance(type, element)) {
// if (((EClass) type.getETarget()).getName().equals(element.eClass().getName())) {
elements.add(element);
}
}
}
}
}
else {
for (TreeIterator<EObject> contents = modelResourceMap.get(model).getAllContents(); contents.hasNext();) {
EObject element = contents.next();
//System.out.println(type.getETarget());
//System.out.println(((EClassifier) type.getETarget()).getName());
//System.out.println(object.eClass().getName());
if ((element != null) && isInstance(type, element)) {
// if (((EClass) type.getETarget()).getName().equals(element.eClass().getName())) {
elements.add(element);
}
}
}
return elements;
}
/**
* 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);
}
/**
* Implemented by subclasses to determine whether the specified element
* is an instance of the specified class, according to the metamodel
* semantics implemented by the environment that created this extent map.
*
* @param type the type
* @param element a potential run-time (M0) instance of that class
* @return <code>true</code> if this element is an instance of the given
* class; <code>false</code> otherwise
*/
protected boolean isInstance(@NonNull Type requiredType, @NonNull EObject eObject) {
EClass eClass = eObject.eClass();
EPackage ePackage = eClass.getEPackage();
Type objectType = null;
if (ePackage == PivotPackage.eINSTANCE) {
String name = ClassUtil.nonNullEMF(eClass.getName());
objectType = metamodelManager.getASClass(name);
}
else {
try {
objectType = metamodelManager.getASOf(Type.class, eClass);
} catch (ParserException e) {
// FIXME if (!generatedErrorMessage) {
// generatedErrorMessage = true;
// logger.error("Failed to load an '" + eClass.getName() + "'", e);
// }
}
}
return (objectType != null) && objectType.conformsTo(metamodelManager.getStandardLibrary(), requiredType);
}
public @NonNull QVTiTransformationAnalysis getTransformationAnalysis() {
return transformationAnalysis;
}
public List<EObject> getTypeModelEObjectList(TypedModel model) {
if (modelElementsMap.containsKey(model)) {
return modelElementsMap.get(model);
} else {
return new ArrayList<EObject>();
}
}
/**
* Saves all the models managed by the domain manager
*/
public void saveModels() {
this.saveModels(null);
}
/**
* Saves all the models managed by the domain manager using the provided (optional)
* saving options.
*/
public void saveModels(@Nullable Map<?, ?> savingOptions) {
for (Map.Entry<TypedModel, Resource> entry : modelResourceMap.entrySet()) {
Resource model = entry.getValue();
TypedModel key = entry.getKey();
if (modelElementsMap.containsKey(key)) { // Only save modified models
// Move elements without container to the resource contents
for (EObject e : modelElementsMap.get(key)) {
if (e.eContainer() == null) {
model.getContents().add(e);
}
}
}
}
for (Map.Entry<TypedModel, Resource> entry : modelResourceMap.entrySet()) {
Resource model = entry.getValue();
TypedModel key = entry.getKey();
if (modelElementsMap.containsKey(key)) { // Only save modified models
try{
model.save(savingOptions);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void saveMiddleModel(@NonNull URI uri) {
this.saveMiddleModel(uri, null);
}
public void saveMiddleModel(@NonNull URI uri, Map<?, ?> savingOptions) {
// TODO
/* Resource r = metamodelManager.getExternalResourceSet().createResource(uri);
for (EObject e : modelElementsMap.get(MIDDLE_MODEL)) {
if (e.eContainer() == null) {
r.getContents().add(e);
}
}
try{
Map<Object, Object> options = new HashMap<Object, Object>();
options.put(XMLResource.OPTION_SCHEMA_LOCATION, Boolean.TRUE);
r.save(options);
} catch (IOException e) {
e.printStackTrace();
} */
}
/**
* 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 implements TransformationInstance
{
protected final @NonNull QVTiModelManager modelManager;
protected final @NonNull Transformation transformation;
public QVTiTransformationInstance(@NonNull QVTiModelManager modelManager, @NonNull Transformation transformation) {
this.modelManager = modelManager;
this.transformation = transformation;
}
}
public static class QVTiTypedModelInstance implements TypedModelInstance
{
protected final @NonNull QVTiModelManager modelManager;
protected final @NonNull TypedModel typedModel;
public QVTiTypedModelInstance(@NonNull QVTiModelManager modelManager, @NonNull TypedModel typedModel) {
this.modelManager = modelManager;
this.typedModel = typedModel;
}
@Override
public @NonNull Set<EObject> getAllObjects() {
throw new UnsupportedOperationException();
}
@Override
public @NonNull Set<EObject> getObjectsOfKind(@NonNull org.eclipse.ocl.pivot.Class type) {
IdResolver idResolver = modelManager.getMetamodelManager().getEnvironmentFactory().getIdResolver();
Set<EObject> results = new HashSet<EObject>();
Set<?> instances = modelManager.get(type);
for (Object instance : instances) {
if (instance != null) {
results.add((EObject) idResolver.ecoreValueOf(null, instance)); // FIXME Move to model manager
}
}
return results;
}
@Override
public @NonNull Set<EObject> getObjectsOfType(@NonNull org.eclipse.ocl.pivot.Class type) {
throw new UnsupportedOperationException();
}
@Override
public @NonNull Collection<EObject> getRootObjects() {
throw new UnsupportedOperationException();
}
}
public Object getTransformationInstance(@NonNull Transformation transformation) {
return new QVTiTransformationInstance(this, transformation);
}
public Object getTypedModelInstance(@NonNull TypedModel typedModel) {
return new QVTiTypedModelInstance(this, typedModel);
}
}