/*******************************************************************************
* Copyright (c) 2008 SAP
* see https://research.qkal.sap.corp/mediawiki/index.php/CoMONET
*
* Date: $Date: 2010-04-14 09:00:22 +0200 (Mi, 14 Apr 2010) $
* Revision: $Revision: 9646 $
* Author: $Author: c5106462 $
*******************************************************************************/
package com.sap.furcas.runtime.parser.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.regex.Matcher;
import org.eclipse.emf.ecore.EObject;
import com.sap.furcas.runtime.common.exceptions.ModelAdapterException;
import com.sap.furcas.runtime.common.implementation.ResolvedModelElementProxy;
import com.sap.furcas.runtime.common.interfaces.IModelElementProxy;
import com.sap.furcas.runtime.common.util.ContextAndForeachHelper;
import com.sap.furcas.runtime.parser.ANTLR3LocationToken;
import com.sap.furcas.runtime.parser.IModelAdapter;
import com.sap.furcas.runtime.parser.ModelElementCreationException;
import com.sap.furcas.runtime.parser.impl.context.ContextManager;
/**
* separate class for lenghty algorithmic methods involved with setting references after the parsing process.
*
* @author C5107456
*/
public class DelayedReferencesHelper {
/**
* Instantiates a new injector helper.
*/
public DelayedReferencesHelper() {
}
/**
* Sets the delayed reference.
*
* @param reference
* the reference
* @param modelAdapter
* the model handler
* @param contextByElement
* the context by element
* @return <code>true</code> if the reference was resolved successfully, <code>false</code> else.
*
* @throws ModelAdapterException
* the model handler exception
* @throws ModelElementCreationException
*/
public boolean setDelayedReference(DelayedReference reference, IModelAdapter modelAdapter,
ContextManager contextManager, ObservableInjectingParser parser) throws ModelAdapterException,
ModelElementCreationException {
return reference.setDelayedReference(reference, modelAdapter, contextManager, parser);
}
public static Collection<?> evaluateForeachOcl(EObject sourceElement, DelayedReference reference,
IModelAdapter modelAdapter, Object contextElement) throws ModelAdapterException {
String flattenOCL = appendFlattenToOclQuery(reference.getOclQuery());
// evaluate the predicate by OCL, return value is a list of objects
Collection<?> result = modelAdapter.evaluateOCLQuery(sourceElement, reference.getKeyValue(), flattenOCL,
contextElement);
return result;
}
static String appendFlattenToOclQuery(String ocl) {
String flattenOCL = "";
if (ocl.startsWith("OCL:")) {
flattenOCL = "OCL:(" + ocl.substring(4) + ")->asSequence()->flatten()";
} else {
flattenOCL = "OCL:(" + ocl + ")->asSequence()->flatten()";
}
return flattenOCL;
}
static Object getNavigatedContextElementFromReference(DelayedReference reference, IModelAdapter modelAdapter,
ContextManager contextManager, Object contextElement) throws ModelAdapterException,
LookupPathNavigationException {
// check if something like "#context(..)" is contained in the query
Matcher match = ContextAndForeachHelper.contextPattern.matcher(reference.getOclQuery());
if (match.find()) {
String occurence = match.group();
if (match.groupCount() >= 2) {
// check whether all occurences refer to the
// same context element
while (match.find()) {
if (!match.group().equals(occurence)) {
throw new ModelAdapterException(
"Within a query only references to the same context element are supported. Found at least on other usage then the first:"
+ occurence + " != " + match.group(1));
}
}
}
String[] path = new String[] { occurence };
// navigate to an object, to later use that objects
// context as query context
Object navigatedObject = navigateLookIn(contextElement, reference.getModelElement(), path, true,
modelAdapter, reference.getToken(), contextManager);
if (navigatedObject instanceof IModelElementProxy) {
contextElement = contextManager.getContextForElement(navigatedObject);
} else {
contextElement = contextManager.getContextForElement(new ResolvedModelElementProxy(navigatedObject));
}
}
return contextElement;
}
/**
* Returns an element in the context tree according to path notation.
*
* @param object
* @param object
*
* @param path
* the path
* @param includingLastPathElement
* whether to consider the last element of the path
* @param token
* @param contextManager
*
* @return the object
* @throws ModelAdapterException
* @throws LookupPathNavigationException
*/
static Object navigateLookIn(Object contextElement, Object modelElement, String[] path,
boolean includingLastPathElement, IModelAdapter modelAdapter, ANTLR3LocationToken token,
ContextManager contextManager) throws ModelAdapterException, LookupPathNavigationException {
Object returnModelElement = modelElement;
if (path == null || path.length == 0) {
return returnModelElement;
}
int startIndex = 0;
if (path[0].startsWith("#context")) { // do not start path navigation at
// current model element, but
// rather at context
String optionalTag = getTag(path[0].substring("#context".length()), token);
if (optionalTag != null) {
modelElement = contextManager.getTaggedContext(contextElement, optionalTag);
} else {
modelElement = contextElement;
}
if (modelElement == null) {
throw new LookupPathNavigationException("Lookup path context is null for " + path[0], token);
}
startIndex = 1; // jump over this in loop
} else {
if (modelElement == null) {
throw new LookupPathNavigationException("Lookup path starting point is null", token);
}
}
for (int i = startIndex; (i < path.length - (includingLastPathElement ? 0 : 1)); i++) {
String nextPathStep = path[i];
if (nextPathStep.equals("#context")) {
throw new LookupPathNavigationException("'#context' keyword may only be used as root of path: "
+ Arrays.toString(path), token);
} else if (nextPathStep.equals("#all")) {
throw new LookupPathNavigationException("'#all' keyword can't be used with more path elements "
+ Arrays.toString(path), token);
} else {
// need to continue along path
Object v = modelAdapter.get(modelElement, nextPathStep);
if (v != null) { // && modelAdapter.isAModelElement(v)) { // let
// this cause an error later
modelElement = v;
} else { // can't continue on path
throw new LookupPathNavigationException("Model element feature " + modelElement + "."
+ nextPathStep + " is null for path " + Arrays.toString(path), token);
}
}
}
return modelElement;
}
/**
* expects a String like "(helloWorld )" and returns "helloWorld" or an empty String. else throws Exception
*
* @param substring
* @param token
* @return
*/
private static String getTag(String substring, ANTLR3LocationToken token) throws LookupPathNavigationException {
if (substring == null || substring.trim().length() == 0) {
return null;
}
String trimString = substring.trim();
if (trimString.startsWith("(") && trimString.endsWith(")")) {
String result = trimString.substring(1, trimString.length() - 1).trim();
if (result == null || result.length() == 0) {
return null;
}
return result;
} else {
throw new LookupPathNavigationException("Illegasuuffix after '#context'", token);
}
}
}