/******************************************************************************* * Copyright (c) 2007, 2015 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 v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Borland Software Corporation - initial API and implementation * Christopher Gerking - bug 388801 * Alex Paperno - bug 419299 *******************************************************************************/ package org.eclipse.m2m.internal.qvt.oml.ast.parser; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EFactory; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.emf.ecore.impl.DynamicEObjectImpl; import org.eclipse.emf.ecore.impl.EFactoryImpl; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalStdLibrary; import org.eclipse.m2m.internal.qvt.oml.cst.adapters.AbstractGenericAdapter; import org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil; import org.eclipse.m2m.internal.qvt.oml.evaluator.EvaluationUtil; import org.eclipse.m2m.internal.qvt.oml.evaluator.QvtOperationalEvaluationVisitor; import org.eclipse.m2m.internal.qvt.oml.evaluator.QvtRuntimeException; import org.eclipse.m2m.internal.qvt.oml.expressions.ExpressionsFactory; import org.eclipse.m2m.internal.qvt.oml.expressions.ExpressionsPackage; import org.eclipse.m2m.internal.qvt.oml.expressions.ModelType; import org.eclipse.m2m.internal.qvt.oml.expressions.Module; import org.eclipse.m2m.internal.qvt.oml.expressions.OperationalTransformation; import org.eclipse.m2m.internal.qvt.oml.stdlib.model.ExceptionInstance; import org.eclipse.m2m.qvt.oml.ExecutionStackTraceElement; import org.eclipse.ocl.expressions.OCLExpression; /** * @author sboyko * */ public class IntermediateClassFactory extends EFactoryImpl { public static IntermediateClassFactory getFactory(Module module) { for (EPackage subPackage : module.getESubpackages()) { // should be fast we will always have a single and the only // intermediate package if(isIntermediatePackage(subPackage)) { EFactory eFactory = subPackage.getEFactoryInstance(); if(eFactory.getClass().equals(IntermediateClassFactory.class)) { return (IntermediateClassFactory) eFactory; } } } return null; } public IntermediateClassFactory(EPackage intermediatePackage) { if(isIntermediatePackage(intermediatePackage) == false) { throw new IllegalArgumentException("Requires intermediate package"); //$NON-NLS-1$ } myModule = (Module) intermediatePackage.getESuperPackage(); myIntermediatePackage = intermediatePackage; myIntermediatePackage.setEFactoryInstance(this); EClassifier intermModuleType = myModule.getEClassifier(INTERMEDIATE_MODELTYPE_NAME); if(intermModuleType instanceof ModelType == false) { throw new IllegalArgumentException("Intermediate model type must exist"); //$NON-NLS-1$ } myIntermediateModelType = (ModelType) intermModuleType; } public IntermediateClassFactory(Module module) { super(); myModule = module; /* * 8.2.1.3 Module The model type package is also named '_INTERMEDIATE'. * This package is nested by the transformation (by means of the * inherited Package::nestedPackage property). */ myIntermediatePackage = EcoreFactory.eINSTANCE.createEPackage(); myIntermediatePackage.setEFactoryInstance(this); myIntermediatePackage.setName(INTERMEDIATE_MODELTYPE_NAME); myIntermediatePackage.setNsPrefix(INTERMEDIATE_PACKAGE_NAME); myIntermediatePackage.setNsURI(ExpressionsPackage.eNS_URI + "/" + INTERMEDIATE_MODELTYPE_NAME); //$NON-NLS-1$ module.getESubpackages().add(myIntermediatePackage); /* * 8.2.1.3 Module If the module contains the declaration of intermediate * classes (see OperationalTransformation::intermediateClass definition) * a model type named '_INTERMEDIATE' is automatically defined and * inserted in the list of owned types. */ myIntermediateModelType = ExpressionsFactory.eINSTANCE .createModelType(); myIntermediateModelType.setName(INTERMEDIATE_MODELTYPE_NAME); myIntermediateModelType.getMetamodel().add(myIntermediatePackage); // metamodel: // Package // [1..*] // {ordered} module.getUsedModelType().add(myIntermediateModelType); // usedModelType // : ModelType // [0..*] // {ordered} module.getEClassifiers().add(myIntermediateModelType); // /ownedType : // Type [0..*] // (from // Package) // {composes,ordered} myIntermediateModelType.getMetamodel().add(myIntermediatePackage); // No need to use a metamodel adapter, the package is referenced by the // modeltype directly // ModelTypeMetamodelsAdapter.addMetamodel(myIntermediateModelType, // myIntermediatePackage); } @Override public EObject create(EClass class_) { // return super.create(class_); if (myClassifierInitializations.get(class_) == null) { cacheClassifierInitExp(class_); } if (EmfUtil.isAssignableFrom(QvtOperationalStdLibrary.INSTANCE.getExceptionClass(), class_)) { return new ExceptionClassInstance(class_); } else { return new IntermediateClassInstance(class_); } } public EClass createIntermediateClassifier() { EClass eClassifier = EcoreFactory.eINSTANCE.createEClass(); /* * 8.2.1.3 Module Associations /ownedType : Type [0..] (from Package) * {composes,ordered} * * All the types being defined by this module. Specifically this * includes the model types, locally defined classes, and any composite * type used to define the type of a variable or a parameter - for * instance a 'Set(MyMetaclass)' user-defined datatype. */ // myModule.getEClassifiers().add(eClassifier); myIntermediatePackage.getEClassifiers().add(eClassifier); /* * 8.2.1.1 OperationalTransformation Associations intermediateClass : * Class [] {ordered} * * The classes that are defined explicitly by the transformation writer * to contain structured intermediate data used for the purpose of the * transformation. These intermediate classes are to be distinguished * from the trace classes that are implicitly and automatically derived * from the relations. Instances of intermediate classes do not survive * the execution of the transformation, except for ensuring trace * persistence. */ if (myModule instanceof OperationalTransformation) { ((OperationalTransformation) myModule).getIntermediateClass().add( eClassifier); } return eClassifier; } public ModelType getIntermediateModelType() { return myIntermediateModelType; } public static boolean isIntermediateModelType(ModelType modelType) { return modelType.eContainer() instanceof OperationalTransformation && INTERMEDIATE_MODELTYPE_NAME.equals(modelType.getName()); } public static boolean isIntermediateClass(EClassifier class_) { if (class_ == null) { return false; } EPackage ePackage = class_.getEPackage(); return ePackage != null && isIntermediatePackage(ePackage); } public static boolean isIntermediatePackage(EPackage ePackage) { return INTERMEDIATE_MODELTYPE_NAME.equals(ePackage.getName()) && ePackage.getESuperPackage() instanceof Module; } public void cacheClassifierInitExp(EClass clazz) { Map<EStructuralFeature, OCLExpression<EClassifier>> clsFeatures = myClassifierInitializations .get(clazz); if (clsFeatures == null) { // Remark: follow the declaration order during feature // initialization clsFeatures = new LinkedHashMap<EStructuralFeature, OCLExpression<EClassifier>>( 2); myClassifierInitializations.put(clazz, clsFeatures); } for (EStructuralFeature next : clazz.getEAllStructuralFeatures()) { OCLExpression<EClassifier> initExp = QvtOperationalParserUtil .getInitExpression(next); if (initExp != null) { clsFeatures.put(next, initExp); } } } public void doInstancePropertyInit(Object instance, QvtOperationalEvaluationVisitor evalVisitor) { if (false == instance instanceof EObject) { return; } EObject eInstance = (EObject) instance; EClass intermediateClass = eInstance.eClass(); Map<EStructuralFeature, OCLExpression<EClassifier>> clsFeatures = myClassifierInitializations .get(intermediateClass); if (clsFeatures == null) { return; } for (EStructuralFeature eFeature : intermediateClass.getEAllStructuralFeatures()) { IntermediateStaticFieldAdapter adapter = (IntermediateStaticFieldAdapter) EcoreUtil .getAdapter(eFeature.eAdapters(), IntermediateStaticFieldAdapter.class); if (adapter != null && adapter.isInitialized()) { continue; } OCLExpression<EClassifier> expression = clsFeatures .get(eFeature); Object evalResult = expression != null ? evalVisitor.visitExpression(expression) : null; if (evalResult == null) { // no init expression specified for a single-valued feature, or init expression evaluated to null EClassifier featureType = evalVisitor.getEnvironment().getUMLReflection().getOCLType(eFeature); evalResult = EvaluationUtil.createInitialValue(featureType, evalVisitor.getEnvironment().getOCLStandardLibrary(), evalVisitor.getEvaluationEnvironment()); } // temporary switch off read-only property boolean isChangeable = eFeature.isChangeable(); eFeature.setChangeable(true); boolean isUndefined = QvtOperationalUtil.isUndefined(evalResult, evalVisitor.getEvaluationEnvironment()); evalVisitor.getOperationalEvaluationEnv().callSetter(eInstance, eFeature, evalResult, isUndefined, true); eFeature.setChangeable(isChangeable); } } public static void markFeatureAsStatic(EStructuralFeature eFeature) { IntermediateStaticFieldAdapter adapter = (IntermediateStaticFieldAdapter) EcoreUtil .getAdapter(eFeature.eAdapters(), IntermediateStaticFieldAdapter.class); if (adapter == null) { adapter = new IntermediateStaticFieldAdapter(); eFeature.eAdapters().add(adapter); } } public static boolean isFeatureStatic(EStructuralFeature eFeature) { return EcoreUtil.getAdapter(eFeature.eAdapters(), IntermediateStaticFieldAdapter.class) != null; } private static class IntermediateClassInstance extends DynamicEObjectImpl { IntermediateClassInstance(EClass eClass) { super(eClass); } @Override public Object eGet(EStructuralFeature feature, boolean resolve, boolean coreType) { IntermediateStaticFieldAdapter adapter = (IntermediateStaticFieldAdapter) EcoreUtil .getAdapter(feature.eAdapters(), IntermediateStaticFieldAdapter.class); if (adapter != null) { return adapter.eGet(feature); } return super.eGet(feature, resolve, coreType); } @Override public void eSet(EStructuralFeature feature, Object newValue) { IntermediateStaticFieldAdapter adapter = (IntermediateStaticFieldAdapter) EcoreUtil .getAdapter(feature.eAdapters(), IntermediateStaticFieldAdapter.class); if (adapter != null) { adapter.eSet(feature, newValue); } super.eSet(feature, newValue); } @Override public String toString() { return eClass().getName(); } } public static class ExceptionClassInstance extends IntermediateClassInstance implements ExceptionInstance { private String argument; private List<? extends ExecutionStackTraceElement> stackElements; public ExceptionClassInstance(EClass eClass) { super(eClass); } public String getArgument() { return argument; } public void setArgument(String argumentNew) { argument = argumentNew; } public void setStackElements(List<? extends ExecutionStackTraceElement> stackElementsNew) { stackElements = stackElementsNew; } public List<? extends ExecutionStackTraceElement> getStackElements() { return (stackElements != null) ? stackElements : Collections.<ExecutionStackTraceElement>emptyList(); } @Override public String toString() { StringWriter contents = new StringWriter(); contents.write(eClass().getName()); if(getArgument() != null) { contents.write(" : "); //$NON-NLS-1$ contents.write(argument); contents.write(System.getProperty("line.separator")); //$NON-NLS-1$ } PrintWriter pw = new PrintWriter(contents); QvtRuntimeException.printQvtStackTrace(pw, getStackElements()); return contents.toString(); } } private static class IntermediateStaticFieldAdapter extends AbstractGenericAdapter<IntermediateStaticFieldAdapter> { private IntermediateStaticFieldAdapter() { super(); } public Object eGet(EStructuralFeature eFeature) { return myValue; } public void eSet(EStructuralFeature eFeature, Object value) { myValue = value; myIsInitialized = true; } public boolean isInitialized() { return myIsInitialized; } public boolean isAdapterForType(Object type) { return IntermediateStaticFieldAdapter.class == type; } @Override public boolean equals(Object obj) { return obj instanceof IntermediateStaticFieldAdapter; } @Override public int hashCode() { return IntermediateStaticFieldAdapter.class.hashCode(); } private Object myValue = null; private boolean myIsInitialized = false; } private final EPackage myIntermediatePackage; private final Module myModule; private final ModelType myIntermediateModelType; private final Map<EClass, Map<EStructuralFeature, OCLExpression<EClassifier>>> myClassifierInitializations = new HashMap<EClass, Map<EStructuralFeature, OCLExpression<EClassifier>>>(); private static final String INTERMEDIATE_MODELTYPE_NAME = "_INTERMEDIATE"; //$NON-NLS-1$ private static final String INTERMEDIATE_PACKAGE_NAME = INTERMEDIATE_MODELTYPE_NAME.toLowerCase(); }