/******************************************************************************* * Copyright (c) 2010, 2014 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: * 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$ } }