blob: f99cc54f8195197e310c2e401634cb0eefd21bd6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2018 Borland Software Corporation 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:
* Borland Software Corporation - initial API and implementation
* Christopher Gerking - bug 254962
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.stdlib;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.m2m.internal.qvt.oml.ast.env.InternalEvaluationEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.env.ModelParameterExtent;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEvaluationEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.ExecutableXMIHelper;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalUtil;
import org.eclipse.m2m.internal.qvt.oml.evaluator.EvaluationUtil;
import org.eclipse.m2m.internal.qvt.oml.evaluator.ModuleInstance;
import org.eclipse.m2m.internal.qvt.oml.expressions.ContextualProperty;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModuleImport;
import org.eclipse.m2m.qvt.oml.util.Dictionary;
import org.eclipse.ocl.ecore.CollectionType;
import org.eclipse.ocl.ecore.TupleType;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.util.Tuple;
import org.eclipse.ocl.util.TypeUtil;
public class ElementOperations extends AbstractContextualOperations {
static final String CONTAINER_NAME = "container"; //$NON-NLS-1$
static final String DEEP_CLONE_NAME = "deepclone"; //$NON-NLS-1$
static final String CLONE_NAME = "clone"; //$NON-NLS-1$
static final String METACLASS_NAME = "metaClassName"; //$NON-NLS-1$
static final String SUBOBJECTS_NAME = "subobjects"; //$NON-NLS-1$
static final String ALL_SUBOBJECTS_NAME = "allSubobjects"; //$NON-NLS-1$
static final String SUBOBJECTS_OF_KIND_NAME = "subobjectsOfKind"; //$NON-NLS-1$
static final String ALL_SUBOBJECTS_OF_KIND_NAME = "allSubobjectsOfKind"; //$NON-NLS-1$
static final String SUBOBJECTS_OF_TYPE_NAME = "subobjectsOfType"; //$NON-NLS-1$
static final String ALL_SUBOBJECTS_OF_TYPE_NAME = "allSubobjectsOfType"; //$NON-NLS-1$
public ElementOperations(AbstractQVTStdlib library) {
super(library, library.getElementType());
}
@Override
protected OperationProvider[] getOperations() {
OCLStandardLibrary<EClassifier> oclStdlib = getStdlib().getOCLStdLib();
EClassifier setOfElement = TypeUtil.resolveSetType(getStdlib().getEnvironment(), getStdlib().getElementType());
return new OwnedOperationProvider[] {
new OwnedOperationProvider(UNSUPPORTED_OPER, "_localId", oclStdlib.getString()), //$NON-NLS-1$
new OwnedOperationProvider(UNSUPPORTED_OPER, "_globalId", oclStdlib.getString()), //$NON-NLS-1$
new OwnedOperationProvider(ALL_SUBOBJECTS, ALL_SUBOBJECTS_NAME, setOfElement),
new OwnedOperationProvider(ALL_SUBOBJECTS_OF_TYPE, ALL_SUBOBJECTS_OF_TYPE_NAME,
new String[] { "type" }, oclStdlib.getSet(), oclStdlib.getOclType()), //$NON-NLS-1$
new OwnedOperationProvider(ALL_SUBOBJECTS_OF_KIND, ALL_SUBOBJECTS_OF_KIND_NAME,
new String[] { "type" }, oclStdlib.getSet(), oclStdlib.getOclType()), //$NON-NLS-1$
new OwnedOperationProvider(CLONE, CLONE_NAME, oclStdlib.getT()),
new OwnedOperationProvider(CONTAINER, CONTAINER_NAME, getStdlib().getElementType()),
new OwnedOperationProvider(DEEP_CLONE, DEEP_CLONE_NAME, oclStdlib.getT()),
new OwnedOperationProvider(UNSUPPORTED_OPER, "markedAs", //$NON-NLS-1$
new String[] { "value" }, oclStdlib.getBoolean(), oclStdlib.getString()), //$NON-NLS-1$
new OwnedOperationProvider(UNSUPPORTED_OPER, "markValue", getStdlib().getObject()), //$NON-NLS-1$
new OwnedOperationProvider(METACLASS, METACLASS_NAME, oclStdlib.getString()),
new OwnedOperationProvider(UNSUPPORTED_OPER, "stereotypedBy", //$NON-NLS-1$
new String[] { "value" }, oclStdlib.getBoolean(), oclStdlib.getString()), //$NON-NLS-1$
new OwnedOperationProvider(UNSUPPORTED_OPER, "stereotypedStrictBy", //$NON-NLS-1$
new String[] { "value" }, oclStdlib.getBoolean(), oclStdlib.getString()), //$NON-NLS-1$
new OwnedOperationProvider(SUBOBJECTS, SUBOBJECTS_NAME, setOfElement),
new OwnedOperationProvider(SUBOBJECTS_OF_TYPE, SUBOBJECTS_OF_TYPE_NAME,
new String[] { "type" }, oclStdlib.getSet(), oclStdlib.getOclType()), //$NON-NLS-1$
new OwnedOperationProvider(SUBOBJECTS_OF_KIND, SUBOBJECTS_OF_KIND_NAME,
new String[] { "type" }, oclStdlib.getSet(), oclStdlib.getOclType()), //$NON-NLS-1$
};
}
static final int FILTER_ALL = 0;
static final int FILTER_OF_TYPE = 1;
static final int FILTER_OF_KIND = 2;
private static final CallHandler DEEP_CLONE = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, final QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
EcoreUtil.Copier copier = new IntermediatePropertyCopier(evalEnv);
ExecutableXMIHelper.fixEObjectOnSave(eObject);
EObject result = copier.copy(eObject);
copier.copyReferences();
ExecutableXMIHelper.fixEObjectOnLoad(result);
ModelParameterExtent extent = evalEnv.getDefaultInstantiationExtent(eObject.eClass());
if(extent != null) {
//269245: Deep clone causes breaking containment hierarchy if used with resource bound extent
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=269245
// add only the top level container object
extent.addObject(result);
}
return result;
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
private static final CallHandler CLONE = new CallHandler() {
@SuppressWarnings("serial")
public Object invoke(ModuleInstance module, Object source, Object[] args, final QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
EcoreUtil.Copier copier = new IntermediatePropertyCopier(evalEnv) {
@Override
protected void copyContainment(org.eclipse.emf.ecore.EReference arg0, EObject arg1, EObject arg2) {
// make a shallow copy
}
};
EObject result = copier.copy(eObject);
copier.copyReferences();
ModelParameterExtent extent = evalEnv.getDefaultInstantiationExtent(eObject.eClass());
if(extent != null) {
TreeIterator<EObject> contentIter = EcoreUtil.getAllProperContents(result, false);
while (contentIter.hasNext()) {
extent.addObject(contentIter.next());
}
extent.addObject(result);
}
return result;
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
private static class IntermediatePropertyCopier extends EcoreUtil.Copier {
private static final long serialVersionUID = 1L;
private final QvtOperationalEvaluationEnv evalEnv;
private final Map<Object, Object> qvtoObjectMap;
public IntermediatePropertyCopier(QvtOperationalEvaluationEnv evalEnv) {
this.evalEnv = evalEnv;
qvtoObjectMap = new IdentityHashMap<Object, Object>();
}
@Override
public void copyReferences() {
super.copyReferences();
Module rootModule = evalEnv.getRoot().getAdapter(InternalEvaluationEnv.class).getCurrentModule().getModule();
// bug 254962: clone intermediate properties
for (Map.Entry<EObject, EObject> entry : entrySet()) {
EObject eObject = entry.getKey();
EObject copyEObject = entry.getValue();
cloneIntermediateProperties(eObject, copyEObject, rootModule);
}
}
private void cloneIntermediateProperties(EObject source, EObject target, Module module) {
for (EStructuralFeature feature : module.getEAllStructuralFeatures()) {
if (feature instanceof ContextualProperty) {
ContextualProperty property = (ContextualProperty) feature;
if (property.getContext().isSuperTypeOf(source.eClass())) {
Object value = evalEnv.navigateProperty(property, null, source);
Object result = get(value);
if (result == null) {
if ((property.getEType() instanceof CollectionType && value instanceof Collection<?>)
|| (property.getEType() instanceof TupleType && value instanceof Tuple<?, ?>)) {
result = cloneOclObjects(value);
}
}
if (useOriginalReferences && result == null) {
result = value;
}
evalEnv.callSetter(target, property, result, QvtOperationalUtil.isUndefined(result, evalEnv), true);
}
}
}
for (ModuleImport moduleImport : module.getModuleImport()) {
Module importedModule = moduleImport.getImportedModule();
cloneIntermediateProperties(source, target, importedModule);
}
}
private Object cloneOclObjects(Object value) {
if (value instanceof Dictionary<?, ?>) {
Object result = qvtoObjectMap.get(value);
if (result == null) {
result = cloneDictionary((Dictionary<?,?>) value);
qvtoObjectMap.put(value, result);
}
return result;
}
else if (value instanceof Collection<?>) {
Object result = qvtoObjectMap.get(value);
if (result == null) {
result = cloneCollection((Collection<?>) value);
qvtoObjectMap.put(value, result);
}
return result;
}
else if (value instanceof Tuple<?, ?>) {
Object result = qvtoObjectMap.get(value);
if (result == null) {
result = cloneTuple((Tuple<?, ?>) value);
qvtoObjectMap.put(value, result);
}
return result;
}
Object result = get(value);
if (useOriginalReferences && result == null) {
result = value;
}
return result;
}
@SuppressWarnings("unchecked")
private <T> Collection<T> cloneCollection(Collection<T> collection) {
Collection<T> resultCollection = EvaluationUtil.createNewCollectionOfSameKind(collection);
for (T object : collection) {
resultCollection.add((T) cloneOclObjects(object));
}
return resultCollection;
}
@SuppressWarnings("unchecked")
private <T1,T2> Dictionary<T1,T2> cloneDictionary(Dictionary<T1, T2> dictionary) {
Dictionary<T1, T2> resultDict = (Dictionary<T1, T2>) EvaluationUtil.createNewCollectionOfSameKind(dictionary);
for (T1 key : dictionary.keys()) {
T2 value = dictionary.get(key);
resultDict.put((T1) cloneOclObjects(key), (T2) cloneOclObjects(value));
}
return resultDict;
}
@SuppressWarnings("unchecked")
private <T1,T2> Tuple<T1,T2> cloneTuple(Tuple<T1, T2> value) {
Map<T2, Object> propertyValues = new HashMap<T2, Object>();
org.eclipse.ocl.types.TupleType<T1, T2> tupleType = value.getTupleType();
for (T2 part : tupleType.oclProperties()) {
propertyValues.put(part, cloneOclObjects(value.getValue(part)));
}
return (Tuple<T1, T2>) evalEnv.createTuple((EClassifier) tupleType, (Map<EStructuralFeature, Object>) propertyValues);
}
}
private static final CallHandler CONTAINER = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
return eObject.eContainer();
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
private static final CallHandler METACLASS = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
return eObject.eClass().getName();
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
private static final CallHandler SUBOBJECTS = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
return getSubObjects(eObject, null, FILTER_ALL, evalEnv);
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
private static final CallHandler SUBOBJECTS_OF_TYPE = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
return getSubObjects(eObject, getTypeFilterArg(args), FILTER_OF_TYPE, evalEnv);
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
private static final CallHandler SUBOBJECTS_OF_KIND = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
return getSubObjects(eObject, getTypeFilterArg(args), FILTER_OF_KIND, evalEnv);
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
private static final CallHandler ALL_SUBOBJECTS = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
return getAllSubObjects(eObject, null, FILTER_ALL, evalEnv);
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
private static final CallHandler ALL_SUBOBJECTS_OF_TYPE = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
EClassifier type = getTypeFilterArg(args);
return getAllSubObjects(eObject, type, FILTER_OF_TYPE, evalEnv);
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
static final CallHandler ALL_SUBOBJECTS_OF_KIND = new CallHandler() {
public Object invoke(ModuleInstance module, Object source, Object[] args, QvtOperationalEvaluationEnv evalEnv) {
if(source instanceof EObject) {
EObject eObject = (EObject) source;
EClassifier type = getTypeFilterArg(args);
return getAllSubObjects(eObject, type, FILTER_OF_KIND, evalEnv);
}
return CallHandlerAdapter.getInvalidResult(evalEnv);
}
};
static EClassifier getTypeFilterArg(Object[] args) {
if(args.length != 1) {
throw new IllegalArgumentException();
}
Object typeObj = args[0];
if(typeObj instanceof EClassifier == false) {
throw new IllegalArgumentException();
}
return (EClassifier) typeObj;
}
private static Object getAllSubObjects(EObject eObject, EClassifier type, final int filterFlag, QvtOperationalEvaluationEnv evalEnv) {
HashSet<EObject> result = new HashSet<EObject>();
for(TreeIterator<EObject> iter = EcoreUtil.getAllContents(eObject, false); iter.hasNext(); ) {
EObject subObject = iter.next();
boolean accept = filterFlag == FILTER_ALL;
if(FILTER_OF_KIND == filterFlag) {
accept = evalEnv.isKindOf(subObject, type);
} else if(FILTER_OF_TYPE == filterFlag) {
accept = evalEnv.isTypeOf(subObject, type);
}
if(accept) {
result.add(subObject);
}
}
return result;
}
private static Object getSubObjects(EObject eObject, EClassifier type, final int filterFlag, QvtOperationalEvaluationEnv evalEnv) {
HashSet<EObject> result = new HashSet<EObject>();
for(EObject subObject : eObject.eContents()) {
boolean accept = filterFlag == FILTER_ALL;
if(FILTER_OF_KIND == filterFlag) {
accept = evalEnv.isKindOf(subObject, type);
} else if(FILTER_OF_TYPE == filterFlag) {
accept = evalEnv.isTypeOf(subObject, type);
}
if(accept) {
result.add(subObject);
}
}
return result;
}
}