blob: bcdbaa370e01d2b60515a0f9a90901ff34f759c0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2017 Dennis Wagelaar, Vrije Universiteit Brussel.
* 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:
* Dennis Wagelaar, Vrije Universiteit Brussel - initial API and
* implementation and/or initial documentation
* William Piers, Obeo
*******************************************************************************/
package org.eclipse.m2m.atl.emftvm.util;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreSwitch;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.m2m.atl.common.ATLLogger;
import org.eclipse.m2m.atl.emftvm.CodeBlock;
import org.eclipse.m2m.atl.emftvm.EmftvmFactory;
import org.eclipse.m2m.atl.emftvm.EmftvmPackage;
import org.eclipse.m2m.atl.emftvm.ExecEnv;
import org.eclipse.m2m.atl.emftvm.Field;
import org.eclipse.m2m.atl.emftvm.LocalVariable;
import org.eclipse.m2m.atl.emftvm.Metamodel;
import org.eclipse.m2m.atl.emftvm.Model;
import org.eclipse.m2m.atl.emftvm.Operation;
import org.eclipse.m2m.atl.emftvm.Parameter;
import org.eclipse.m2m.atl.emftvm.trace.TracePackage;
/**
* EMFTVM static utility methods.
*
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
*/
public final class EMFTVMUtil {
/**
* Returns the registry type of the switched object.
*
* @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
*/
public static class RegistryTypeSwitch extends EcoreSwitch<Object> {
/**
* {@inheritDoc}
*/
@Override
public Object defaultCase(EObject object) {
throw new IllegalArgumentException("Unsupported type: " + object);
}
/**
* {@inheritDoc}
*/
@Override
public Object caseEClass(EClass object) {
return object;
}
/**
* {@inheritDoc}
*/
@Override
public Object caseEClassifier(EClassifier object) {
final Class<?> ic = object.getInstanceClass();
if (ic == null) {
throw new IllegalArgumentException(String.format("Primitive EMF type without instance class %s", object));
}
return ic;
}
/**
* {@inheritDoc}
*/
@Override
public Object caseEEnum(EEnum object) {
return EnumLiteral.class;
}
}
/**
* Native type namespace.
*/
public static final String NATIVE = "#native";
/**
* Type namespace delimiter.
*/
public static final String NS_DELIM = "::";
/**
* Type namespace matching pattern.
*
* @see #NS_DELIM
*/
public static final Pattern DELIM_PATTERN = Pattern.compile(NS_DELIM);
/**
* Name of the "main" static parameterless operation.
*/
public static final String MAIN_OP_NAME = "main";
/**
* Name if the XMI ID feature for {@link EObject}s contained in {@link XMIResource}s.
*/
public static final String XMI_ID_FEATURE = "__xmiID__";
/**
* Implementation class name for {@link WorkspaceUtil}.
*/
private static final String WORKSPACE_UTIL_IMPL = "org.eclipse.m2m.atl.emftvm.util.WorkspaceUtilImpl";
/**
* Cache used to store native Java methods.
*
* @author <a href="mailto:frederic.jouault@univ-nantes.fr">Frederic Jouault</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
*/
private static final ConcurrentMap<MethodSignature, WeakReference<Method>> METHOD_CACHE = new ConcurrentHashMap<MethodSignature, WeakReference<Method>>();
/**
* Represents the <code>null</code> method reference for the {@link #METHOD_CACHE}.
*/
private static final WeakReference<Method> NULL_METHOD_REFERENCE = new WeakReference<Method>(null);
/**
* Cache used to store the found root methods for native Java methods.
*
* @author <a href="mailto:frederic.jouault@univ-nantes.fr">Frederic Jouault</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
*/
private static final Map<Method, WeakReference<Method>> ROOT_METHOD_CACHE = Collections
.synchronizedMap(new WeakHashMap<Method, WeakReference<Method>>());
/**
* Singleton {@link RegistryTypeSwitch} instance.
*/
private static final RegistryTypeSwitch REGISTRY_TYPE_SWITCH = new RegistryTypeSwitch();
private static Metamodel ecoreMetamodel;
private static Metamodel emfTvmMetamodel;
private static Metamodel traceMetamodel;
private static volatile int MethodCacheAccesses;
private static volatile int MethodCacheHits;
private static volatile int RootMethodCacheAccesses;
private static volatile int RootMethodCacheHits;
/**
* Not used.
*/
private EMFTVMUtil() {
}
/**
* Returns the name of <code>type</code>, for printing.
*
* @param env
* the current {@link ExecEnv}
* @param type
* the type
* @return the name of <code>type</code>, for printing
*/
public static String getTypeName(final ExecEnv env, final Object type) {
if (type instanceof EClass) {
final EClass eCls = (EClass) type;
final Metamodel mm = env.getMetaModel(eCls.eResource());
if (mm != null) {
return env.getMetaModelID(mm) + '!' + eCls.getName();
}
return eCls.getName();
} else if (type instanceof Class<?>) {
final Class<?> cls = (Class<?>) type;
final String nativeTypeName = NativeTypes.typeName(cls);
return cls.getName().equals(nativeTypeName) ? NATIVE + '!' + nativeTypeName : nativeTypeName;
} else {
return type.toString();
}
}
/**
* Returns the names of <code>types</code>, for printing.
*
* @param env
* the current {@link ExecEnv}.
* @param types
* the types
* @return the names of <code>types</code>, for printing
*/
public static String getTypeNames(final ExecEnv env, final Object[] types) {
final StringBuffer names = new StringBuffer();
boolean notFirst = false;
for (Object type : types) {
if (notFirst) {
names.append(", ");
}
names.append(getTypeName(env, type));
notFirst = true;
}
return names.toString();
}
/**
* Returns the type object to use for the registry.
*
* @param type
* the type object
* @return the type object to use for the registry
* @throws IllegalArgumentException
* if type is a primitive EMF type without instance class
*/
public static Object getRegistryType(final Object type) throws IllegalArgumentException {
if (type instanceof EClassifier) {
return REGISTRY_TYPE_SWITCH.doSwitch((EClassifier) type);
}
return type;
}
/**
* Returns the singleton instance of the Ecore metamodel.
*
* @return the singleton instance of the Ecore metamodel
*/
public static synchronized Metamodel getEcoreMetamodel() {
if (ecoreMetamodel == null) {
ecoreMetamodel = EmftvmFactory.eINSTANCE.createMetamodel();
ecoreMetamodel.setResource(EcorePackage.eINSTANCE.eResource());
}
return ecoreMetamodel;
}
/**
* Returns the singleton instance of the EMFTVM metamodel.
*
* @return the singleton instance of the EMFTVM metamodel
*/
public static synchronized Metamodel getEmfTvmMetamodel() {
if (emfTvmMetamodel == null) {
emfTvmMetamodel = EmftvmFactory.eINSTANCE.createMetamodel();
emfTvmMetamodel.setResource(EmftvmPackage.eINSTANCE.eResource());
}
return emfTvmMetamodel;
}
/**
* Returns the singleton instance of the Trace metamodel.
*
* @return the singleton instance of the Trace metamodel
*/
public static synchronized Metamodel getTraceMetamodel() {
if (traceMetamodel == null) {
traceMetamodel = EmftvmFactory.eINSTANCE.createMetamodel();
traceMetamodel.setResource(TracePackage.eINSTANCE.eResource());
}
return traceMetamodel;
}
/**
* Finds all instances of type in the registered input/inout models.
*
* @param type
* the type
* @param env
* the current {@link ExecEnv}
* @return all instances of type in the registered input/inout models
*/
public static LazyList<EObject> findAllInstances(final EClass type, final ExecEnv env) {
LazyList<EObject> allInst = new LazyList<EObject>();
for (Model model : env.getInputModels().values()) {
allInst = allInst.union(model.allInstancesOf(type));
}
for (Model model : env.getInoutModels().values()) {
allInst = allInst.union(model.allInstancesOf(type));
}
return allInst;
}
/**
* Finds all instances of type in the given model.
*
* @param modelname
* the model name
* @param type
* the type
* @param env
* the current {@link ExecEnv}
* @return all instances of type in the given model
*/
public static LazyList<EObject> findAllInstIn(final Object modelname, final EClass type, final ExecEnv env) {
Model model = env.getInputModels().get(modelname);
if (model == null) {
model = env.getInoutModels().get(modelname);
}
if (model == null) {
throw new IllegalArgumentException(String.format("No input/inout model found with name %s", modelname));
}
return model.allInstancesOf(type);
}
/**
* Offers an alternative to the default <code>toString()</code> method. Uses <code>env</code> to determine the containing model of
* types. Compensates for {@link EObject}'s notoriously bad <code>toString()</code>.
*
* @param object
* @param env
* @return the string representation of <code>object</code>.
*/
public static String toPrettyString(final Object object, final ExecEnv env) {
if (object instanceof EClassifier) {
final StringBuffer sb = new StringBuffer();
if (env != null) {
final Model model = env.getModelOf((EClassifier) object);
if (model != null) {
sb.append(env.getModelID(model));
sb.append('!');
}
}
sb.append(((EClassifier) object).getName());
return sb.toString();
} else if (object instanceof EObject) { // EObjects have a notoriously bad toString()
final StringBuffer buf = new StringBuffer();
final EObject eo = (EObject) object;
EStructuralFeature sf = eo.eClass().getEIDAttribute();
if (sf == null) {
sf = eo.eClass().getEStructuralFeature("name");
}
if (sf != null && eo.eGet(sf) != null) {
buf.append(eo.eGet(sf));
} else {
buf.append(Integer.toHexString(eo.hashCode()));
}
buf.append(':');
buf.append(toPrettyString(eo.eClass(), env));
return buf.toString();
} else if (object instanceof Class<?>) {
return ((Class<?>) object).getName();
} else if (object instanceof String) {
return new StringBuffer().append('\'').append(object.toString()).append('\'').toString();
} else if (object instanceof LazyCollection<?>) {
return ((LazyCollection<?>) object).asString(env);
} else if (object != null) {
return object.toString();
} else {
return "OclUndefined";
}
}
/**
* Offers an alternative to the default <code>toString()</code> method. Uses <code>env</code> to determine the containing model of
* types. Compensates for {@link EObject}'s notoriously bad <code>toString()</code>.
*
* @param coll
* @param env
* @return the string representation of <code>coll</code>.
*/
public static String toPrettyString(final Collection<?> coll, final ExecEnv env) {
final StringBuffer sb = new StringBuffer();
sb.append('[');
boolean first = true;
for (Object object : coll) {
if (!first) {
sb.append(", ");
}
first = false;
sb.append(EMFTVMUtil.toPrettyString(object, env));
}
sb.append(']');
return sb.toString();
}
/**
* Offers an alternative to the default <code>toString()</code> method. Uses <code>env</code> to determine the containing model of
* types. Compensates for {@link EObject}'s notoriously bad <code>toString()</code>.
*
* @param array
* @param env
* @return the string representation of <code>coll</code>.
*/
public static <T> String toPrettyString(final T[] array, final ExecEnv env) {
final StringBuffer sb = new StringBuffer();
sb.append('[');
boolean first = true;
for (Object object : array) {
if (!first) {
sb.append(", ");
}
first = false;
sb.append(EMFTVMUtil.toPrettyString(object, env));
}
sb.append(']');
return sb.toString();
}
/**
* Retrieves the value of <code>eo.sf</code>. Checks that <code>eo</code> is not in an output model.
*
* @param env
* the current {@link ExecEnv}
* @param eo
* the model element to retrieve the value from
* @param sf
* the structural feature to retrieve the value from
* @return the value of <code>eo.sf</code>.
*/
public static Object get(final ExecEnv env, final EObject eo, final EStructuralFeature sf) {
if (env.getOutputModelOf(eo) != null) {
throw new IllegalArgumentException(String.format("Cannot read properties of %s, as it is contained in an output model",
toPrettyString(eo, env)));
}
return uncheckedGet(env, eo, sf);
}
/**
* Retrieves the value of <code>eo.sf</code>.
*
* @param env
* the current {@link ExecEnv}
* @param eo
* the model element to retrieve the value from
* @param sf
* the structural feature to retrieve the value from
* @return the value of <code>eo.sf</code>.
*/
@SuppressWarnings("unchecked")
public static Object uncheckedGet(final ExecEnv env, final EObject eo, final EStructuralFeature sf) {
if (sf instanceof EReference) {
// EReferences need only EList conversion, notably not EnumLiteral conversion
final Object value = eo.eGet(sf);
if (!(value instanceof Collection<?>) || (value instanceof LazyCollection<?>)) {
// Simple values and internal collection types don't need conversion
return value;
}
if (value instanceof List<?>) {
if (eo != null && env.getInoutModelOf(eo) != null) {
// Copy list for inout models
return new LazyListOnList<Object>(new ArrayList<Object>((List<Object>) value));
} else {
return new LazyListOnList<Object>((List<Object>) value);
}
} else if (value instanceof Set<?>) {
if (eo != null && env.getInoutModelOf(eo) != null) {
// Copy list for inout models
return new LazySetOnSet<Object>(new LinkedHashSet<Object>((Set<Object>) value));
} else {
return new LazySetOnSet<Object>((Set<Object>) value);
}
} else {
if (eo != null && env.getInoutModelOf(eo) != null) {
// Copy list for inout models
return new LazyListOnCollection<Object>(new ArrayList<Object>((Collection<Object>) value));
} else {
return new LazyListOnCollection<Object>((Collection<Object>) value);
}
}
} else {
return emf2vm(env, eo, eo.eGet(sf));
}
}
/**
* Converts <code>value</code> to an EMFTVM value.
*
* @param env
* the current {@link ExecEnv}
* @param eo
* the {@link EObject} from which the value was obtained
* @param value
* the EMF value to convert
* @return the EMFTVM value
*/
@SuppressWarnings("unchecked")
public static Object emf2vm(final ExecEnv env, final EObject eo, final Object value) {
if (value instanceof Enumerator) {
return new EnumLiteral(value.toString());
} else if (value instanceof Collection<?>) {
if (value instanceof LazyCollection<?>) {
// Internal collection types don't need conversion
return value;
} else if (value instanceof List<?>) {
if (eo != null && env.getInoutModelOf(eo) != null) {
// Copy list for inout models
return new EnumConversionListOnList((List<Object>) value).cache();
} else {
return new EnumConversionListOnList((List<Object>) value);
}
} else if (value instanceof Set<?>) {
if (eo != null && env.getInoutModelOf(eo) != null) {
// Copy list for inout models
return new EnumConversionSetOnSet((Set<Object>) value).cache();
} else {
return new EnumConversionSetOnSet((Set<Object>) value);
}
} else {
if (eo != null && env.getInoutModelOf(eo) != null) {
// Copy list for inout models
return new EnumConversionList((Collection<Object>) value).cache();
} else {
return new EnumConversionList((Collection<Object>) value);
}
}
} else if (value != null && value.getClass().isArray()) {
if (Object.class.isAssignableFrom(value.getClass().getComponentType())) {
return new LazyListOnList<Object>(Arrays.asList((Object[]) value));
} else {
return value; // don't wrap primitive type arrays
}
}
assert eo == null || !(value instanceof Collection<?>);
return value;
}
/**
* Sets the <code>value</code> of <code>eo.sf</code>.
*
* @param env
* the current {@link ExecEnv}
* @param eo
* the model element to set the value for
* @param sf
* the structural feature to set the value for
* @param value
* the value to set
*/
public static void set(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Object value) {
if (!sf.isChangeable()) {
throw new IllegalArgumentException(String.format("Field %s::%s is not changeable",
toPrettyString(sf.getEContainingClass(), env), sf.getName()));
}
if (env.getInputModelOf(eo) != null) {
throw new IllegalArgumentException(String.format("Cannot set properties of %s, as it is contained in an input model",
toPrettyString(eo, env)));
}
if (sf.isMany()) {
if (!(value instanceof Collection<?>)) {
throw new IllegalArgumentException(String.format("Cannot assign %s to multi-valued field %s::%s",
toPrettyString(value, env), sf.getEContainingClass().getName(), sf.getName()));
}
setMany(env, eo, sf, (Collection<?>) value);
} else {
setSingle(env, eo, sf, value, -1);
}
assert eo.eResource() != null;
}
/**
* Adds the <code>value</code> of <code>eo.sf</code>.
*
* @param env
* @param eo
* @param sf
* @param value
* @param index
* the insertion index (-1 for end)
*/
public static void add(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Object value, final int index) {
if (!sf.isChangeable()) {
throw new IllegalArgumentException(String.format("Field %s::%s is not changeable",
toPrettyString(sf.getEContainingClass(), env), sf.getName()));
}
if (env.getInputModelOf(eo) != null) {
throw new IllegalArgumentException(String.format("Cannot add properties to %s, as it is contained in an input model",
toPrettyString(eo, env)));
}
if (sf.isMany()) {
if (value instanceof Collection<?>) {
EMFTVMUtil.addMany(env, eo, sf, (Collection<?>) value, index);
} else {
EMFTVMUtil.addMany(env, eo, sf, value, index);
}
} else {
if (eo.eIsSet(sf)) {
throw new IllegalArgumentException(String.format("Cannot add more than one value to %s::%s",
toPrettyString(eo.eClass(), env), sf.getName()));
}
EMFTVMUtil.setSingle(env, eo, sf, value, index);
}
assert eo.eResource() != null;
}
/**
* Removes the <code>value</code> from <code>eo.sf</code>.
*
* @param env
* @param eo
* @param sf
* @param value
*/
public static void remove(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Object value) {
if (!sf.isChangeable()) {
throw new IllegalArgumentException(String.format("Field %s::%s is not changeable",
toPrettyString(sf.getEContainingClass(), env), sf.getName()));
}
if (env.getInputModelOf(eo) != null) {
throw new IllegalArgumentException(String.format("Cannot remove properties of %s, as it is contained in an input model",
toPrettyString(eo, env)));
}
if (sf.isMany()) {
if (value instanceof Collection<?>) {
EMFTVMUtil.removeMany(env, eo, sf, (Collection<?>) value);
} else {
EMFTVMUtil.removeMany(env, eo, sf, value);
}
} else {
final Object oldValue = eo.eGet(sf);
final EClassifier sfType = sf.getEType();
if (sfType instanceof EEnum && value instanceof EnumLiteral) {
final EEnum eEnum = (EEnum) sfType;
if (oldValue != null && oldValue.equals(((EnumLiteral) value).getEnumerator(eEnum))) {
EMFTVMUtil.setSingle(env, eo, sf, sf.getDefaultValue(), -1);
}
} else {
if (oldValue == null ? value == null : oldValue.equals(value)) {
EMFTVMUtil.setSingle(env, eo, sf, sf.getDefaultValue(), -1);
}
}
}
assert eo.eResource() != null;
}
/**
* Sets the <code>value</code> of <code>eo.sf</code>. Assumes <code>sf</code> has a multiplicity &lt;= 1.
*
* @param env
* @param eo
* @param sf
* @param value
* @param index
* the insertion index (-1 for end)
*/
private static void setSingle(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Object value, final int index) {
assert !sf.isMany();
if (index > 0) {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
if (sf instanceof EReference) {
final EReference ref = (EReference) sf;
if (checkValue(env, eo, ref, value, isAllowInterModelReferences(env, eo))) {
final EObject oldValue = (EObject) eo.eGet(sf);
assert eo.eResource() != null;
assert value == null || ((EObject) value).eResource() != null;
assert oldValue == null || oldValue.eResource() != null;
eo.eSet(sf, value);
if (ref.isContainment() || ref.isContainer()) {
if (value != null) {
updateResource(eo, (EObject) value);
}
if (oldValue != null) {
updateResource(eo, oldValue);
}
}
assert eo.eResource() != null;
assert value == null || ((EObject) value).eResource() != null;
assert oldValue == null || oldValue.eResource() != null;
}
} else {
final EClassifier sfType = sf.getEType();
if (sfType instanceof EEnum) {
final EEnum eEnum = (EEnum) sfType;
if (value instanceof EnumLiteral) {
eo.eSet(sf, ((EnumLiteral) value).getEnumerator(eEnum));
} else {
eo.eSet(sf, value);
}
} else {
eo.eSet(sf, value);
}
}
}
/**
* Sets the <code>value</code> of <code>eo.sf</code>. Assumes <code>sf</code> has a multiplicity &gt; 1.
*
* @param env
* @param eo
* @param sf
* @param value
*/
@SuppressWarnings("unchecked")
private static void setMany(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Collection<?> value) {
assert sf.isMany();
final Collection<Object> values = (Collection<Object>) eo.eGet(sf);
if (!values.isEmpty()) {
if (sf instanceof EReference) {
final List<Object> vCopy = new ArrayList<Object>(values);
for (EObject v : (List<? extends EObject>) vCopy) {
removeRefValue((EReference) sf, eo, values, v);
}
} else {
values.clear();
}
}
addMany(env, eo, sf, value, -1);
}
/**
* Adds <code>value</code> to <code>eo.sf</code>. Assumes <code>sf</code> has a multiplicity &gt; 1.
*
* @param env
* @param eo
* @param sf
* @param value
* @param index
* the insertion index (-1 for end)
*/
@SuppressWarnings("unchecked")
private static void addMany(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Object value, final int index) {
assert sf.isMany();
final Collection<Object> values = (Collection<Object>) eo.eGet(sf); // All EMF collections are ELists
if (sf instanceof EReference) {
final EReference ref = (EReference) sf;
addRefValue(env, ref, eo, values, (EObject) value, index, isAllowInterModelReferences(env, eo));
} else {
final EClassifier sfType = sf.getEType();
if (sfType instanceof EEnum) {
addEnumValue((EEnum) sfType, values, value, index);
} else if (index > -1) {
((List<Object>) values).add(index, value);
} else {
values.add(value);
}
}
}
/**
* Adds all <code>value</code> elements to <code>eo.sf</code>. Assumes <code>sf</code> has a multiplicity &gt; 1.
*
* @param env
* @param eo
* @param sf
* @param value
* @param index
* the insertion index (-1 for end)
*/
@SuppressWarnings("unchecked")
private static void addMany(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Collection<?> value, final int index) {
assert sf.isMany();
final Collection<Object> values = (Collection<Object>) eo.eGet(sf);
if (sf instanceof EReference) {
final EReference ref = (EReference) sf;
final boolean allowInterModelReferences = isAllowInterModelReferences(env, eo);
final Collection<?> srcValues = ref.isContainment() ? new ArrayList<Object>(value) : value;
if (index > -1) {
int currentIndex = index;
for (Object v : srcValues) {
checkValueTypeIsEObject(env, ref, v);
addRefValue(env, ref, eo, values, (EObject) v, currentIndex++, allowInterModelReferences);
}
} else {
for (Object v : srcValues) {
checkValueTypeIsEObject(env, ref, v);
addRefValue(env, ref, eo, values, (EObject) v, -1, allowInterModelReferences);
}
}
} else {
final EClassifier sfType = sf.getEType();
if (sfType instanceof EEnum) {
final EEnum eEnum = (EEnum) sfType;
if (index > -1) {
int currentIndex = index;
for (Object v : value) {
addEnumValue(eEnum, values, v, currentIndex++);
}
} else {
for (Object v : value) {
addEnumValue(eEnum, values, v, -1);
}
}
} else if (index > -1) {
((List<Object>) values).addAll(index, value);
} else {
values.addAll(value);
}
}
}
/**
* Checks that the value is an instance of {@link EObject}.
*
* @param env
* the current {@link ExecEnv}
* @param ref
* the {@link EReference} to assign to
* @param v
* the value to check
*/
private static void checkValueTypeIsEObject(final ExecEnv env, final EReference ref, final Object v) {
if (!(v instanceof EObject)) {
throw new IllegalArgumentException(String.format(
"Cannot add/remove values of type %s to/from multi-valued field %s::%s",
getTypeName(env, v.getClass()), ref.getEContainingClass().getName(), ref.getName()));
}
}
/**
* Removes the <code>value</code> from <code>eo.sf</code>. Assumes <code>sf</code> has a multiplicity &gt; 1.
*
* @param env
* @param eo
* @param sf
* @param value
*/
@SuppressWarnings("unchecked")
private static void removeMany(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Object value) {
assert sf.isMany();
final Collection<Object> values = (Collection<Object>) eo.eGet(sf);
if (sf instanceof EReference) {
final EReference ref = (EReference) sf;
removeRefValue(ref, eo, values, (EObject) value);
} else {
final EClassifier sfType = sf.getEType();
if (sfType instanceof EEnum) {
final EEnum eEnum = (EEnum) sfType;
removeEnumValue(eEnum, values, value);
} else {
values.remove(value);
}
}
}
/**
* Removes all elements of <code>value</code> from <code>eo.sf</code>. Assumes <code>sf</code> has a multiplicity &gt; 1.
*
* @param env
* @param eo
* @param sf
* @param value
*/
@SuppressWarnings("unchecked")
private static void removeMany(final ExecEnv env, final EObject eo, final EStructuralFeature sf, final Collection<?> value) {
assert sf.isMany();
final Collection<Object> values = (Collection<Object>) eo.eGet(sf);
if (sf instanceof EReference) {
final EReference ref = (EReference) sf;
final Collection<?> srcValues = ref.isContainment() ? new ArrayList<Object>(value) : value;
for (Object v : srcValues) {
checkValueTypeIsEObject(env, ref, v);
removeRefValue(ref, eo, values, (EObject) v);
}
} else {
final EClassifier sfType = sf.getEType();
if (sfType instanceof EEnum) {
final EEnum eEnum = (EEnum) sfType;
for (Object v : value) {
removeEnumValue(eEnum, values, v);
}
} else {
values.removeAll(value);
}
}
}
/**
* Adds <code>v</code> to <code>values</code>. Performs enumerator conversion.
*
* @param eEnum
* The enumeration type
* @param values
* @param v
* @param index
* the insertion index (-1 for end)
*/
private static void addEnumValue(final EEnum eEnum, final Collection<Object> values, final Object v, final int index) {
final Object v2;
if (v instanceof EnumLiteral) {
v2 = ((EnumLiteral) v).getEnumerator(eEnum);
} else {
v2 = v;
}
if (index > -1) {
((List<Object>) values).add(index, v2);
} else {
values.add(v2);
}
}
/**
* Removes <code>v</code> from <code>values</code>. Performs enumerator conversion.
*
* @param eEnum
* The enumeration type
* @param values
* @param v
*/
private static void removeEnumValue(final EEnum eEnum, final Collection<Object> values, final Object v) {
if (v instanceof EnumLiteral) {
values.remove(((EnumLiteral) v).getEnumerator(eEnum));
} else {
values.remove(v);
}
}
/**
* Adds <code>v</code> to <code>values</code>. Performs constraint checking on <code>v</code>.
*
* @param env
* @param ref
* The reference type
* @param eo
* The object with <code>ref</code> set to <code>values</code>
* @param values
* @param v
* @param index
* the insertion index (-1 for end)
* @param allowInterModelReferences
*/
private static void addRefValue(final ExecEnv env, final EReference ref, final EObject eo, final Collection<Object> values,
final EObject v, final int index, final boolean allowInterModelReferences) {
assert eo.eResource() != null;
assert v.eResource() != null;
if (checkValue(env, eo, ref, v, allowInterModelReferences)) {
if (index > -1) {
((List<Object>) values).add(index, v);
} else {
values.add(v);
}
if (ref.isContainment() || ref.isContainer()) {
updateResource(eo, v);
}
}
assert eo.eResource() != null;
assert v.eResource() != null;
}
/**
* Removes <code>v</code> from <code>values</code>. Performs constraint checking on <code>v</code>.
*
* @param ref
* The reference type
* @param eo
* The object with <code>ref</code> set to <code>values</code>
* @param values
* @param v
*/
private static void removeRefValue(final EReference ref, final EObject eo, final Collection<Object> values, final EObject v) {
assert eo.eResource() != null;
assert v.eResource() != null;
if (values.remove(v) && (ref.isContainment() || ref.isContainer())) {
updateResource(eo, v);
}
assert eo.eResource() != null;
assert v.eResource() != null;
}
/**
* Updates the eResource() for <code>eo</code> and <code>v</code> where necessary
*
* @param eo
* the {@link EObject} for which an {@link EReference} has just been modified
* @param v
* the value of the {@link EReference} that has just been modified
*/
private static void updateResource(final EObject eo, final EObject v) {
if (eo.eResource() == null) {
assert eo.eContainer() == null;
v.eResource().getContents().add(eo);
} else if (v.eResource() == null) {
assert v.eContainer() == null;
eo.eResource().getContents().add(v);
}
if (eo.eContainer() != null) {
eo.eResource().getContents().remove(eo);
}
if (v.eContainer() != null) {
v.eResource().getContents().remove(v);
}
}
/**
* Checks whether the model containing <code>eo</code> allows inter-model references.
*
* @param env
* the {@link ExecEnv} in which to find the model.
* @param eo
* the model element to find the model for.
* @return <code>true</code> iff the model of <code>eo</code> allows inter-model references
*/
private static boolean isAllowInterModelReferences(final ExecEnv env, final EObject eo) {
final Model eoModel = env.getModelOf(eo);
if (eoModel != null) {
return eoModel.isAllowInterModelReferences();
} else {
return true;
}
}
/**
* Checks whether <code>value</code> may be assigned to <code>eo.ref</code>.
*
* @param env
* the current {@link ExecEnv}
* @param eo
* the model element to assign to
* @param ref
* the reference of the model element to assign to
* @param value
* the value to assign
* @param allowInterModelReferences
* whether to allow inter-model references
* @return <code>true</code> iff the value may be assigned
*/
private static boolean checkValue(final ExecEnv env, final EObject eo, final EReference ref, final Object value,
final boolean allowInterModelReferences) {
if (value instanceof EObject) {
assert eo.eResource() != null;
final EObject ev = (EObject) value;
if (eo.eResource() == ev.eResource() || ev.eResource() == null) {
return true;
}
assert ev.eResource() != null;
if (!allowInterModelReferences) {
ATLLogger
.warning(String.format("Cannot set %s::%s to %s for %s: inter-model references are not allowed for this model",
toPrettyString(ref.getEContainingClass(), env), ref.getName(), toPrettyString(value, env),
toPrettyString(eo, env)));
return false;
}
if (ref.isContainer() || ref.isContainment()) {
ATLLogger
.warning(String.format("Cannot set %s::%s to %s for %s: containment references cannot span across models",
toPrettyString(ref.getEContainingClass(), env), ref.getName(), toPrettyString(value, env),
toPrettyString(eo, env)));
return false;
}
final EReference opposite = ref.getEOpposite();
if (opposite != null) {
final Model evModel = env.getInputModelOf(ev);
if (evModel != null) {
ATLLogger.warning(String.format(
"Cannot set %s::%s to %s for %s: inter-model reference with opposite causes changes in input model %s",
toPrettyString(ref.getEContainingClass(), env), ref.getName(), toPrettyString(value, env),
toPrettyString(eo, env), env.getModelID(evModel)));
return false;
}
if (!opposite.isMany()) {
// Single-valued opposites cause changes in their respective opposite,
// i.e. ref, which can belong to eo or another input model element.
final Model oppositeModel = env.getInputModelOf((EObject) ev.eGet(opposite));
if (oppositeModel != null) {
ATLLogger
.warning(String
.format("Cannot set %s::%s to %s for %s: inter-model reference with single-valued opposite causes changes in input model %s",
toPrettyString(ref.getEContainingClass(), env), ref.getName(), toPrettyString(value, env),
toPrettyString(eo, env), env.getModelID(oppositeModel)));
return false;
}
}
}
}
return true; // any type errors can be delegated to EMF
}
/**
* Retrieves the types of <code>args</code>.
*
* @param args
* @return the types of <code>args</code>
*/
public static Object[] getArgumentTypes(final Object[] args) {
final int argcount = args.length;
final Object[] argTypes = new Object[argcount];
for (int i = 0; i < argcount; i++) {
argTypes[i] = getArgumentType(args[i]);
}
return argTypes;
}
/**
* Retrieves the type of <code>arg</code>.
*
* @param arg
* @return the type of <code>arg</code>
*/
public static Object getArgumentType(final Object arg) {
if (arg instanceof EObject) {
return ((EObject) arg).eClass();
} else if (arg != null) {
return arg.getClass();
}
// null is an instance of Void for the purpose of our multi-method semantics
return Void.TYPE;
}
/**
* Invokes native Java method <code>opname</code> on <code>self</code> with arguments <code>args</code>.
*
* @param frame
* the current stack frame
* @param self
* the object on which to invoke the method
* @param opname
* the method name
* @param args
* the method arguments
* @return the method result
*/
public static Object invokeNative(final StackFrame frame, final Object self, final String opname, final Object[] args) {
final Method method = EMFTVMUtil.findNativeMethod(self == null ? Void.TYPE : self.getClass(), opname,
EMFTVMUtil.getArgumentClasses(args), false);
if (method != null) {
return invokeNative(frame, self, method, args);
}
throw new UnsupportedOperationException(String.format("%s::%s(%s)", EMFTVMUtil.getTypeName(frame.getEnv(), getArgumentType(self)),
opname, EMFTVMUtil.getTypeNames(frame.getEnv(), getArgumentTypes(args))));
}
/**
* Invokes native Java <code>method</code> on <code>self</code> with arguments <code>args</code>.
*
* @param frame
* the current stack frame
* @param self
* the object on which to invoke the method
* @param method
* the method
* @param args
* the method arguments
* @return the method result
*/
public static Object invokeNative(final StackFrame frame, final Object self, Method method, final Object[] args) {
// Fix for Bug # 461445: EMFTVM cannot invoke Java methods on instances of private classes:
method = findRootMethod(method);
final StackFrame subFrame = frame.prepareNativeArgs(method, self, args);
try {
return emf2vm(frame.getEnv(), self instanceof EObject ? (EObject) self : null, method.invoke(self, args));
} catch (InvocationTargetException e) {
final Throwable target = e.getTargetException();
if (target instanceof VMException) {
throw (VMException) target;
} else {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, target);
}
} catch (VMException e) {
throw e;
} catch (Exception e) {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, e);
}
}
/**
* Invokes native Java method <code>opname</code> on <code>self</code> with argument <code>arg</code>.
*
* @param frame
* the current stack frame
* @param self
* the object on which to invoke the method
* @param opname
* the method name
* @param arg
* the method argument
* @return the method result
*/
public static Object invokeNative(final StackFrame frame, final Object self, final String opname, final Object arg) {
final Method method = EMFTVMUtil.findNativeMethod(self == null ? Void.TYPE : self.getClass(), opname,
arg == null ? Void.TYPE : arg.getClass(), false);
if (method != null) {
return invokeNative(frame, self, method, arg);
}
throw new UnsupportedOperationException(String.format("%s::%s(%s)", EMFTVMUtil.getTypeName(frame.getEnv(), getArgumentType(self)),
opname, EMFTVMUtil.getTypeName(frame.getEnv(), getArgumentType(arg))));
}
/**
* Invokes native Java <code>method</code> on <code>self</code> with argument <code>arg</code>.
*
* @param frame
* the current stack frame
* @param self
* the object on which to invoke the method
* @param method
* the method
* @param arg
* the method argument
* @return the method result
*/
public static Object invokeNative(final StackFrame frame, final Object self, Method method, Object arg) {
// Fix for Bug # 461445: EMFTVM cannot invoke Java methods on instances of private classes:
method = findRootMethod(method);
StackFrame subFrame = frame.prepareNativeContext(method, self);
if (arg instanceof CodeBlock) {
if (subFrame == null) {
subFrame = new StackFrame(frame, method);
}
((CodeBlock) arg).setParentFrame(subFrame);
} else if (arg instanceof EnumLiteral) {
arg = convertEnumLiteral((EnumLiteral) arg, method.getParameterTypes()[0]);
}
try {
return emf2vm(frame.getEnv(), self instanceof EObject ? (EObject) self : null, method.invoke(self, arg));
} catch (InvocationTargetException e) {
final Throwable target = e.getTargetException();
if (target instanceof VMException) {
throw (VMException) target;
} else {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, target);
}
} catch (VMException e) {
throw e;
} catch (Exception e) {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, e);
}
}
/**
* Invokes native Java method <code>opname</code> on <code>self</code> without arguments.
*
* @param frame
* the current stack frame
* @param self
* the object on which to invoke the method
* @param opname
* the method name
* @return the method result
*/
public static Object invokeNative(final StackFrame frame, final Object self, final String opname) {
final Method method = EMFTVMUtil.findNativeMethod(self == null ? Void.TYPE : self.getClass(), opname, false);
if (method != null) {
return invokeNative(frame, self, method);
}
throw new UnsupportedOperationException(String.format("%s::%s()", EMFTVMUtil.getTypeName(frame.getEnv(), getArgumentType(self)),
opname));
}
/**
* Invokes native Java <code>method</code> on <code>self</code> without arguments.
*
* @param frame
* the current stack frame
* @param self
* the object on which to invoke the method
* @param method
* the method
* @return the method result
*/
public static Object invokeNative(final StackFrame frame, final Object self, Method method) {
// Fix for Bug # 461445: EMFTVM cannot invoke Java methods on instances of private classes:
method = findRootMethod(method);
final StackFrame subFrame = frame.prepareNativeContext(method, self);
try {
return emf2vm(frame.getEnv(), self instanceof EObject ? (EObject) self : null, method.invoke(self));
} catch (InvocationTargetException e) {
final Throwable target = e.getTargetException();
if (target instanceof VMException) {
throw (VMException) target;
} else {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, target);
}
} catch (VMException e) {
throw e;
} catch (Exception e) {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, e);
}
}
/**
* Invokes static native Java method <code>opname</code> with arguments <code>args</code>.
*
* @param frame
* the current stack frame
* @param type
* the class in which the static method is defined
* @param opname
* the method name
* @param args
* the method arguments
* @return the method result
*/
public static Object invokeNativeStatic(final StackFrame frame, final Class<?> type, final String opname, final Object[] args) {
final Method method = EMFTVMUtil.findNativeMethod(type, opname, EMFTVMUtil.getArgumentClasses(args), true);
if (method != null) {
final StackFrame subFrame = frame.prepareNativeArgs(method, args);
try {
return emf2vm(frame.getEnv(), null, method.invoke(type, args));
} catch (InvocationTargetException e) {
final Throwable target = e.getTargetException();
if (target instanceof VMException) {
throw (VMException) target;
} else {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, target);
}
} catch (VMException e) {
throw e;
} catch (Exception e) {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, e);
}
}
throw new UnsupportedOperationException(String.format("static %s::%s(%s)", EMFTVMUtil.getTypeName(frame.getEnv(), type), opname,
EMFTVMUtil.getTypeNames(frame.getEnv(), getArgumentTypes(args))));
}
/**
* Invokes static native Java method <code>opname</code> with argument <code>arg</code>.
*
* @param frame
* the current stack frame
* @param type
* the class in which the static method is defined
* @param opname
* the method name
* @param arg
* the method arguments
* @return the method result
*/
public static Object invokeNativeStatic(final StackFrame frame, final Class<?> type, final String opname, Object arg) {
final Method method = EMFTVMUtil.findNativeMethod(type, opname, arg == null ? Void.TYPE : arg.getClass(), true);
if (method != null) {
StackFrame subFrame = null;
if (arg instanceof CodeBlock) {
subFrame = new StackFrame(frame, method);
((CodeBlock) arg).setParentFrame(subFrame);
} else if (arg instanceof EnumLiteral) {
arg = convertEnumLiteral((EnumLiteral) arg, method.getParameterTypes()[0]);
}
try {
return emf2vm(frame.getEnv(), null, method.invoke(type, arg));
} catch (InvocationTargetException e) {
final Throwable target = e.getTargetException();
if (target instanceof VMException) {
throw (VMException) target;
} else {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, target);
}
} catch (VMException e) {
throw e;
} catch (Exception e) {
throw new VMException(subFrame == null ? new StackFrame(frame, method) : subFrame, e);
}
}
throw new UnsupportedOperationException(String.format("static %s::%s(%s)", EMFTVMUtil.getTypeName(frame.getEnv(), type), opname,
EMFTVMUtil.getTypeName(frame.getEnv(), getArgumentType(arg))));
}
/**
* Invokes static native Java method <code>opname</code> without arguments.
*
* @param frame
* the current stack frame
* @param type
* the class in which the static method is defined
* @param opname
* the method name
* @return the method result
*/
public static Object invokeNativeStatic(final StackFrame frame, final Class<?> type, final String opname) {
final Method method = EMFTVMUtil.findNativeMethod(type, opname, true);
if (method != null) {
try {
return emf2vm(frame.getEnv(), null, method.invoke(type));
} catch (InvocationTargetException e) {
final Throwable target = e.getTargetException();
if (target instanceof VMException) {
throw (VMException) target;
} else {
throw new VMException(new StackFrame(frame, method), target);
}
} catch (VMException e) {
throw e;
} catch (Exception e) {
throw new VMException(new StackFrame(frame, method), e);
}
}
throw new UnsupportedOperationException(String.format("static %s::%s()", EMFTVMUtil.getTypeName(frame.getEnv(), type), opname));
}
/**
* Looks for a native Java method.
*
* @param context
* The class of the method
* @param opname
* The method name
* @param argTypes
* The types of all arguments
* @param isStatic
* Whether to look for a static method or not
* @return the method if found, null otherwise
* @author <a href="mailto:frederic.jouault@univ-nantes.fr">Frederic Jouault</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
*/
public static Method findNativeMethod(final Class<?> context, final String opname, final Class<?>[] argTypes, final boolean isStatic) {
if (context == Void.TYPE) {
return null; // Java methods cannot be invoked on null, or defined on Void
}
final MethodSignature sig = getMethodSignature(context, opname, argTypes, isStatic);
MethodCacheAccesses++;
final WeakReference<Method> methodRef = METHOD_CACHE.get(sig);
final Method ret = methodRef != null ? methodRef.get() : null;
if (ret != null || methodRef == NULL_METHOD_REFERENCE) {
MethodCacheHits++;
return methodRef.get();
}
return findNativeMethodInternal(context, opname, argTypes, isStatic, sig);
}
/**
* Looks for a native Java method.
*
* @param context
* The class of the method
* @param opname
* The method name
* @param argumentType
* The type of the argument
* @param isStatic
* Whether to look for a static method or not
* @return the method if found, null otherwise
* @author <a href="mailto:frederic.jouault@univ-nantes.fr">Frederic Jouault</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
*/
public static Method findNativeMethod(final Class<?> context, final String opname, final Class<?> argType, final boolean isStatic) {
if (context == Void.TYPE) {
return null; // Java methods cannot be invoked on null, or defined on Void
}
final MethodSignature sig = getMethodSignature(context, opname, argType, isStatic);
MethodCacheAccesses++;
final WeakReference<Method> methodRef = METHOD_CACHE.get(sig);
final Method ret = methodRef != null ? methodRef.get() : null;
if (ret != null || methodRef == NULL_METHOD_REFERENCE) {
MethodCacheHits++;
return ret;
}
return findNativeMethodInternal(context, opname, argType, isStatic, sig);
}
/**
* Looks for a native Java method without arguments.
*
* @param context
* The class of the method
* @param opname
* The method name
* @param isStatic
* Whether to look for a static method or not
* @return the method if found, null otherwise
* @author <a href="mailto:frederic.jouault@univ-nantes.fr">Frederic Jouault</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
*/
public static Method findNativeMethod(final Class<?> context, final String opname, final boolean isStatic) {
if (context == Void.TYPE) {
return null; // Java methods cannot be invoked on null, or defined on Void
}
final MethodSignature sig = getMethodSignature(context, opname, isStatic);
MethodCacheAccesses++;
final WeakReference<Method> methodRef = METHOD_CACHE.get(sig);
Method ret = methodRef != null ? methodRef.get() : null;
if (ret != null || methodRef == NULL_METHOD_REFERENCE) {
MethodCacheHits++;
return ret;
}
return findNativeMethodInternal(context, opname, isStatic, sig);
}
/**
* Looks for a native Java method.
*
* @param context
* The class of the method
* @param opname
* The method name
* @param argTypes
* The types of all arguments
* @param isStatic
* Whether to look for a static method or not
* @param sig
* the method signature
* @return the method if found, null otherwise
*/
private static Method findNativeMethodInternal(final Class<?> context, final String opname,
final Class<?>[] argTypes, final boolean isStatic, final MethodSignature sig) {
Method ret = null;
final Method[] methods = context.getDeclaredMethods();
METHODS: for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if ((Modifier.isStatic(method.getModifiers()) == isStatic) && method.getName().equals(opname)) {
Class<?>[] pts = method.getParameterTypes();
if (pts.length == argTypes.length) {
boolean ok = true;
for (int j = 0; (j < pts.length) && ok; j++) {
if (!checkParameterType(argTypes[j], pts[j])) {
ok = false;
break;
}
}
if (ok) {
ret = method;
break METHODS;
}
}
}
}
if ((ret == null) && !isStatic && (context.getSuperclass() != null)) {
ret = findNativeMethodInternal(context.getSuperclass(), opname, argTypes, isStatic, sig);
} else {
METHOD_CACHE.put(sig, ret != null ? new WeakReference<Method>(ret) : NULL_METHOD_REFERENCE);
}
return ret;
}
/**
* Looks for a native Java method.
*
* @param context
* The class of the method
* @param opname
* The method name
* @param argType
* The type of the argument
* @param isStatic
* Whether to look for a static method or not
* @param sig
* the method signature
* @return the method if found, null otherwise
*/
private static Method findNativeMethodInternal(final Class<?> context, final String opname, final Class<?> argType,
final boolean isStatic, final MethodSignature sig) {
Method ret = null;
final Method[] methods = context.getDeclaredMethods();
METHODS: for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if ((Modifier.isStatic(method.getModifiers()) == isStatic) && method.getName().equals(opname)) {
Class<?>[] pts = method.getParameterTypes();
if (pts.length == 1) {
if (checkParameterType(argType, pts[0])) {
ret = method;
break METHODS;
}
}
}
}
if ((ret == null) && !isStatic && (context.getSuperclass() != null)) {
ret = findNativeMethodInternal(context.getSuperclass(), opname, argType, isStatic, sig);
} else {
METHOD_CACHE.put(sig, ret != null ? new WeakReference<Method>(ret) : NULL_METHOD_REFERENCE);
}
return ret;
}
/**
* Looks for a native Java method without arguments.
*
* @param context
* The class of the method
* @param opname
* The method name
* @param isStatic
* Whether to look for a static method or not
* @param sig
* the method signature
* @return the method if found, null otherwise
*/
private static Method findNativeMethodInternal(final Class<?> context, final String opname, final boolean isStatic,
final MethodSignature sig) {
Method ret = null;
final Method[] methods = context.getDeclaredMethods();
METHODS: for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if ((Modifier.isStatic(method.getModifiers()) == isStatic) && method.getName().equals(opname)) {
if (method.getParameterTypes().length == 0) {
ret = method;
break METHODS;
}
}
}
if ((ret == null) && !isStatic && (context.getSuperclass() != null)) {
ret = findNativeMethodInternal(context.getSuperclass(), opname, isStatic, sig);
} else {
METHOD_CACHE.put(sig, ret != null ? new WeakReference<Method>(ret) : NULL_METHOD_REFERENCE);
}
return ret;
}
/**
* Checks whether the parameter type is compatible with the argument type
*
* @param argType
* the invocation argument type
* @param pt
* the method parameter type
* @return <code>true</code> if the parameter type is compatible,
* <code>false</code> otherwise
*/
private static boolean checkParameterType(final Class<?> argType, final Class<?> pt) {
if (argType == EnumLiteral.class && Enumerator.class.isAssignableFrom(pt)) {
return true;
}
if (pt.isAssignableFrom(argType)) {
return true;
}
if (pt == boolean.class){
return argType == Boolean.class;
}
if (pt == int.class) {
return argType == Integer.class;
}
if (pt == char.class) {
return argType == Character.class;
}
if (pt == long.class) {
return argType == Long.class;
}
if (pt == float.class) {
return argType == Float.class;
}
if (pt == double.class) {
return argType == Double.class;
}
return argType == Void.TYPE; // any type
}
/**
* Compares 0-parameter <code>op</code> to <code>method</code>.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param method
* the found method
* @return the method if more specific than <code>op</code>, <code>null</code> otherwise
*/
private static Method compareNativeMethod0(final Operation op, final Method method) {
if (op != null && method != null) {
final Class<?> opCtx = op.getEContext().getInstanceClass();
final Class<?> methCtx = NativeTypes.boxedType(method.getDeclaringClass());
if (opCtx == null || methCtx.isAssignableFrom(opCtx) || !opCtx.isAssignableFrom(methCtx)) {
return null;
}
}
return method;
}
/**
* Compares 1-parameter <code>op</code> to <code>method</code>.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param method
* the found method
* @return the method if more specific than <code>op</code>, <code>null</code> otherwise
*/
private static Method compareNativeMethod1(final Operation op, final Method method) {
if (op != null && method != null) {
final Class<?> opCtx = op.getEContext().getInstanceClass();
final Class<?> methCtx = NativeTypes.boxedType(method.getDeclaringClass());
if (opCtx == null) {
return null;
}
if (methCtx.isAssignableFrom(opCtx) || !opCtx.isAssignableFrom(methCtx)) {
if (!methCtx.equals(opCtx)) {
return null;
}
final Class<?> opArgType = op.getParameters().get(0).getEType().getInstanceClass();
final Class<?> methArgType = NativeTypes.boxedType(method.getParameterTypes()[0]);
if (opArgType == null || methArgType.isAssignableFrom(opArgType) || !opArgType.isAssignableFrom(methArgType)) {
return null;
}
}
}
return method;
}
/**
* Compares N-parameter <code>op</code> to <code>method</code>.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param method
* the found method
* @return the method if more specific than <code>op</code>, <code>null</code> otherwise
*/
private static Method compareNativeMethodN(final Operation op, final Method method) {
if (op != null && method != null) {
Class<?> opCtx = op.getEContext().getInstanceClass();
Class<?> methCtx = NativeTypes.boxedType(method.getDeclaringClass());
if (opCtx == null) {
return null;
}
final Class<?>[] parameterTypes = method.getParameterTypes();
final EList<Parameter> parameters = op.getParameters();
final int len = parameterTypes.length;
int i = -1;
while (methCtx.isAssignableFrom(opCtx) || !opCtx.isAssignableFrom(methCtx)) {
i++;
if (!methCtx.equals(opCtx) || i == len) {
return null;
}
opCtx = parameters.get(i).getEType().getInstanceClass();
if (opCtx == null) {
return null;
}
methCtx = NativeTypes.boxedType(parameterTypes[i]);
}
}
return method;
}
/**
* Looks for a native Java method without arguments.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param self
* the object on which to invoke the method
* @param opname
* the method name
* @return the method if found and more specific than <code>op</code>, <code>null</code> otherwise
*/
public static Method findNativeMethod(final Operation op, final Object self, final String opname) {
return compareNativeMethod0(op, findNativeMethod(self == null ? Void.TYPE : self.getClass(), opname, false));
}
/**
* Looks for a native Java method with one argument.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param self
* the object on which to invoke the method
* @param opname
* the method name
* @param arg
* the method argument
* @return the method if found and more specific than <code>op</code>, <code>null</code> otherwise
*/
public static Method findNativeMethod(final Operation op, final Object self, final String opname, final Object arg) {
return compareNativeMethod1(op,
findNativeMethod(self == null ? Void.TYPE : self.getClass(), opname, arg == null ? Void.TYPE : arg.getClass(), false));
}
/**
* Looks for a native Java method with multiple arguments.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param self
* the object on which to invoke the method
* @param opname
* the method name
* @param args
* the method arguments
* @return the method if found and more specific than <code>op</code>, <code>null</code> otherwise
*/
public static Method findNativeMethod(final Operation op, final Object self, final String opname, final Object[] args) {
return compareNativeMethodN(op,
findNativeMethod(self == null ? Void.TYPE : self.getClass(), opname, getArgumentClasses(args), false));
}
/**
* Returns the superclass as well as super-interfaces for <code>type</code>.
*
* @param type
* the type for which to return supertypes
* @return the superclass as well as super-interfaces for <code>type</code>
*/
private static Class<?>[] getSuperTypes(final Class<?> type) {
if (type == null || type == Void.TYPE) {
return new Class<?>[] { Void.TYPE };
}
final Class<?>[] interfaces = type.getInterfaces();
final Class<?> superClass = type.getSuperclass();
final Class<?>[] superTypes;
if (superClass != null) {
superTypes = new Class<?>[interfaces.length + 1];
superTypes[0] = superClass;
System.arraycopy(interfaces, 0, superTypes, 1, interfaces.length);
} else {
superTypes = new Class<?>[interfaces.length];
System.arraycopy(interfaces, 0, superTypes, 0, interfaces.length);
}
return superTypes;
}
/**
* Compares 0-parameter <code>method1</code> to <code>method2</code>.
*
* @param method1
* the previously found method
* @param method2
* the next found method
* @return <code>method2</code> if more specific than <code>method1</code>, <code>method1</code> otherwise
*/
private static Method compareNativeMethod0(final Method method1, final Method method2) {
if (method2 == null) {
return method1;
}
if (method1 != null) {
final Class<?> meth1Ctx = NativeTypes.boxedType(method1.getDeclaringClass());
final Class<?> meth2Ctx = NativeTypes.boxedType(method2.getDeclaringClass());
if (meth2Ctx.isAssignableFrom(meth1Ctx) || !meth1Ctx.isAssignableFrom(meth2Ctx)) {
return method1;
}
}
return method2;
}
/**
* Compares 1-parameter <code>method1</code> to <code>method2</code>.
*
* @param method1
* the previously found method
* @param method2
* the next found method
* @return <code>method2</code> if more specific than <code>method1</code>, <code>method1</code> otherwise
*/
private static Method compareNativeMethod1(final Method method1, final Method method2) {
if (method2 == null) {
return method1;
}
if (method1 != null) {
final Class<?> meth1Ctx = NativeTypes.boxedType(method1.getDeclaringClass());
final Class<?> meth2Ctx = NativeTypes.boxedType(method2.getDeclaringClass());
if (meth2Ctx.isAssignableFrom(meth1Ctx) || !meth1Ctx.isAssignableFrom(meth2Ctx)) {
if (!meth2Ctx.equals(meth1Ctx)) {
return method1;
}
final Class<?> meth1ArgType = NativeTypes.boxedType(method1.getParameterTypes()[0]);
final Class<?> meth2ArgType = NativeTypes.boxedType(method2.getParameterTypes()[0]);
if (meth2ArgType.isAssignableFrom(meth1ArgType) || !meth1ArgType.isAssignableFrom(meth2ArgType)) {
return method1;
}
}
}
return method2;
}
/**
* Compares N-parameter <code>op</code> to <code>method</code>.
*
* @param method1
* the previously found EMFTVM {@link Operation}
* @param method2
* the found method
* @return the method if more specific than <code>op</code>, <code>null</code> otherwise
*/
private static Method compareNativeMethodN(final Method method1, final Method method2) {
if (method2 == null) {
return method1;
}
if (method1 != null) {
Class<?> meth1Ctx = NativeTypes.boxedType(method1.getDeclaringClass());
Class<?> meth2Ctx = NativeTypes.boxedType(method2.getDeclaringClass());
final Class<?>[] parameterTypes1 = method1.getParameterTypes();
final Class<?>[] parameterTypes2 = method2.getParameterTypes();
final int len = parameterTypes1.length;
int i = -1;
while (meth2Ctx.isAssignableFrom(meth1Ctx) || !meth1Ctx.isAssignableFrom(meth2Ctx)) {
i++;
if (!meth2Ctx.equals(meth1Ctx) || i == len) {
return method1;
}
meth1Ctx = NativeTypes.boxedType(parameterTypes1[i]);
meth2Ctx = NativeTypes.boxedType(parameterTypes2[i]);
}
}
return method2;
}
/**
* Looks for a native superclass Java method without arguments.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param context
* the context for which to find the superclass method
* @param opname
* the method name
* @return the method if found and more specific than <code>op</code>, <code>null</code> otherwise
*/
public static Method findNativeSuperMethod(final Operation op, final Class<?> context, final String opname) {
Method method = null;
for (Class<?> superCtx : getSuperTypes(context)) {
method = compareNativeMethod0(method, findNativeMethod(superCtx, opname, false));
}
return compareNativeMethod0(op, method);
}
/**
* Looks for a native superclass Java method with one argument.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param context
* the context for which to find the superclass method
* @param name
* the method name
* @param arg
* the method argument
* @return the method if found and more specific than <code>op</code>, <code>null</code> otherwise
*/
public static Method findNativeSuperMethod(final Operation op, final Class<?> context, final String opname, final Object arg) {
final Class<?> argType = arg == null ? Void.TYPE : arg.getClass();
Method method = null;
for (Class<?> superCtx : getSuperTypes(context)) {
method = compareNativeMethod1(method, findNativeMethod(superCtx, opname, argType, false));
}
return compareNativeMethod1(op, method);
}
/**
* Looks for a native superclass Java method with multiple arguments.
*
* @param op
* the previously found EMFTVM {@link Operation}
* @param context
* the context for which to find the superclass method
* @param name
* the method name
* @param args
* the method arguments
* @return the method if found and more specific than <code>op</code>, <code>null</code> otherwise
*/
public static Method findNativeSuperMethod(final Operation op, final Class<?> context, final String opname, final Object[] args) {
final Class<?>[] argTypes = getArgumentClasses(args);
Method method = null;
for (Class<?> superCtx : getSuperTypes(context)) {
method = compareNativeMethodN(method, findNativeMethod(superCtx, opname, argTypes, false));
}
return compareNativeMethodN(op, method);
}
/**
* Looks for a native Java constructor.
*
* @param context
* The class of the method
* @param argumentTypes
* The types of all arguments
* @return the constructor if found, <code>null</code> otherwise
* @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
*/
public static Constructor<?> findConstructor(final Class<?> context, final Class<?>[] argTypes) {
if (context == Void.TYPE) {
return null; // Java methods cannot be invoked on null, or defined on Void
}
Constructor<?> ret = null;
final Constructor<?>[] constructors = context.getConstructors();
CONSTRUCTOR: for (int i = 0; i < constructors.length; i++) {
Constructor<?> constructor = constructors[i];
Class<?>[] pts = constructor.getParameterTypes();
if (pts.length == argTypes.length) {
boolean ok = true;
for (int j = 0; (j < pts.length) && ok; j++) {
if (argTypes[j] == EnumLiteral.class && Enumerator.class.isAssignableFrom(pts[j])) {
continue;
}
if (!pts[j].isAssignableFrom(argTypes[j])) {
if (pts[j] == boolean.class)
ok = argTypes[j] == Boolean.class;
else if (pts[j] == int.class)
ok = argTypes[j] == Integer.class;
else if (pts[j] == char.class)
ok = argTypes[j] == Character.class;
else if (pts[j] == long.class)
ok = argTypes[j] == Long.class;
else if (pts[j] == float.class)
ok = argTypes[j] == Float.class;
else if (pts[j] == double.class)
ok = argTypes[j] == Double.class;
else
ok = argTypes[j] == Void.TYPE; // any type
}
}
if (ok) {
ret = constructor;
break CONSTRUCTOR;
}
}
}
return ret;
}
/**
* Generates a signature to store methods.
*
* @param context
* the method declaring class
* @param name
* the method name
* @param argumentTypes
* the argument types
* @param isStatic
* whether the method is static
* @return The method signature
* @author <a href="mailto:frederic.jouault@univ-nantes.fr">Frederic
* Jouault</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
*/
private static MethodSignature getMethodSignature(final Class<?> context, final String name,
final Class<?>[] argumentTypes, final boolean isStatic) {
return new MethodSignature(context, name, argumentTypes, isStatic);
}
/**
* Generates a signature to store methods.
*
* @param context
* the method declaring class
* @param name
* the method name
* @param argumentType
* the (single) argument type
* @param isStatic
* whether the method is static
* @param isStatic
* @return The method signature
* @author <a href="mailto:frederic.jouault@univ-nantes.fr">Frederic
* Jouault</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
*/
private static MethodSignature getMethodSignature(final Class<?> context, final String name,
final Class<?> argumentType, final boolean isStatic) {
return new MethodSignature(context, name, new Class<?>[] { argumentType }, isStatic);
}
/**
* Generates a signature to store methods.
*
* @param context
* the method declaring class
* @param name
* the method name
* @param isStatic
* whether the method is static
* @return The method signature
* @author <a href="mailto:frederic.jouault@univ-nantes.fr">Frederic
* Jouault</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
*/
private static MethodSignature getMethodSignature(final Class<?> context, final String name,
final boolean isStatic) {
return new MethodSignature(context, name, null, isStatic);
}
/**
* Retrieves the classes of <code>args</code>.
*
* @param args
* @return the classes of <code>args</code>
*/
public static Class<?>[] getArgumentClasses(final Object[] args) {
final int argcount = args.length;
final Class<?>[] argTypes = new Class<?>[argcount];
for (int i = 0; i < argcount; i++) {
argTypes[i] = args[i] == null ? Void.TYPE : args[i].getClass();
}
return argTypes;
}
/**
* Writes <code>string</code> to <code>path</code> with the given <code>charset</code>.
*
* @param string
* the string to write
* @param path
* the path of the file to write to
* @param charset
* the character set to use, or use default when null
* @return true on success
* @throws IOException
* when writing fails
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
*/
public static boolean writeToWithCharset(final String string, final String path, final String charset) throws IOException {
final File file = getFile(path);
if (file.getParentFile() != null) {
file.getParentFile().mkdirs();
}
final PrintStream out;
if (charset == null) {
out = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)), true);
} else {
out = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)), true, charset);
}
out.print(string);
out.close();
return true;
}
/**
* Returns the file with the given <code>path</code> in the workspace, or the file in the filesystem if the workspace is not available.
*
* @param path
* the absolute or relative path to a file.
* @return the file in the workspace, or the file in the filesystem if the workspace is not available.
* @author <a href="mailto:dennis.wagelaar@vub.ac.be">Dennis Wagelaar</a>
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
*/
public static File getFile(final String path) {
try {
final WorkspaceUtil wsUtil = (WorkspaceUtil) Class.forName(WORKSPACE_UTIL_IMPL).newInstance();
final String wsPath = wsUtil.getWorkspaceLocation(path);
if (wsPath != null) {
return new File(wsPath);
}
} catch (InstantiationException e) {
ATLLogger.fine(e.getMessage());
} catch (IllegalAccessException e) {
ATLLogger.fine(e.getMessage());
} catch (ClassNotFoundException e) {
ATLLogger.fine(e.getMessage());
}
ATLLogger.info("Could not find workspace root; falling back to native java.io.File path resolution");
return new File(path);
}
/**
* Creates a new {@link Operation}.
*
* @param isStatic
* whether the created operation is static
* @param name
* operation name
* @param context
* operation context [type model, type name]
* @param returnType
* operation return [type model, type name]
* @param parameters
* operations parameters: [[[name], [type model, type name]], ...]
* @param body
* operation body
* @return a new {@link Operation}.
* @see Types
*/
public static Operation createOperation(final boolean isStatic, final String name, final String[] context, final String[] returnType,
final String[][][] parameters, final CodeBlock body) {
final EmftvmFactory factory = EmftvmFactory.eINSTANCE;
final Operation op = factory.createOperation();
op.setStatic(isStatic);
op.setName(name);
op.setContextModel(context[0]);
op.setContext(context[1]);
op.setTypeModel(returnType[0]);
op.setType(returnType[1]);
final EList<Parameter> pars = op.getParameters();
final EList<LocalVariable> locals = body.getLocalVariables();
if (!isStatic) {
LocalVariable self = factory.createLocalVariable();
self.setName("self");
self.setTypeModel(context[0]);
self.setType(context[1]);
locals.add(self);
}
for (String[][] par : parameters) {
Parameter p = factory.createParameter();
p.setName(par[0][0]);
p.setTypeModel(par[1][0]);
p.setType(par[1][1]);
pars.add(p);
LocalVariable lv = factory.createLocalVariable();
lv.setName(par[0][0]);
lv.setTypeModel(par[1][0]);
lv.setType(par[1][1]);
locals.add(lv);
}
op.setBody(body);
return op;
}
/**
* Creates a new {@link Field}.
*
* @param name
* field name
* @param isStatic
* whether the field is static
* @param context
* field context [type model, type name]
* @param type
* field [type model, type name]
* @param initialiser
* field initialiser codeblock
* @return a new {@link Field}.
* @see Types
*/
public static Field createField(final String name, final boolean isStatic, final String[] context, final String[] type,
final CodeBlock initialiser) {
final Field f = EmftvmFactory.eINSTANCE.createField();
f.setName(name);
f.setContextModel(context[0]);
f.setContext(context[1]);
f.setTypeModel(type[0]);
f.setType(type[1]);
f.setInitialiser(initialiser);
f.setStatic(isStatic);
return f;
}
/**
* Retrieves the transitive closure of <code>field</code> on <code>object</code>.
*
* @param object
* the object on which to retrieve <code>field</code>
* @param field
* the field for which to retrieve the value
* @param frame
* the current {@link StackFrame}
* @param result
* the intermediate list of values
* @return the updated result
*/
@SuppressWarnings("unchecked")
public static LazyList<Object> getTrans(final Object object, final Field field, final StackFrame frame, final LazyList<Object> result) {
LazyList<Object> newResult = result;
final Object value = field.getValue(object, frame);
if (value instanceof List<?>) {
final List<Object> cvalue = (List<Object>) value;
newResult = newResult.union(new LazyListOnList<Object>(cvalue));
for (Object v : cvalue) {
newResult = getTrans(v, field, frame, newResult);
}
} else if (value instanceof Collection<?>) {
final Collection<Object> cvalue = (Collection<Object>) value;
newResult = newResult.union(new LazyListOnCollection<Object>(cvalue));
for (Object v : cvalue) {
newResult = getTrans(v, field, frame, newResult);
}
} else if (value != null) {
newResult = newResult.append(value);
newResult = getTrans(value, field, frame, newResult);
}
return newResult;
}
/**
* Retrieves the transitive closure of <code>sf</code> on <code>object</code>.
*
* @param object
* the object on which to retrieve <code>sf</code>
* @param sf
* the structural feature for which to retrieve the value
* @param env
* the current {@link ExecEnv}
* @param result
* the intermediate list of values
* @return the updated result
*/
@SuppressWarnings("unchecked")
public static LazyList<Object> getTrans(final EObject object, final EStructuralFeature sf, final ExecEnv env,
final LazyList<Object> result) {
if (!sf.getEContainingClass().isSuperTypeOf(object.eClass())) {
return result; // feature does not apply to object
}
LazyList<Object> newResult = result;
final Object value = get(env, object, sf);
if (value instanceof LazyList<?>) {
final LazyList<Object> cvalue = (LazyList<Object>) value;
newResult = newResult.union(cvalue);
for (Object v : cvalue) {
if (v instanceof EObject) {
newResult = getTrans((EObject) v, sf, env, newResult);
}
}
} else if (value != null) {
assert !(value instanceof Collection<?>); // All collections should be LazyLists
if (value instanceof Enumerator) {
newResult = newResult.append(new EnumLiteral(value.toString()));
} else {
newResult = newResult.append(value);
if (value instanceof EObject) {
newResult = getTrans((EObject) value, sf, env, newResult);
}
}
}
return newResult;
}
/**
* Retrieves the transitive closure of <code>field</code> on <code>object</code>.
*
* @param object
* the object on which to retrieve <code>field</code>
* @param field
* the field for which to retrieve the value
* @param result
* the intermediate list of values
* @return the updated result
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
@SuppressWarnings("unchecked")
public static LazyList<Object> getTrans(final Object object, final java.lang.reflect.Field field, final LazyList<Object> result)
throws IllegalArgumentException, IllegalAccessException {
if (!field.getDeclaringClass().isAssignableFrom(object.getClass())) {
return result; // field does not apply to object
}
LazyList<Object> newResult = result;
final Object value = field.get(object);
if (value instanceof LazyList<?>) {
final LazyList<Object> cvalue = (LazyList<Object>) value;
newResult = newResult.union(cvalue);
for (Object v : cvalue) {
newResult = getTrans(v, field, newResult);
}
} else if (value instanceof List<?>) {
final List<Object> cvalue = (List<Object>) value;
newResult = newResult.union(new LazyListOnList<Object>(cvalue));
for (Object v : cvalue) {
newResult = getTrans(v, field, newResult);
}
} else if (value instanceof Collection<?>) {
final Collection<Object> cvalue = (Collection<Object>) value;
newResult = newResult.union(new LazyListOnCollection<Object>(cvalue));
for (Object v : cvalue) {
newResult = getTrans(v, field, newResult);
}
} else if (value != null) {
newResult = newResult.append(value);
newResult = getTrans(value, field, newResult);
}
return newResult;
}
/**
* Tries to convert literal to an instance of type.
*
* @param literal
* the enum literal to convert
* @param type
* the type to instantiate
* @return an instance of type, or literal if conversion failed
*/
static Object convertEnumLiteral(final EnumLiteral literal, final Class<?> type) {
if (Enumerator.class.isAssignableFrom(type)) {
try {
final String litName = literal.getName();
final java.lang.reflect.Field valuesField = type.getDeclaredField("VALUES");
final Object values = valuesField.get(null);
if (values instanceof Collection<?>) {
for (Object value : (Collection<?>) values) {
if (value instanceof Enumerator) {
if (litName.equals(((Enumerator) value).getName()) || litName.equals(value.toString())) {
return value;
}
}
}
}
// Ignore exceptions; just don't convert here
} catch (SecurityException e) {
// do nothing
} catch (NoSuchFieldException e) {
// do nothing
} catch (IllegalArgumentException e) {
// do nothing
} catch (IllegalAccessException e) {
// do nothing
}
}
return literal;
}
/**
* Returns the {@link Locale} for the given <code>locale</code> string.
*
* @param locale
* the locale string (e.g. "nl_BE", "es_ES_Traditional_WIN")
* @return the {@link Locale} for the given <code>locale</code> string
*/
public static Locale getLocale(final String locale) {
final StringTokenizer st = new StringTokenizer(locale, "_");
final String language = st.nextToken();
if (st.hasMoreTokens()) {
final String country = st.nextToken();
if (st.hasMoreTokens()) {
final String variant = st.nextToken("\n");
return new Locale(language, country, variant);
}
return new Locale(language, country);
}
return new Locale(language);
}
/**
* Registers all {@link EPackage} nsURIs in <code>rs</code> in the local <code>rs</code> {@link EPackage.Registry}. Sets the
* {@link EPackage} nsURI to the {@link EPackage} name if not set.
*
* @param rs
* the {@link ResourceSet}
*/
public static void registerEPackages(final ResourceSet rs) {
final EPackage.Registry registry = rs.getPackageRegistry();
for (Iterator<Object> i = EcoreUtil.getAllContents(rs, true); i.hasNext();) {
Object object = i.next();
if (object instanceof EPackage) {
EPackage p = (EPackage) object;
// force existence of nsURI
String nsURI = p.getNsURI();
if (nsURI == null) {
nsURI = p.getName();
p.setNsURI(nsURI);
}
// overwrite with current value to prevent aliasing problems
registry.put(nsURI, p);
} else if (object instanceof EDataType) {
adaptDataType((EDataType) object);
}
}
}
/**
* Adapts the given {@link EDataType} if necessary.
* @param dt the {@link EDataType} to adapt
*/
private static void adaptDataType(final EDataType dt) {
String icn = dt.getInstanceClassName();
if (icn == null) {
final String tname = dt.getName();
if (tname.equals("Boolean")) { //$NON-NLS-1$
icn = "boolean"; //$NON-NLS-1$
} else if (tname.equals("Double") || tname.equals("Real")) { //$NON-NLS-1$ //$NON-NLS-2$
icn = "java.lang.Double"; //$NON-NLS-1$
} else if (tname.equals("Float")) { //$NON-NLS-1$
icn = "java.lang.Float"; //$NON-NLS-1$
} else if (tname.equals("Integer")) { //$NON-NLS-1$
icn = "java.lang.Integer"; //$NON-NLS-1$
} else if (tname.equals("String")) { //$NON-NLS-1$
icn = "java.lang.String"; //$NON-NLS-1$
}
if (icn != null) {
dt.setInstanceClassName(icn);
}
}
}
/**
* Finds the root {@link Class} declaration for the given <code>method</code>.
* @param method the method for which to find the root declaration
* @return the root {@link Method}
*/
public static Method findRootMethod(final Method method) {
if (method == null) {
return null;
}
RootMethodCacheAccesses++;
final WeakReference<Method> rootMethodReference = ROOT_METHOD_CACHE.get(method);
Method rootMethod = rootMethodReference != null ? rootMethodReference.get() : null;
if (rootMethod != null) {
RootMethodCacheHits++;
return rootMethod;
}
rootMethod = findRootMethodInner(method);
ROOT_METHOD_CACHE.put(method, new WeakReference<Method>(rootMethod));
return rootMethod;
}
/**
* Finds the root {@link Class} declaration for the given <code>method</code>.
* @param method the method for which to find the root declaration
* @return the root {@link Method}
*/
private static Method findRootMethodInner(Method method) {
final int methodModifiers = getRelevantModifiers(method);
Class<?> dc = method.getDeclaringClass();
java.util.Set<Class<?>> dis = new LinkedHashSet<Class<?>>(
Arrays.<Class<?>> asList(dc.getInterfaces()));
while ((dc = dc.getSuperclass()) != null) {
try {
Method superMethod = dc.getDeclaredMethod(method.getName(), method.getParameterTypes());
if (getRelevantModifiers(superMethod) == methodModifiers) {
method = superMethod;
} else {
break;
}
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
}
dis.addAll(Arrays.<Class<?>> asList(dc.getInterfaces()));
}
while (!dis.isEmpty()) {
java.util.Set<Class<?>> newDis = new LinkedHashSet<Class<?>>();
for (Class<?> di : dis) {
try {
// Only replace by method declared in a super-interface
if (di.isAssignableFrom(method.getDeclaringClass())) {
method = di.getDeclaredMethod(method.getName(), method.getParameterTypes());
}
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
}
newDis.addAll(Arrays.<Class<?>> asList(di.getInterfaces()));
}
newDis.removeAll(dis);
dis = newDis;
}
return method;
}
/**
* Returns the relevant modifiers (visibility and static) for the given method.
* @param method the method for which to return the modifiers
* @return the relevant modifiers (visibility and static) for the given method
*/
private static int getRelevantModifiers(final Method method) {
final int methodModifiers = method.getModifiers();
return methodModifiers & (Modifier.PRIVATE + Modifier.PROTECTED + Modifier.PUBLIC + Modifier.STATIC);
}
/**
* Returns the hit rate of the method cache.
*
* @return the hit rate of the method cache, or <code>-1.0</code> if no hits
* were recorded yet
*/
public static double getMethodCacheHitRate() {
return MethodCacheAccesses > 0 ? (double) MethodCacheHits / (double) MethodCacheAccesses : -1.0;
}
/**
* Returns the hit rate of the root method cache.
*
* @return the hit rate of the root method cache, or <code>-1.0</code> if no
* hits were recorded yet
*/
public static double getRootMethodCacheHitRate() {
return RootMethodCacheAccesses > 0 ? (double) RootMethodCacheHits / (double) RootMethodCacheAccesses : -1.0;
}
}