All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.ocl.AbstractEvaluationEnvironment Maven / Gradle / Ivy

/**
 * 
 *
 * Copyright (c) 2007 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
 *
 * 
 *
 * $Id: AbstractEvaluationEnvironment.java,v 1.7 2009/06/25 19:23:52 ewillink Exp $
 */

package org.eclipse.ocl;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.ocl.internal.OCLPlugin;
import org.eclipse.ocl.internal.OCLStatusCodes;
import org.eclipse.ocl.internal.l10n.OCLMessages;
import org.eclipse.ocl.options.Customizable;
import org.eclipse.ocl.options.Option;
import org.eclipse.ocl.util.Adaptable;
import org.eclipse.ocl.util.OCLUtil;
import org.eclipse.ocl.utilities.PredefinedType;

/**
 * A partial implementation of the {@link EvaluationEnvironment} interface,
 * providing some useful common behaviors.  Implementors of metamodel-specific
 * environments are encourage to extend this class rather than implement
 * an evaluation environment "from scratch."
 * 

* See the {@link Environment} class for a description of the * generic type parameters of this class. *

* Since the 1.2 release, this interface is {@link Adaptable} to support the * optional adapter protocols such as {@link EvaluationEnvironment.Enumerations} * and {@link Customizable}. *

* * @author Christian W. Damus (cdamus) */ public abstract class AbstractEvaluationEnvironment implements EvaluationEnvironment, Adaptable, Customizable { private final EvaluationEnvironment parent; private final Map map = new HashMap(); private final Map, Object> options = new java.util.HashMap, Object>(); protected AbstractEvaluationEnvironment() { this(null); } protected AbstractEvaluationEnvironment( EvaluationEnvironment parent) { this.parent = parent; } /** * Obtains my parent (nesting) environment. * * @return my parent environment, or null if none */ protected EvaluationEnvironment getParent() { return parent; } /** * Returns the value associated with the supplied name * * @param name * the name whose value is to be returned * @return the value associated with the name */ public Object getValueOf(String name) { return map.get(name); } /** * Replaces the current value of the supplied name with the supplied value. * * @param name * the name * @param value * the new value */ public void replace(String name, Object value) { map.put(name, value); } /** * Adds the supplied name and value binding to the environment * * @param name * the name to add * @param value * the associated binding */ public void add(String name, Object value) { if (map.containsKey(name)) { String message = OCLMessages.bind( OCLMessages.BindingExist_ERROR_, name, map.get(name)); throw new IllegalArgumentException(message); } map.put(name, value); } /** * Removes the supplied name and binding from the environment (if it exists) * and returns it. * * @param name * the name to remove * @return the value associated with the removed name */ public Object remove(String name) { return map.remove(name); } /** * Clears the environment of variables. */ public void clear() { map.clear(); } /** * Returns a string representation of the bindings */ @Override public String toString() { return map.toString(); } /** * By default, a subclass will not support overriding the operations defined * by the OCL Standard Library. This implementation delegates to the * parent environment (if any), otherwise returns false. */ public boolean overrides(O operation, int opcode) { return (getParent() != null)? getParent().overrides(operation, opcode) : false; } /** * Implements the inherited method by attempting to find an appropriate * Java method in the actual type of the source object and invoking * it. On failure to find or invoke the method (e.g., an exception), the * OclInvalid result is returned. * * @return the result of the Java method invocation, or OclInvalid * on failure of the method invocation */ public Object callOperation(O operation, int opcode, Object source, Object[] args) throws IllegalArgumentException { if (getParent() != null) { return getParent().callOperation(operation, opcode, source, args); } Method method = getJavaMethodFor(operation, source); if (method != null) { try { // coerce any collection arguments to EList as necessary Class[] parmTypes = method.getParameterTypes(); for (int i = 0; i < parmTypes.length; i++) { if (EList.class.isAssignableFrom(parmTypes[i])) { if (args[i] == null) { args[i] = ECollections.EMPTY_ELIST; } else if (!(args[i] instanceof Collection)) { EList list = new BasicEList.FastCompare(1); list.add(args[i]); args[i] = list; } else if (!(args[i] instanceof EList)) { args[i] = new BasicEList.FastCompare((Collection) args[i]); } } } return method.invoke(source, args); } catch (Exception e) { OCLPlugin.catching(getClass(), "callOperation", e);//$NON-NLS-1$ OCLPlugin.log( Diagnostic.ERROR, OCLStatusCodes.IGNORED_EXCEPTION_WARNING, OCLMessages.bind( OCLMessages.ErrorMessage_ERROR_, "calloperation", //$NON-NLS-1$ e.getLocalizedMessage()), e); return getInvalidResult(); } } // maybe it's a comparison operation that is implemented implicitly // via the Comparable interface? switch (opcode) { case PredefinedType.LESS_THAN: case PredefinedType.GREATER_THAN: case PredefinedType.LESS_THAN_EQUAL: case PredefinedType.GREATER_THAN_EQUAL: if ((source instanceof Comparable) && (args.length == 1)) { @SuppressWarnings("unchecked") Comparable comparable = (Comparable) source; Object other = args[0]; switch (opcode) { case PredefinedType.LESS_THAN: return comparable.compareTo(other) < 0; case PredefinedType.GREATER_THAN: return comparable.compareTo(other) > 0; case PredefinedType.LESS_THAN_EQUAL: return comparable.compareTo(other) <= 0; case PredefinedType.GREATER_THAN_EQUAL: return comparable.compareTo(other) >= 0; } } break; } throw new IllegalArgumentException(); } /** * Returns the java method that corresponds to the supplied * EOperation * * @param operation * the operation * @return a java method */ protected abstract Method getJavaMethodFor(O operation, Object receiver); /** * Obtains the language-binding-specific representation of the predefined * OclInvalid object. * * @return OclInvalid */ protected abstract Object getInvalidResult(); /** * Implements the interface method by testing whether I am an instance of * the requested adapter type. */ @SuppressWarnings("unchecked") public T getAdapter(Class adapterType) { T result; if (adapterType.isInstance(this)) { result = (T) this; } else { result = null; } return result; } protected Map, Object> basicGetOptions() { return options; } public Map, Object> getOptions() { Customizable parent = (getParent() != null)? OCLUtil.getAdapter(getParent(), Customizable.class) : null; Map, Object> result = (parent != null) ? new java.util.HashMap, Object>(parent.getOptions()) : new java.util.HashMap, Object>(); result.putAll(basicGetOptions()); return result; } public void setOption(Option option, T value) { basicGetOptions().put(option, value); } public void putOptions(Map, ? extends T> options) { Map, Object> myOptions = basicGetOptions(); myOptions.clear(); myOptions.putAll(options); } public T removeOption(Option option) { T result = getValue(option); basicGetOptions().remove(option); return result; } public Map, T> removeOptions(Collection> options) { Map, T> result = new java.util.HashMap, T>(); Map, Object> myOptions = basicGetOptions(); for (Option next : options) { result.put(next, getValue(next)); myOptions.remove(next); } return result; } public Map, Object> clearOptions() { Map, Object> myOptions = basicGetOptions(); Map, Object> result = new java.util.HashMap, Object>( myOptions); myOptions.clear(); return result; } public boolean isEnabled(Option option) { Boolean result = getValue(option); return (result == null)? false : result.booleanValue(); } public T getValue(Option option) { @SuppressWarnings("unchecked") T result = (T) getOptions().get(option); if (result == null) { Customizable parent = (getParent() != null)? OCLUtil.getAdapter(getParent(), Customizable.class) : null; result = (parent != null)? parent.getValue(option) : option.getDefaultValue(); } return result; } }