/******************************************************************************
* Copyright (c) 2007,2010 E.D.Willink 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:
* E.D.Willink - initial API and implementation
*
* </copyright>
*
* $Id: CSTFileEcoreEnvironment.java,v 1.2 2010/04/08 06:26:21 ewillink Exp $
*/
package org.eclipse.ocl.examples.parser.environment.ecore;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.EnvironmentFactory;
import org.eclipse.ocl.TypeResolver;
import org.eclipse.ocl.cst.CSTNode;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.EcoreEnvironment;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.ecore.EcorePackage;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.ecore.internal.OCLFactoryImpl;
import org.eclipse.ocl.ecore.internal.OCLStandardLibraryImpl;
import org.eclipse.ocl.ecore.internal.UMLReflectionImpl;
import org.eclipse.ocl.examples.modelregistry.environment.FileHandle;
import org.eclipse.ocl.examples.parser.environment.CSTFileEnvironment;
import org.eclipse.ocl.expressions.ExpressionsPackage;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.expressions.impl.ExpressionsPackageImpl;
import org.eclipse.ocl.parser.AbstractOCLAnalyzer;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.types.TypesPackage;
import org.eclipse.ocl.utilities.OCLFactory;
import org.eclipse.ocl.utilities.UMLReflection;
import org.eclipse.ocl.utilities.UtilitiesPackage;
@SuppressWarnings("restriction") // FIXME Eliminate @SuppressWarnings("restriction")
public abstract class CSTFileEcoreEnvironment<RV extends ICSTRootEcoreEnvironment, V extends ICSTNodeEcoreEnvironment, CST extends CSTNode>
extends CSTFileEnvironment<EPackage, EClassifier, EOperation, EStructuralFeature,
EEnumLiteral, EParameter,
EObject, CallOperationAction, SendSignalAction, Constraint,
EClass, EObject, RV, V, CST> implements ICSTFileEcoreEnvironment
{
// FIXME This is a public duplicate of EcoreEnvironment.OCL_PACKAGES
public static final Map<List<String>, EPackage> OCL_PACKAGES =
new java.util.HashMap<List<String>, EPackage>();
static {
List<String> names = new java.util.ArrayList<String>();
names.add(ExpressionsPackageImpl.OCL_ROOT_PACKAGE.getName());
OCL_PACKAGES.put(names, ExpressionsPackageImpl.OCL_ROOT_PACKAGE);
names = new java.util.ArrayList<String>(names);
names.add(ExpressionsPackage.eINSTANCE.getName());
OCL_PACKAGES.put(names, ExpressionsPackage.eINSTANCE);
names = new java.util.ArrayList<String>(names);
names.set(1, TypesPackage.eINSTANCE.getName());
OCL_PACKAGES.put(names, TypesPackage.eINSTANCE);
names = new java.util.ArrayList<String>(names);
names.set(1, UtilitiesPackage.eINSTANCE.getName());
OCL_PACKAGES.put(names, UtilitiesPackage.eINSTANCE);
names = new java.util.ArrayList<String>(names);
names.set(1, EcorePackage.eINSTANCE.getName());
OCL_PACKAGES.put(names, EcorePackage.eINSTANCE);
}
private EnvironmentFactory<
EPackage, EClassifier, EOperation, EStructuralFeature,
EEnumLiteral, EParameter,
EObject, CallOperationAction, SendSignalAction, Constraint,
EClass, EObject>
factory;
// private TypeResolver<EClassifier, EOperation, EStructuralFeature> typeResolver;
protected CSTFileEcoreEnvironment(FileHandle file, ResourceSet resourceSet, XMIResource astResource) {
super(file, resourceSet, astResource);
}
/**
* Implemented by subclasses to find all states in the specified owner type
* that match the given path name prefix and add them to the accumulator
* list. The default implementation does nothing, as Ecore does not model
* states.
* <p>
* Implementors must only provide the states defined directly in the
* namespace indicated by the path prefix (i.e., only one level).
* </p>
*
* @param owner the owner type
* @param pathPrefix partial qualified name, specifying the parent of the
* states to be collection
* @param states a list of states directly owned by the namespace indicated
* by path prefix, within the owner type
*
* @see #getStates(EClassifier, List)
*/
protected void collectStates(EClassifier owner, List<String> pathPrefix, List<EObject> states) {
// do nothing
}
// implements the inherited specification
// @Override
public EnvironmentFactory<
EPackage, EClassifier, EOperation, EStructuralFeature,
EEnumLiteral, EParameter,
EObject, CallOperationAction, SendSignalAction, Constraint,
EClass, EObject>
createFactory() {
EPackage.Registry registry = getRegistry();
if (registry == EPackage.Registry.INSTANCE) {
return EcoreEnvironmentFactory.INSTANCE;
} else {
return new EcoreEnvironmentFactory(registry);
}
}
// @Override
@SuppressWarnings("deprecation")
protected TypeResolver<EClassifier, EOperation, EStructuralFeature> createTypeResolver() {
return new EcoreTypeResolverImpl(this, ast);
}
@Deprecated
@Override
protected TypeResolver<EClassifier, EOperation, EStructuralFeature> createTypeResolver(Resource resource) {
return new EcoreTypeResolverImpl(this, resource);
}
// @Override
protected UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> createUMLReflection() {
return UMLReflectionImpl.INSTANCE;
}
public EStructuralFeature defineAttribute(
EClassifier owner,
org.eclipse.ocl.expressions.Variable<
EClassifier, EParameter> variable,
Constraint constraint) {
EStructuralFeature result;
String name = variable.getName();
EClassifier type = variable.getType();
if (type instanceof EClass) {
result = EcoreFactory.eINSTANCE.createEReference();
} else {
result = EcoreFactory.eINSTANCE.createEAttribute();
}
result.setName(name);
result.setEType(type);
Constraint existing = getDefinition(result);
if (existing != null) {
// replace existing definition
EcoreUtil.replace(existing, constraint);
} else {
EAnnotation ann = result.getEAnnotation(Environment.OCL_NAMESPACE_URI);
if (ann == null) {
ann = EcoreFactory.eINSTANCE.createEAnnotation();
ann.setSource(Environment.OCL_NAMESPACE_URI);
result.getEAnnotations().add(ann);
}
ann.getContents().add(constraint);
}
addHelperProperty(owner, result);
return result;
}
public EOperation defineOperation(EClassifier owner, String name,
EClassifier type,
List<org.eclipse.ocl.expressions.Variable<
EClassifier, EParameter>> params,
Constraint constraint) {
EOperation result = EcoreFactory.eINSTANCE.createEOperation();
result.setName(name);
result.setEType((type == null) ? getOCLStandardLibrary().getOclVoid() :
type);
for (Variable<EClassifier, EParameter> next : params) {
EParameter param = EcoreFactory.eINSTANCE.createEParameter();
param.setName(next.getName());
param.setEType((next.getType() == null)?
getOCLStandardLibrary().getOclVoid() : next.getType());
result.getEParameters().add(param);
}
Constraint existing = getDefinition(result);
if (existing != null) {
// replace existing definition
EcoreUtil.replace(existing, constraint);
} else {
EAnnotation ann = result.getEAnnotation(Environment.OCL_NAMESPACE_URI);
if (ann == null) {
ann = EcoreFactory.eINSTANCE.createEAnnotation();
ann.setSource(Environment.OCL_NAMESPACE_URI);
result.getEAnnotations().add(ann);
}
ann.getContents().add(constraint);
}
addHelperOperation(owner, result);
return result;
}
// @Override
public EClassifier getClassifier(EPackage pkg, String name) {
EClassifier result = pkg.getEClassifier(name);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
// try the unescaped name
result = pkg.getEClassifier(AbstractOCLAnalyzer.unescape(name));
}
return result;
}
public Constraint getDefinition(Object feature) {
Constraint result = null;
ETypedElement typedFeature = (ETypedElement) feature;
EAnnotation ann = typedFeature.getEAnnotation(
Environment.OCL_NAMESPACE_URI);
if ((ann != null) && !ann.getContents().isEmpty()) {
for (EObject o : ann.getContents()) {
if ((o instanceof Constraint)
&& UMLReflection.DEFINITION.equals(((Constraint) o).getStereotype())) {
result = (Constraint) o;
break;
}
}
}
return result;
}
/**
* Foreign method for {@link EPackage#getEClassifier(String)} that accounts
* for possibility of underscore-escaped names.
*
* @param pkg
* a package
* @param name
* a possibly underscore-escaped name of a nested classifier
*
* @return the matching classifier, or <code>null</code> if none
*/
@Deprecated // use getClassifier
public EClassifier getEClassifier(EPackage pkg, String name) {
return getClassifier(pkg, name);
}
public EnvironmentFactory<EPackage, EClassifier, EOperation, EStructuralFeature,
EEnumLiteral, EParameter,EObject, CallOperationAction, SendSignalAction, Constraint,
EClass, EObject> getFactory() {
if (factory == null) {
// obtain a reasonable default factory
if (registry == EPackage.Registry.INSTANCE) {
factory = EcoreEnvironmentFactory.INSTANCE;
} else {
factory = new EcoreEnvironmentFactory(registry);
}
}
return factory;
}
// @Override
public EPackage getNestedPackage(EPackage pkg, String name) {
for (EPackage sub : pkg.getESubpackages()) {
if (name.equals(sub.getName())) {
return sub;
}
}
if (AbstractOCLAnalyzer.isEscaped(name)) {
// try the unescaped name
name = AbstractOCLAnalyzer.unescape(name);
for (EPackage sub : pkg.getESubpackages()) {
if (name.equals(sub.getName())) {
return sub;
}
}
}
return null;
}
public OCLFactory getOCLFactory() {
return OCLFactoryImpl.INSTANCE;
}
public OCLStandardLibrary<EClassifier> getOCLStandardLibrary() {
return OCLStandardLibraryImpl.INSTANCE;
}
/**
* Obtains the states matching the specified path prefix in the owner type
* by trying the {@link #collectStates} method on it and, recursively, its
* supertypes to find all matches. For implicit (<code>null</code>) owners,
* looks up the innermost-scoped variable as the implicit source and tries
* again on this variable's type.
* <p>
* To extend this implementation, override the
* {@link #collectStates} method.
* </p>
*/
public List<EObject> getStates(EClassifier owner, List<String> pathPrefix) {
EList<EObject> result = new BasicEList<EObject>();
collectStates(owner, pathPrefix, result);
if (owner instanceof EClass) {
// search supertypes
for (EClass superclass : ((EClass) owner).getEAllSuperTypes()) {
collectStates(superclass, pathPrefix, result);
}
}
return result;
}
public UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> getUMLReflection() {
return UMLReflectionImpl.INSTANCE;
}
public boolean isInPostcondition(
org.eclipse.ocl.expressions.OCLExpression<EClassifier> exp) {
Constraint constraint = null;
EObject parent = exp;
while (parent != null) {
if (parent instanceof Constraint) {
constraint = (Constraint) parent;
break;
}
parent = parent.eContainer();
}
return (constraint != null)
&& UMLReflection.POSTCONDITION.equals(constraint.getStereotype());
}
public EClassifier lookupClassifier(List<String> names) {
return lookupClassifier(getContextPackage(), names);
}
public EClassifier lookupClassifier(EPackage contextPackage, List<String> names) {
EPackage pkg = null;
EPackage currPkg = contextPackage;
UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> reflection = getUMLReflection();
if (names.size() > 1) {
List<String> lookup = names;
// Check whether this package is in the default package
if (currPkg != null) {
while (currPkg != null) {
pkg = currPkg;
for (int i = 0; i < lookup.size() - 1; i++) {
String name = lookup.get(i);
pkg = getNestedPackage(pkg, name);
if (pkg == null) {
break;
}
}
if (pkg != null) {
return getClassifier(pkg, lookup.get(lookup.size() - 1));
}
if ((currPkg == contextPackage) && (lookup.size() > 1)
&& isNamed(currPkg, lookup.get(0))) {
// handle the case where the first part of the qualified
// name matches the context package name
lookup = lookup.subList(1, lookup.size());
} else {
lookup = names;
currPkg = reflection.getNestingPackage(currPkg);
}
}
}
// Check whether this package exists
List<String> newNames = names.subList(0, names.size() - 1);
EPackage.Registry registry = getRegistry();
pkg = EcoreEnvironment.findPackage(newNames, registry);
if (pkg == null) {
return null;
}
return getClassifier(pkg, names.get(names.size() - 1));
} else if (contextPackage != null) {
String name = names.get(0);
EClassifier result = null;
while (currPkg != null) {
result = getClassifier(currPkg, name);
if (result != null) {
return result;
}
currPkg = reflection.getNestingPackage(currPkg);
}
}
return null;
}
@Override
protected boolean isNamed(EObject element, String name) {
return (element instanceof ENamedElement)
&& AbstractOCLAnalyzer.equalName(name, ((ENamedElement)element).getName());
}
/**
* {@inheritDoc}
* <p>
* Implements the inherited specification by looking up the qualified name
* in my package registry.
* </p>
*/
public EPackage lookupPackage(List<String> path) {
if (!path.isEmpty() && OCL_PACKAGES.containsKey(path)) {
return OCL_PACKAGES.get(path);
}
EPackage pkg = null;
EPackage currPkg = getContextPackage();
// UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> reflection = getUMLReflection();
// Check whether this package is in the default package
if (currPkg != null) {
List<String> lookup = path;
while (currPkg != null) {
pkg = currPkg;
for (int i = 0; i < lookup.size(); i++) {
String name = lookup.get(i);
pkg = getNestedPackage(pkg, name);
if (pkg == null) {
break;
}
}
if (pkg != null) {
return pkg;
}
if ((currPkg == getContextPackage()) && (lookup.size() > 0)
&& isNamed(currPkg, lookup.get(0))) {
// handle the case where the first part of the qualified
// name matches the context package name
lookup = lookup.subList(1, lookup.size());
} else {
lookup = path;
currPkg = currPkg.getESuperPackage();
}
}
}
// Check whether this package exists in the global package registry
EPackage.Registry registry = getRegistry();
return EcoreEnvironment.findPackage(path, registry);
}
// implements the inherited specification
public void undefine(Object feature) {
Constraint definition = getDefinition(feature);
if (definition == null) {
throw new IllegalArgumentException(
"not an additional feature: " + feature); //$NON-NLS-1$
}
EcoreUtil.remove((EObject) feature);
EcoreUtil.remove(definition);
definition.getConstrainedElements().clear();
}
}