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

it.xsemantics.runtime.XsemanticsRuntimeSystem Maven / Gradle / Ivy

There is a newer version: 1.12.1
Show newest version
/**
 * 
 */
package it.xsemantics.runtime;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.util.PolymorphicDispatcher;

import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Provider;

/**
 * All generated systems will inherit from this class.
 * 
 * @author Lorenzo Bettini
 * 
 */
public class XsemanticsRuntimeSystem {

	// The first two params are RuleEnvironment and RuleApplicationTrace
	protected static final int INDEX_OF_RULE_PARAMETERS = 2;

	// The first param is the RuleApplicationTrace
	protected static final int INDEX_OF_AUX_PARAMETERS = 1;

	@Inject
	protected StringRepresentation stringRepresentation;

	@Inject
	private Provider cacheProvider;

	private XsemanticsCache cache = null;

	/**
	 * @since 1.6
	 */
	public XsemanticsCache getCache() {
		if (cache == null) {
			cache = cacheProvider.get();
		}
		return cache;
	}

	/**
	 * @since 1.6
	 */
	protected  T getFromCache(String methodName, RuleEnvironment environment,
			RuleApplicationTrace trace, XsemanticsProvider provider,
			Object... elements) {
		return getCache().get(methodName, environment, trace, provider, elements);
	}

	protected Predicate getPredicate(String methodName, int numOfArgs) {
		return PolymorphicDispatcher.Predicates.forName(methodName, numOfArgs);
	}

	protected  PolymorphicDispatcher buildPolymorphicDispatcher(
			final String methodName, int numOfArgs) {
		return new PolymorphicDispatcher(
				Collections.singletonList(this), getPredicate(methodName,
						numOfArgs)) {
			@Override
			protected T handleNoSuchMethod(Object... params) {
				throw noSuchMethodException(methodName, params);
			}
		};
	}

	protected  PolymorphicDispatcher> buildPolymorphicDispatcher1(
			String methodName, int numOfArgs, final String judgmentSymbol,
			final String... relationSymbols) {
		return new PolymorphicDispatcher>(
				Collections.singletonList(this), getPredicate(methodName,
						numOfArgs)) {
			@Override
			protected Result handleNoSuchMethod(Object... params) {
				throw noSuchMethodException(
						judgmentSymbol, Arrays.asList(relationSymbols),
						params);
			}
		};
	}

	protected  PolymorphicDispatcher> buildPolymorphicDispatcher2(
			String methodName, int numOfArgs, final String judgmentSymbol,
			final String... relationSymbols) {
		return new PolymorphicDispatcher>(
				Collections.singletonList(this), getPredicate(methodName,
						numOfArgs)) {
			@Override
			protected Result2 handleNoSuchMethod(
					Object... params) {
				throw noSuchMethodException(
						judgmentSymbol, Arrays.asList(relationSymbols),
						params);
			}
		};
	}

	protected  PolymorphicDispatcher> buildPolymorphicDispatcher3(
			String methodName, int numOfArgs, final String judgmentSymbol,
			final String... relationSymbols) {
		return new PolymorphicDispatcher>(
				Collections.singletonList(this), getPredicate(methodName,
						numOfArgs)) {
			@Override
			protected Result3 handleNoSuchMethod(
					Object... params) {
				throw noSuchMethodException(
						judgmentSymbol, Arrays.asList(relationSymbols),
						params);
			}
		};
	}

	public boolean isResultAssignableTo(Object result, Class destinationClass) {
		// null is always assignable
		if (result == null) {
			return true;
		}
		return destinationClass.isAssignableFrom(result.getClass());
	}

	/**
	 * Checks whether the result is assignable to the specified
	 * destinationClass; if not it throws a {@link RuleFailedException}.
	 * 
	 * @param result
	 * @param destinationClass
	 */
	public void checkAssignableTo(Object result, Class destinationClass) {
		if (!isResultAssignableTo(result, destinationClass)) {
			throw newRuleFailedException(stringRep(result)
					+ " cannot be assigned to " + stringRep(destinationClass));
		}
	}

	public void checkParamsNotNull(Object... objects) {
		for (int i = 0; i < objects.length; i++) {
			Object object = objects[i];
			checkNotNull(object);
		}
	}

	/**
	 * Checks that the passed object is not null; if it is null it throws a
	 * {@link RuleFailedException}.
	 * 
	 * @param object
	 */
	public void checkNotNull(Object object) {
		if (object == null) {
			throw newRuleFailedException("passed null object to system");
		}
	}

	public String stringRep(Object object) {
		return stringRepresentation.string(object);
	}

	protected String stringRepForEnv(RuleEnvironment ruleEnvironment) {
		if (ruleEnvironment == null) {
			return "[]";
		}
		return stringRepresentation.string(ruleEnvironment);
	}

	protected String stringRepForParams(Object[] params,
			Iterable relationSymbols) {
		StringBuilder builder = new StringBuilder();
		Iterator it = relationSymbols.iterator();
		for (int i = INDEX_OF_RULE_PARAMETERS; i < params.length; i++) {
			builder.append(stringRep(params[i]));
			if (it.hasNext()) {
				builder.append(" " + it.next() + " ");
			}
		}
		return builder.toString();
	}

	protected String stringRepForParams(Object[] params) {
		StringBuilder builder = new StringBuilder();
		for (int i = INDEX_OF_AUX_PARAMETERS; i < params.length; i++) {
			if (builder.length() > 0) {
				builder.append(", ");
			}
			builder.append(stringRep(params[i]));
		}
		return builder.toString();
	}

	protected RuleFailedException noSuchMethodException(
			final String judgmentSymbol,
			final Iterable relationSymbols, Object... params) {
		return newRuleFailedException("cannot find a rule for "
				+ judgmentSymbol + " "
				+ stringRepForParams(params, relationSymbols));
	}

	protected RuleFailedException noSuchMethodException(final String name,
			Object... params) {
		return newRuleFailedException("cannot find an implementation for "
				+ name + "(" + stringRepForParams(params) + ")");
	}

	public void sneakyThrowRuleFailedException(Exception e) {
		throw extractRuleFailedException(e);
	}

	public void sneakyThrowRuleFailedException(String message) {
		throw newRuleFailedException(message, null);
	}

	public void throwForExplicitFail() {
		throw newRuleFailedException();
	}

	public void throwForExplicitFail(String message,
			ErrorInformation errorInformation) {
		final RuleFailedException ex = newRuleFailedException(message);
		ex.addErrorInformation(errorInformation);
		throw ex;
	}

	public void throwRuleFailedException(String message, String issue,
			Throwable t, ErrorInformation... errorInformations) {
		throw newRuleFailedException(message, issue, t, errorInformations);
	}

	/**
	 * @since 1.5
	 */
	public RuleFailedException newRuleFailedException() {
		return createRuleFailedException(null, null, null);
	}

	/**
	 * @since 1.5
	 */
	public RuleFailedException newRuleFailedException(Throwable t) {
		return createRuleFailedException(t.toString(), null, t);
	}

	/**
	 * @since 1.5
	 */
	public RuleFailedException newRuleFailedException(String message) {
		return createRuleFailedException(message, null, null);
	}

	public RuleFailedException newRuleFailedException(String message,
			String issue, ErrorInformation... errorInformations) {
		return newRuleFailedException(message, issue, null, errorInformations);
	}

	public RuleFailedException newRuleFailedException(String message,
			String issue, Throwable t, ErrorInformation... errorInformations) {
		final RuleFailedException ruleFailedException = createRuleFailedException(
				failed(message), issue, t);
		ruleFailedException.addErrorInformations(errorInformations);
		return ruleFailedException;
	}

	/**
	 * This factory method can be overridden if one needs to provide a custom implementation
	 * of {@link RuleFailedException}.
	 * 
	 * @param message
	 * @param issue
	 * @param t
	 * @return
	 * 
	 * @since 1.5
	 */
	protected RuleFailedException createRuleFailedException(String message,
			String issue, Throwable t) {
		return new RuleFailedException(message, issue, t);
	}

	public RuleFailedException extractRuleFailedException(Exception e) {
		if (e instanceof WrappedException) {
			WrappedException wrappedException = (WrappedException) e;
			Exception exception = wrappedException.exception();
			if (exception instanceof RuleFailedException) {
				return (RuleFailedException) exception;
			}
		} else if (e instanceof RuleFailedException) {
			return (RuleFailedException) e;
		}
		return newRuleFailedException(e);
	}

	protected  Result resultForFailure(Exception e) {
		return new Result(extractRuleFailedException(e));
	}

	protected  Result2 resultForFailure2(
			Exception e) {
		return new Result2(extractRuleFailedException(e));
	}

	protected  Result3 resultForFailure3(
			Exception e) {
		return new Result3(extractRuleFailedException(e));
	}

	protected String failed(String message) {
		return "failed: " + trimIfNotNull(message);
	}

	protected String trimIfNotNull(String message) {
		return message != null ? message.trim() : message;
	}

	protected String ruleName(String ruleName) {
		return trimIfNotNull(ruleName) + ": ";
	}
	
	protected String auxFunName(String ruleName) {
		return trimIfNotNull(ruleName);
	}

	/**
	 * If the passed ruleApplicationTrace is not null, then uses the
	 * traceElementProvider to create an element to add to the trace.
	 * 
	 * Using a provider instead of the actual element avoids the creation of
	 * such element in case the ruleApplicationTrace is null, heavily reducing
	 * overhead and increasing performance.
	 * 
	 * See also https
	 * ://github.com/LorenzoBettini/xsemantics/pull/27
	 * 
	 * @param ruleApplicationTrace
	 * @param traceElementProvider
	 * 
	 * @since 1.6
	 */
	public void addToTrace(RuleApplicationTrace ruleApplicationTrace,
			Provider traceElementProvider) {
		if (ruleApplicationTrace != null) {
			ruleApplicationTrace.addToTrace(traceElementProvider.get());
		}
	}

	public RuleApplicationTrace newTrace(
			RuleApplicationTrace ruleApplicationTrace) {
		if (ruleApplicationTrace != null) {
			return new RuleApplicationTrace();
		}
		return null;
	}

	public void addAsSubtrace(RuleApplicationTrace ruleApplicationTrace,
			RuleApplicationTrace subTrace) {
		if (ruleApplicationTrace != null) {
			ruleApplicationTrace.addAsSubtrace(subTrace);
		}
	}

	/**
	 * This method replaces the previous keyword 'env' in Xsemantics grammar
	 * (before version 1.5.0).
	 * 
	 * It is useless to have such element in the grammar, since it has
	 * exactly the same syntax of this Java method invocation.
	 * 
	 * @param environment
	 * @param key
	 * @param clazz
	 * @return
	 * @throws RuleFailedException
	 * @since 1.5
	 */
	public  T env(RuleEnvironment environment, Object key,
			Class clazz) throws RuleFailedException {
		return environmentAccess(environment, key, clazz);
	}

	@SuppressWarnings("unchecked")
	public  T environmentAccess(RuleEnvironment environment, Object key,
			Class clazz) throws RuleFailedException {
		if (environment == null) {
			throw newRuleFailedException("access to null environment");
		}
		Object value = environment.get(key);
		if (value == null) {
			throw newRuleFailedException("no mapping in the environment for: "
					+ stringRep(key));
		}
		if (clazz.isAssignableFrom(value.getClass())) {
			return (T) value;
		}
		throw newRuleFailedException("mapped value " + stringRep(value)
				+ " cannot be assigned to " + stringRep(clazz));
	}

	public  T clone(T eObject) {
		return EcoreUtil.copy(eObject);
	}

	public RuleEnvironment emptyEnvironment() {
		return new RuleEnvironment();
	}

	public RuleEnvironment environmentEntry(Object key, Object value) {
		return new RuleEnvironment(new RuleEnvironmentEntry(key, value));
	}

	public RuleEnvironment environmentComposition(RuleEnvironment environment,
			RuleEnvironment environment2) {
		if (environment == null) {
			return new RuleEnvironment(environment2);
		}
		return new RuleEnvironment(environment, environment2);
	}

	public  List getAll(EObject eObject, EStructuralFeature mainFeature,
			EStructuralFeature extendFeature, Class clazz) {
		List list = new LinkedList();

		if (eObject != null) {
			// get all the stuff from the main object
			addToList(list, eObject.eGet(mainFeature), clazz);

			// follow the extend feature performing the closure
			List nodesInRelation = getAllNodesInRelation(eObject,
					extendFeature);
			for (EObject object : nodesInRelation) {
				addToList(list, object.eGet(mainFeature), clazz);
			}
		}

		return list;
	}

	public List getAllNodesInRelation(EObject eObject,
			EStructuralFeature extendFeature) {
		List nodes = new LinkedList();
		Set seen = new HashSet();

		getAllNodesInRelation(eObject, extendFeature, nodes, seen);

		return nodes;
	}

	protected void getAllNodesInRelation(EObject eObject,
			EStructuralFeature extendFeature, List nodes,
			Set seen) {
		if (eObject != null) {
			// follow the extend feature performing the closure
			List objectsToFollow = getList(eObject.eGet(extendFeature));

			for (Object object : objectsToFollow) {
				EObject toFollow = getEObject(object);
				if (toFollow != null && !seen.contains(toFollow)) {
					seen.add(toFollow);
					nodes.add(toFollow);
					getAllNodesInRelation(toFollow, extendFeature, nodes, seen);
				}
			}
		}
	}

	/**
	 * If the object is a list returns the list itself, otherwise returns a list
	 * with only the passed object as element.
	 * 
	 * @param object
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List getList(Object object) {
		if (object == null) {
			return Lists.newArrayList();
		}

		if (object instanceof List) {
			return (List) object;
		}

		return Lists.newArrayList(object);
	}

	public EObject getEObject(Object object) {
		if (object instanceof EObject) {
			return (EObject) object;
		}
		return null;
	}

	/**
	 * Adds the object (or if the object is a list itself, all its elements,
	 * recursively) to the list, if the object can be assigned to the passed
	 * clazz.
	 * 
	 * @param list
	 * @param o
	 * @param clazz
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public  void addToList(List list, Object o, Class clazz) {
		if (o == null) {
			return;
		}
		if (o instanceof List) {
			List objectList = (List) o;
			for (Object object : objectList) {
				addToList(list, object, clazz);
			}
		} else {
			if (clazz.isAssignableFrom(o.getClass())) {
				list.add((T) o);
			}
		}
	}
}