/******************************************************************************* * Copyright (c) 2007, 2014 IBM 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: * IBM - Initial API and implementation * Adolfo Sanchez-Barbudo Herrera - Bug 233673 * Zeligsoft - Bugs 233673, 261128 * E.D.Willink - Bug 242236 * Radek Dvorak - Bug 261128 *******************************************************************************/ package org.eclipse.ocl.util; import java.lang.ref.Reference; import java.util.List; import java.util.Map; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.ocl.Environment; import org.eclipse.ocl.EnvironmentFactory; import org.eclipse.ocl.EvaluationEnvironment; import org.eclipse.ocl.LookupException; import org.eclipse.ocl.Query; import org.eclipse.ocl.SemanticException; import org.eclipse.ocl.SyntaxException; import org.eclipse.ocl.TypeChecker; import org.eclipse.ocl.lpg.AbstractBasicEnvironment; import org.eclipse.ocl.lpg.BasicEnvironment; import org.eclipse.ocl.lpg.BasicEnvironment2; import org.eclipse.ocl.lpg.ProblemHandler; import org.eclipse.ocl.options.Customizable; import org.eclipse.ocl.parser.OCLProblemHandler; import org.eclipse.ocl.utilities.TypedElement; /** * Miscellaneous utilities for use by the OCL parser/interpreter and by clients. * * @author Christian W. Damus (cdamus) * * @since 1.2 */ public final class OCLUtil { /** * The shared Ecore and UML binding plugin identification. * @since 3.2 */ public static final String PLUGIN_ID = "org.eclipse.ocl"; //$NON-NLS-1$ /** Use weak references as the keys to avoid memory leaks. */ private static final Map<Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?>, Reference<BasicEnvironment2>> environments = new java.util.WeakHashMap<Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?>, Reference<BasicEnvironment2>>(); /** Use weak references as the keys to avoid memory leaks. */ private static final Map<Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?>, Reference<TypeChecker<?, ?, ?>>> typesCheckerEnvironments = new java.util.WeakHashMap<Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?>, Reference<TypeChecker<?, ?, ?>>>(); // prevent instantiation private OCLUtil() { super(); } /** * Obtains an adapter for the specified interface type. Even for * environments that do not adapt to the following interfaces, this method * will provide a default implementation for convenience: * <ul> * <li>{@link BasicEnvironment}. In the case that the environment is not * intrinsically adaptable to this interface and an extrinsic adapter * is provided, multiple adaptations of the same environment will * always return the same adapter instance</li> * <li>{@link Customizable}. In the case that the environment is not * intrinsically adaptable to this interface it is forcibly adapted to * the {@link BasicEnvironment} protocol</li> * <li>{@link ProblemHandler}</li> * <li>{@link org.eclipse.ocl.Environment.Lookup Environment.Lookup}</li> * </ul> * * @param <T> the requested adapter interface * * @param env an environment to adapt * @param adapterType the requested adapter interface * @return an instance of the requested interface, or <code>null</code> * if this environment does not adapt to it */ @SuppressWarnings("unchecked") public static <T> T getAdapter( Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> env, Class<T> adapterType) { T result; if (env instanceof Adaptable) { result = ((Adaptable) env).getAdapter(adapterType); } else if (adapterType.isInstance(env)) { result = (T) env; } else { result = null; } if (result == null) { if (adapterType == TypeChecker.class) { result = (T) getTypeChecker(env); } else if (adapterType == BasicEnvironment.class) { result = (T) getBasicEnvironment(env); } else if (adapterType == BasicEnvironment2.class) { result = (T) getBasicEnvironment(env); } else if (adapterType == ProblemHandler.class) { result = (T) getAdapter(env, BasicEnvironment.class).getProblemHandler(); } else if (adapterType == Environment.Lookup.class) { final Environment<Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object> _env = (Environment<Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object>) env; result = (T) new Environment.Lookup<Object, Object, Object, Object>() { public Object tryLookupPackage(List<String> names) throws LookupException { return _env.lookupPackage(names); } public Object tryLookupClassifier(List<String> names) throws LookupException { return _env.lookupClassifier(names); } public Object tryLookupOperation(Object owner, String name, List<? extends TypedElement<Object>> args) throws LookupException { return _env.lookupOperation(owner, name, args); } public Object tryLookupProperty(Object owner, String name) throws LookupException { return _env.lookupProperty(owner, name); } public Object tryLookupAssociationClassReference(Object owner, String name) throws LookupException { return _env.lookupAssociationClassReference(owner, name); } public Object tryLookupSignal(Object owner, String name, List<? extends TypedElement<Object>> args) throws LookupException { return _env.lookupSignal(owner, name, args); }}; } } return result; } /** * Gets the lazily cached basic-environment adapter for the specified OCL * environment. The cache is doubly weak to avoid leaking environment * instances. * * @param env the environment for which to define an external adapter * @return the external adapter */ private static BasicEnvironment2 getBasicEnvironment( final Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> env) { BasicEnvironment2 result = null; Reference<BasicEnvironment2> ref = environments.get(env); if (ref != null) { result = ref.get(); } if (result == null) { result = new AbstractBasicEnvironment(null) { @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> adapterType) { if (adapterType == Environment.class) { return (T) env; // the reverse adaptation } else { return super.getAdapter(adapterType); } } }; environments.put(env, new java.lang.ref.WeakReference<BasicEnvironment2>(result)); } return result; } /** * Creates and caches a suitable {@link TypeChecker} from an Environment . * This method will be called in case of the environment doesn't adapt to * {@link TypeChecker}. * * @param env * the Environment associated to the {@link TypeChecker} * @return the TypeChecker */ @SuppressWarnings("unchecked") private static <PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> TypeChecker<C, O, P> getTypeChecker( Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) { TypeChecker<C, O, P> result = null; Reference<TypeChecker<?, ?, ?>> ref = typesCheckerEnvironments.get(env); if (ref != null) { result = (TypeChecker<C, O, P>) ref.get(); } if (result == null) { result = new OCLTypeChecker<C, O, P, PM>(env); typesCheckerEnvironments.put(env, new java.lang.ref.WeakReference<TypeChecker<?, ?, ?>>(result)); } return result; } /** * Obtains an adapter for the specified interface type. Even for * environment factories that do not adapt to the following interfaces, * this method will provide a default implementation for convenience: * <ul> * <li>{@link org.eclipse.ocl.EnvironmentFactory.Lookup EnvironmentFactory.Lookup}</li> * </ul> * * @param <T> the requested adapter interface * * @param factory an environment factory to adapt * @param adapterType the requested adapter interface * @return an instance of the requested interface, or <code>null</code> * if this environment factory does not adapt to it */ @SuppressWarnings("unchecked") public static <T> T getAdapter( EnvironmentFactory<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> factory, Class<T> adapterType) { T result; if (factory instanceof Adaptable) { result = ((Adaptable) factory).getAdapter(adapterType); } else if (adapterType.isInstance(factory)) { result = (T) factory; } else { result = null; } if (result == null) { if (adapterType == EnvironmentFactory.Lookup.class) { final EnvironmentFactory<Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object> _factory = (EnvironmentFactory<Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object>) factory; result = (T) new EnvironmentFactory.Lookup<Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object>() { public Environment<Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object> tryCreatePackageContext( Environment<Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object> parent, List<String> pathname) throws LookupException { return _factory.createPackageContext(parent, pathname); }}; } } return result; } /** * Obtains an adapter for the specified interface type, if the evaluation * environment is {@link Adaptable} to it. * * @param <T> the requested adapter interface * * @param env an evaluation environment to adapt * @param adapterType the requested adapter interface * @return an instance of the requested interface, or <code>null</code> * if this evaluation environment does not adapt to it */ @SuppressWarnings("unchecked") public static <T> T getAdapter(EvaluationEnvironment<?, ?, ?, ?, ?> env, Class<T> adapterType) { T result; if (env instanceof Adaptable) { result = ((Adaptable) env).getAdapter(adapterType); } else if (adapterType.isInstance(env)) { result = (T) env; } else { result = null; } return result; } /** * Checks whether the specified environment's problem handler has any * diagnostics of error severity or worse and, if so, throws a semantic * exception encapsulating these diagnostics. * * @param env an environment in which we have parsed some OCL * * @throws SyntaxException if there are any errors in parsing the concrete * syntax * @throws SemanticException if there are any errors in analyzing the * abstract syntax * * @see #checkForErrors(ProblemHandler) */ public static Diagnostic checkForErrors( Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> env) throws SyntaxException, SemanticException { return checkForErrors(getAdapter(env, ProblemHandler.class)); } /** * Checks whether the specified problem handler has any * diagnostics of error severity or worse and, if so, throws a semantic * exception encapsulating these diagnostics. * * @param problemHandler a problem handler * * @throws SyntaxException if there are any errors in parsing the concrete * syntax * @throws SemanticException if there are any errors in analyzing the * abstract syntax */ public static Diagnostic checkForErrors(ProblemHandler problemHandler) throws SyntaxException, SemanticException { Diagnostic result = null; if (problemHandler instanceof OCLProblemHandler) { result = ((OCLProblemHandler) problemHandler).getDiagnostic(); if ((result != null) && (result.getSeverity() >= Diagnostic.ERROR)) { List<?> data = result.getData(); if (data.contains(ProblemHandler.Phase.LEXER) || data.contains(ProblemHandler.Phase.PARSER)) { throw new SyntaxException(result); } else { throw new SemanticException(result); } } } return result; } /** * Checks whether the specified environment's problem handler has any * diagnostics of warnings severity or worse and, if so, throws a semantic * exception encapsulating these diagnostics. * * @param env an environment in which we have parsed some OCL * * @throws SyntaxException if there are any errors in parsing the concrete * syntax * @throws SemanticException if there are any errors in analyzing the * abstract syntax * * @see #checkForErrors(ProblemHandler) * @since 3.0 */ public static Diagnostic checkForErrorsOrWarnings( Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> env) throws SyntaxException, SemanticException { return checkForErrorsOrWarnings(getAdapter(env, ProblemHandler.class)); } /** * Checks whether the specified problem handler has any * diagnostics of warning severity or worse and, if so, throws a semantic * exception encapsulating these diagnostics. * * @param problemHandler a problem handler * * @throws SyntaxException if there are any errors in parsing the concrete * syntax * @throws SemanticException if there are any errors in analyzing the * abstract syntax * @since 3.0 */ public static Diagnostic checkForErrorsOrWarnings(ProblemHandler problemHandler) throws SyntaxException, SemanticException { Diagnostic result = null; if (problemHandler instanceof OCLProblemHandler) { result = ((OCLProblemHandler) problemHandler).getDiagnostic(); if ((result != null) && (result.getSeverity() >= Diagnostic.WARNING)) { List<?> data = result.getData(); if (data.contains(ProblemHandler.Phase.LEXER) || data.contains(ProblemHandler.Phase.PARSER)) { throw new SyntaxException(result); } else { throw new SemanticException(result); } } } return result; } /** * Attempts to get an environment instance that is appropriate for introspection * of the specified validation <tt>target</tt>. If an environment is specified * in the validation <tt>context</tt>, then it is used. Otherwise, an * environment is obtained from the registry. * * @param target an object to be validated in an appropriate environment * @param context the current validation context * * @return the environment, or <code>null</code> if none can be found * * @see org.eclipse.ocl.Environment.Registry */ public static <PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> getValidationEnvironment( Object target, Map<Object, Object> context) { @SuppressWarnings("unchecked") Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> result = (Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>) context .get(Environment.class); if (result == null) { // try the extension point result = Environment.Registry.INSTANCE.getEnvironmentFor(target); if (result != null) { context.put(Environment.class, result); } } return result; } /** * Attempts to get evaluation problems available from the last evaluation of * the given query. * * @param query * a query to check for evaluation problems * @return the diagnostic object encapsulating the problem details or * <code>null</code> if no problems are available * * @since 1.3 */ public static <C, CLS, E> Diagnostic getEvaluationProblems( Query<C, CLS, E> query) { if (query instanceof ProblemAware) { ProblemAware problemAware = (ProblemAware) query; return problemAware.getProblems(); } return null; } }