blob: be3e9faa7317148a1b991940a42ca1771f51ed5e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2018 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:
* C.Damus, K.Hussey, E.D.Willink - Initial API and implementation
* E.D.Willink - Bug 360072
*******************************************************************************/
package org.eclipse.ocl.ecore.delegate;
import java.lang.reflect.Constructor;
import org.eclipse.emf.common.EMFPlugin;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.QueryDelegate;
import org.eclipse.ocl.EnvironmentFactory;
import org.eclipse.ocl.ParserException;
import org.eclipse.ocl.common.OCLCommon;
import org.eclipse.ocl.common.OCLConstants;
import org.eclipse.ocl.common.delegate.DelegateResourceSetAdapter;
import org.eclipse.ocl.common.delegate.VirtualDelegateMapping;
import org.eclipse.ocl.common.internal.delegate.OCLInvocationDelegateMapping;
import org.eclipse.ocl.common.internal.delegate.OCLQueryDelegateMapping;
import org.eclipse.ocl.common.internal.delegate.OCLSettingDelegateMapping;
import org.eclipse.ocl.common.internal.delegate.OCLValidationDelegateMapping;
import org.eclipse.ocl.common.internal.options.CommonOptions;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.ecore.OCL;
import org.eclipse.ocl.ecore.OppositePropertyCallExp;
import org.eclipse.ocl.ecore.opposites.EcoreEnvironmentFactoryWithHiddenOpposites;
/**
* An implementation of a delegate domain for an OCL enhanced package. The domain
* maintains an OCL facade to be shared by all delegates within the package.
*
* @since 3.0
*/
public class OCLDelegateDomain implements DelegateDomain
{
/**
* The EAnnotation source URI for delegate OCL annotations.
* <p>
* For an EOperation, the EAnnotation details may include
* <br>
* a <tt>body</tt> key to provide an OCL expression value that specifies <tt>body:</tt> of the operation.
* <br>
* a <tt>precondition</tt> key to provide an OCL expression value that specifies <tt>pre:</tt> for the operation.
* <br>
* a <tt>postcondition</tt> key to provide an OCL expression value that specifies <tt>post:</tt> for the operation.
* <p>
* For an EStructuralFeature, the EAnnotation details may include
* <br>
* a <tt>derivation</tt> key to provide an OCL expression value that specifies <tt>derive:</tt> for the property.
* <br>
* a <tt>initial</tt> key to provide an OCL expression value that specifies <tt>initial:</tt> for the operation.
* <p>
* For an EClassifier (EClass, EDataType), the EAnnotation details may include
* <br>
* a <tt><i>constraintName</i></tt> key to provide an OCL expression value that specifies <tt>inv <i>constraintName</i>:</tt> for the classifier.
* <p>
* For an EPackage, the EAnnotation details may include
* <br>
* an {@link #KEY_FOR_ENVIRONMENT_FACTORY_CLASS environmentFactoryClass} key whose value is the fully qualified
* class name for the {@link EnvironmentFactory}. If no key is specified either the {@link EcoreEnvironmentFactory}
* or {@link EcoreEnvironmentFactoryWithHiddenOpposites} class are used.
* <br>
* a {@link #OCL_DELEGATES_USE_HIDDEN_OPPOSITES_KEY hiddenOpposites} key that may have a <tt>true</tt> value to
* use the {@link EcoreEnvironmentFactoryWithHiddenOpposites} class rather than the {@link EcoreEnvironmentFactory}
* when no {@link #KEY_FOR_ENVIRONMENT_FACTORY_CLASS environmentFactoryClass} key is specified.
* <p>
* Note that the delegate OCL functionality must be enabled by an EPackage Ecore annotation specifying this URI
* as the value of <tt>invocationDelegates</tt>, <tt>settingDelegates</tt> and <tt>validationDelegates</tt> details
* keys.
* <p>
* Note also that validation must be enabled by specifying an EClassifier Ecore annotation with a space separated list
* of invariant <tt><i>constraintName</i></tt>s as the value of the <tt>constraints</tt> details key.
* <p>
* See <tt>/org.eclipse.ocl.ecore.tests/model/Company.ecore</tt> or <tt>http://wiki.eclipse.org/MDT/OCLinEcore</tt> for an example.
*
* @deprecated use OCLConstants.OCL_DELEGATE_URI
*/
@Deprecated
public static final String OCL_DELEGATE_URI = OCLConstants.OCL_DELEGATE_URI;
/**
* If the EPackage annotation with source {@link #OCL_DELEGATE_URI} has a detail using this key,
* the value is the fully qualified name of the class to be used as the {@link EnvironmentFactory}
* for parsing and evaluation for OCL constraints defined in the EPackage. The class must have a
* a constructor that takes a single {@link org.eclipse.emf.ecore.EPackage.Registry EPackage.Registry} argument.
*
* @since 3.1
*/
public static final String KEY_FOR_ENVIRONMENT_FACTORY_CLASS = "environmentFactoryClass"; //$NON-NLS-1$
/**
* If the EPackage annotation with source {@link #OCL_DELEGATE_URI} has a detail using this key with a
* value of "true", the {@link EcoreEnvironmentFactoryWithHiddenOpposites} is used instead of
* the default {@link EcoreEnvironmentFactory}, making the OCL environment used by the delegates
* support "hidden opposites" and the {@link OppositePropertyCallExp}.
*
* @since 3.1
*/
public static final String OCL_DELEGATES_USE_HIDDEN_OPPOSITES_KEY = "hiddenOpposites"; //$NON-NLS-1$
/**
* Initialize the resourceSet registries, if non-null, or the global registries, if null,
* to support usage of the LPG OCL Delegate Evaluator for the LPG OCL Delegate URI.
*
* @since 3.2
*/
public static void initialize(ResourceSet resourceSet) {
initialize(resourceSet, OCLConstants.OCL_DELEGATE_URI_LPG);
initializeMappingFrom(resourceSet, OCLConstants.OCL_DELEGATE_URI);
}
/**
* Initialize the resourceSet registries, if non-null, or the global registries, if null,
* to support usage of the LPG OCL Delegate Evaluator for the oclDelegateURI.
*
* @since 3.2
*/
public static void initialize(ResourceSet resourceSet, String oclDelegateURI) {
if (!EMFPlugin.IS_ECLIPSE_RUNNING) { // Install the 'plugin' registrations
EOperation.Internal.InvocationDelegate.Factory.Registry.INSTANCE.put(oclDelegateURI, new OCLInvocationDelegateFactory.Global());
EStructuralFeature.Internal.SettingDelegate.Factory.Registry.INSTANCE.put(oclDelegateURI, new OCLSettingDelegateFactory.Global());
EValidator.ValidationDelegate.Registry.INSTANCE.put(oclDelegateURI, new OCLValidationDelegateFactory.Global());
QueryDelegate.Factory.Registry.INSTANCE.put(oclDelegateURI, new OCLQueryDelegateFactory.Global());
}
if (resourceSet != null) {
// Install a DelegateResourceSetAdapter to supervise local registries and resource post-loading
DelegateResourceSetAdapter adapter = DelegateResourceSetAdapter.getAdapter(resourceSet);
VirtualDelegateMapping delegationMode = CommonOptions.DEFAULT_DELEGATION_MODE;
adapter.putRegistry(VirtualDelegateMapping.class, new VirtualDelegateMapping(delegationMode.getPluginId(), delegationMode.getKey(), delegationMode.getPreferredValue()));
// Install a local DelegateDomain.Factory
DelegateDomain.Factory.Registry.Impl delegateDomainFactory = new DelegateDomain.Factory.Registry.Impl();
delegateDomainFactory.put(oclDelegateURI, new OCLDelegateDomainFactory());
adapter.putRegistry(DelegateDomain.Factory.Registry.class, delegateDomainFactory);
// Install a local ValidationDelegate.Factory
ValidationDelegate.Factory.Registry validationDelegateFactoryRegistry = new ValidationDelegate.Factory.Registry.Impl();
validationDelegateFactoryRegistry.put(oclDelegateURI, new OCLValidationDelegateFactory(oclDelegateURI));
adapter.putRegistry(ValidationDelegate.Factory.Registry.class, validationDelegateFactoryRegistry);
// Install a local SettingDelegate.Factory
EStructuralFeature.Internal.SettingDelegate.Factory.Registry settingDelegateFactoryRegistry = new EStructuralFeature.Internal.SettingDelegate.Factory.Registry.Impl();
settingDelegateFactoryRegistry.put(oclDelegateURI, new OCLSettingDelegateFactory(oclDelegateURI));
adapter.putRegistry(EStructuralFeature.Internal.SettingDelegate.Factory.Registry.class, settingDelegateFactoryRegistry);
// Install a local InvocationDelegate.Factory
EOperation.Internal.InvocationDelegate.Factory.Registry invocationDelegateFactoryRegistry = new EOperation.Internal.InvocationDelegate.Factory.Registry.Impl();
invocationDelegateFactoryRegistry.put(oclDelegateURI, new OCLInvocationDelegateFactory(oclDelegateURI));
adapter.putRegistry(EOperation.Internal.InvocationDelegate.Factory.Registry.class, invocationDelegateFactoryRegistry);
// Install a local QueryDelegate.Factory
QueryDelegate.Factory.Registry queryDelegateFactoryRegistry = new QueryDelegate.Factory.Registry.Impl();
queryDelegateFactoryRegistry.put(oclDelegateURI, new OCLQueryDelegateFactory(oclDelegateURI));
adapter.putRegistry(QueryDelegate.Factory.Registry.class, queryDelegateFactoryRegistry);
}
}
/**
* @since 3.2
*/
public static void initializeMappingFrom(ResourceSet resourceSet, String oclDelegateURI) {
if (!EMFPlugin.IS_ECLIPSE_RUNNING) { // Install the 'plugin' registrations
EOperation.Internal.InvocationDelegate.Factory.Registry.INSTANCE.put(oclDelegateURI,
new OCLInvocationDelegateMapping());
EStructuralFeature.Internal.SettingDelegate.Factory.Registry.INSTANCE.put(oclDelegateURI,
new OCLSettingDelegateMapping());
EValidator.ValidationDelegate.Registry.INSTANCE.put(oclDelegateURI,
new OCLValidationDelegateMapping());
QueryDelegate.Factory.Registry.INSTANCE.put(oclDelegateURI,
new OCLQueryDelegateMapping());
}
if (resourceSet != null) {
// Install a DelegateResourceSetAdapter to supervise local registries and resource post-loading
DelegateResourceSetAdapter adapter = DelegateResourceSetAdapter.getAdapter(resourceSet);
VirtualDelegateMapping virtualDelegateMapping = adapter.getRegistry(VirtualDelegateMapping.class);
if (virtualDelegateMapping == null) {
virtualDelegateMapping = CommonOptions.DEFAULT_DELEGATION_MODE;
}
// Install a local DelegateDomain.Factory
DelegateDomain.Factory.Registry delegateDomainFactory = adapter.getRegistry(DelegateDomain.Factory.Registry.class);
if (delegateDomainFactory != null) {
delegateDomainFactory.put(oclDelegateURI, new OCLDelegateDomainFactory.Delegator(delegateDomainFactory));
}
// Install a local ValidationDelegate.Mapping
ValidationDelegate.Factory.Registry validationDelegateFactoryRegistry = adapter.getRegistry(ValidationDelegate.Factory.Registry.class);
if (validationDelegateFactoryRegistry != null) {
validationDelegateFactoryRegistry.put(oclDelegateURI, new OCLValidationDelegateMapping(validationDelegateFactoryRegistry, virtualDelegateMapping));
}
// Install a local SettingDelegate.Mapping
EStructuralFeature.Internal.SettingDelegate.Factory.Registry settingDelegateFactoryRegistry = adapter.getRegistry(EStructuralFeature.Internal.SettingDelegate.Factory.Registry.class);
if (settingDelegateFactoryRegistry != null) {
settingDelegateFactoryRegistry.put(oclDelegateURI, new OCLSettingDelegateMapping(settingDelegateFactoryRegistry, virtualDelegateMapping));
}
// Install a local InvocationDelegate.Mapping
EOperation.Internal.InvocationDelegate.Factory.Registry invocationDelegateFactoryRegistry = adapter.getRegistry(EOperation.Internal.InvocationDelegate.Factory.Registry.class);
if (invocationDelegateFactoryRegistry != null) {
invocationDelegateFactoryRegistry.put(oclDelegateURI, new OCLInvocationDelegateMapping(invocationDelegateFactoryRegistry, virtualDelegateMapping));
}
// Install a local QueryDelegate.Mapping
QueryDelegate.Factory.Registry queryDelegateFactoryRegistry = adapter.getRegistry(QueryDelegate.Factory.Registry.class);
if (queryDelegateFactoryRegistry != null) {
queryDelegateFactoryRegistry.put(oclDelegateURI, new OCLQueryDelegateMapping(queryDelegateFactoryRegistry, virtualDelegateMapping));
}
}
}
protected final String uri;
protected final EPackage ePackage;
protected final OCL ocl;
/**
* Initializes me with my delegate URI and package.
*
* @param delegateURI
* the delegate namespace I handle
* @param ePackage
* the package that I handle
*
* @throws ParserException
* if the operation's OCL body expression is invalid
*/
public OCLDelegateDomain(String delegateURI, EPackage ePackage) {
this.uri = delegateURI;
this.ePackage = ePackage;
Resource res = ePackage.eResource();
EPackage.Registry packageRegistry = null;
if (res != null) {
DelegateResourceAdapter.getAdapter(res); // Ensure we get unloaded when the resource is unloaded
ResourceSet resourceSet = res.getResourceSet();
if (resourceSet != null) {
// it's a dynamic package. Use the local package registry
packageRegistry = resourceSet.getPackageRegistry();
}
}
if (packageRegistry == null) { // Deprecated compatibility
packageRegistry = EPackage.Registry.INSTANCE;
}
EcoreEnvironmentFactory envFactory = null;
EAnnotation eAnnotation = OCLCommon.getDelegateAnnotation(ePackage);
if (eAnnotation != null) {
EMap<String, String> details = eAnnotation.getDetails();
String clsName = details.get(KEY_FOR_ENVIRONMENT_FACTORY_CLASS);
if (clsName != null) {
ClassLoader classLoader = ePackage.getClass().getClassLoader();
try {
Class<?> cls = classLoader.loadClass(clsName);
Constructor<?> con = cls.getConstructor(EPackage.Registry.class);
envFactory = (EcoreEnvironmentFactory) con.newInstance(packageRegistry);
} catch (Exception e) {
throw new WrappedException(
"Error instantiating " + KEY_FOR_ENVIRONMENT_FACTORY_CLASS + " " + clsName + //$NON-NLS-1$ //$NON-NLS-2$
": " + e.getMessage(), e); //$NON-NLS-1$
}
} else {
String value = details.get(OCL_DELEGATES_USE_HIDDEN_OPPOSITES_KEY);
if (value != null && Boolean.valueOf(value)) {
envFactory = new EcoreEnvironmentFactoryWithHiddenOpposites(
packageRegistry);
}
}
}
if (envFactory == null) {
envFactory = new EcoreEnvironmentFactory(packageRegistry);
}
this.ocl = OCL.newInstance(envFactory);
}
public void dispose() {
if (ocl != null) {
ocl.dispose();
}
}
public OCL getOCL() {
return ocl;
}
public String getURI() {
return uri;
}
public String toString() {
return ePackage.getName() + " : " + getURI(); //$NON-NLS-1$
}
}