/******************************************************************************* * Copyright (c) 2008 SAP * see https://research.qkal.sap.corp/mediawiki/index.php/CoMONET * * Date: $Date: 2009-11-17 07:26:02 +0100 (Di, 17 Nov 2009) $ * Revision: $Revision: 8574 $ * Author: $Author: d043530 $ *******************************************************************************/ package com.sap.furcas.runtime.parser.impl; import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.antlr.runtime.BaseRecognizer; import com.sap.furcas.runtime.common.exceptions.DeferredActionResolvingException; import com.sap.furcas.runtime.common.exceptions.DeferredModelElementCreationException; import com.sap.furcas.runtime.common.exceptions.ModelAdapterException; import com.sap.furcas.runtime.parser.ANTLR3LocationToken; import com.sap.furcas.runtime.parser.IModelInjector; import com.sap.furcas.runtime.parser.ModelElementCreationException; import com.sap.furcas.runtime.parser.ParsingError; import com.sap.furcas.runtime.parser.TextLocation; import com.sap.furcas.runtime.parser.exceptions.ModelCreationOntheFlyRuntimeException; import com.sap.furcas.runtime.parser.impl.context.ContextManager; /** * The Class AbstractModelInjector. */ public class ModelInjector extends AbstractModelInjector implements IModelInjector { // Helper for lengthy methods. Package access to allow for easy stubbing in unit tests /** The helper. */ DelayedReferencesHelper helper = new DelayedReferencesHelper(); /** * Instantiates a new tCS injector. * * @param parserTokens * the token names, such as those returned by * {@link BaseRecognizer#getTokenNames()}. Needed only for error reporting for parse * errors (see * {@link ErrorMessageGenerator#getParsingError(org.antlr.runtime.RecognitionException, String[])} * and {@link #reportError(org.antlr.runtime.RecognitionException)}) and only if the * errors involve keyword recognition. Can be <tt>null</tt>. */ public ModelInjector(String[] parserTokens) { super(parserTokens); } /* (non-Javadoc) * @see com.sap.mi.textual.grammar.IModelInjector#createModelElementProxy(java.lang.String, boolean, boolean) */ @Override public Object createOrResolve(ModelElementProxy proxy, ANTLR3LocationToken firstToken, ANTLR3LocationToken lastToken) throws ModelElementCreationException { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } try { Map<String, List<Object>> attributes = proxy.getAttributeMap(); resolveAttributeValuesRecursively(proxy, firstToken, lastToken); Object createdObject = getModelAdapter().createOrResolveElement(proxy.getType(), attributes, firstToken, lastToken, proxy.isReferenceOnly(), false); proxy.setRealObject(createdObject); return createdObject; } catch (ModelAdapterException e) { // TODO move this to getModelAdapter() and delete ModelElement on errors (?) throw new ModelElementCreationException("Exception resolving proxy " + proxy + ": " + e.getMessage(), e); } } /** * Checks if all attribute values for the proxy are either no proxies or have been resolved already. * If an attribute value is encountered that is an unresolved proxy, resolution is triggered * using {@link #createOrResolve(Object, ANTLR3LocationToken, ANTLR3LocationToken)}, recursively. */ private void resolveAttributeValuesRecursively(ModelElementProxy proxy, ANTLR3LocationToken firstToken, ANTLR3LocationToken lastToken) throws ModelElementCreationException { Set<String> features = proxy.getAttributeMap().keySet(); for (String prop : features) { List<Object> valueList = proxy.getAttributeMap().get(prop); for (Iterator<Object> iterator = valueList.iterator(); iterator.hasNext();) { // TODO: for single value properties, cause error when trying to set more than once? Object value = iterator.next(); if (value instanceof ModelElementProxy) { ModelElementProxy valueProxy = (ModelElementProxy) value; value = valueProxy.getRealObject(); if (value == null) { valueProxy.setRealObject(createOrResolve(valueProxy, firstToken, lastToken)); } } } } } /** * @param object * @param string * @param string * @return * @throws ModelElementCreationException */ Object doCreate(List<String> list, String keyname, Object keyValue) throws ModelElementCreationException { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } Object element; try { Map<String, List<Object>> valueMap = new HashMap<String, List<Object>>(); List<Object> values = new ArrayList<Object>(); values.add(keyValue); valueMap.put(keyname, values); element = getModelAdapter().createOrResolveElement(list, valueMap, null, null, false, false ); } catch (ModelAdapterException e) { throw new ModelElementCreationException("Exception while creating type " + list + ": " + e.getMessage(), e); } if (element == null) { throw new RuntimeException("Invalid ModelAdapter implementation " + getModelAdapter().getClass() + ": Model Adapter returned null instead of created element of type " + list); } return element; } /* (non-Javadoc) * @see com.sap.mi.textual.grammar.IModelInjector#createEnumLiteral(java.util.List, java.lang.String) */ @Override public Object createEnumLiteral(List<String> enumName, String name) { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } try { return getModelAdapter().createEnumLiteral(enumName, name); } catch (ModelAdapterException e) { throw new ModelCreationOntheFlyRuntimeException("Exception while creating enum literal " + name + " : " + e.getMessage(), e); } } /* (non-Javadoc) * @see com.sap.mi.textual.grammar.impl.IModelInjector#set(java.lang.Object, java.lang.String, java.lang.Object) */ @Override public void set(Object modelElement, String prop, Object value) { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } if (modelElement instanceof ModelElementProxy) { ModelElementProxy proxy = (ModelElementProxy) modelElement; // TODO: maybe check here if feature exists for type? proxy.addProxyAttribute(prop, value); } else { // may happen for delayed references try { getModelAdapter().set(modelElement, prop, value); } catch (ModelAdapterException e) { throw new ModelCreationOntheFlyRuntimeException("Exception setting feature " + prop + " for object " + modelElement + " to " + value +":" + e.getMessage(), e); } } } /* (non-Javadoc) * @see com.sap.mi.textual.grammar.impl.IModelInjector#set(java.lang.Object, java.lang.String, java.lang.Object, int) */ @Override public void set(Object modelElement, String prop, Object value, int index) { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } if (modelElement instanceof ModelElementProxy) { ModelElementProxy proxy = (ModelElementProxy) modelElement; // TODO: maybe check here if feature exists for type? proxy.addProxyAttribute(prop, value, index); } else { // may happen for delayed references try { getModelAdapter().set(modelElement, prop, value, index); } catch (ModelAdapterException e) { throw new ModelCreationOntheFlyRuntimeException("Exception setting feature " + prop + " for object " + modelElement + " to " + value +":" + e.getMessage(), e); } } } /* (non-Javadoc) * @see com.sap.mi.textual.grammar.impl.IModelInjector#setCommentsAfter(java.lang.Object, java.lang.Object[]) */ @Override public void setCommentsAfter(Object ret, org.antlr.runtime.TokenStream stream, org.antlr.runtime.Token nextToken) { // System.out.println("setCommentsAfter" + ret); //TODO find out what we could use this for (conservation of comments in roundtrip?) } /* (non-Javadoc) * @see com.sap.mi.textual.grammar.impl.IModelInjector#setCommentsBefore(java.lang.Object, java.lang.Object[]) */ @Override public void setCommentsBefore(Object ret, org.antlr.runtime.TokenStream stream, org.antlr.runtime.Token firstToken) { // System.out.println("setCommentsBefore" + ret); //TODO find out what we could use this for (conservation of comments in roundtrip?) } /* (non-Javadoc) * @see com.sap.mi.textual.grammar.impl.IModelInjector#setDelayedReferences() */ @Override public boolean resolveReference(DelayedReference reference,ContextManager contextManager, ObservableInjectingParser parser) throws ModelElementCreationException { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } try { return helper.setDelayedReference(reference, getModelAdapter(), contextManager, parser); } catch (ModelAdapterException e) { addError(new ParsingError(e.getMessage(), reference.getToken())); return false; } } /** * */ @Override public void performAdapterDeferredActions() { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } if (getErrorList().size() == 0) { // the getModelAdapter() gets the right to replace mocked objects with real objects. if (getModelAdapter().hasDeferredActions()) { try { Map<Object, Object> replacementMap = getModelAdapter().performAllDeferredActions(); if (replacementMap != null) { // replace mock objects with real objects in textLocation map Map<Object, TextLocation> locationByElement = getLocationsMap(); for (Iterator<Object> iterator = replacementMap.keySet().iterator(); iterator.hasNext();) { Object mock = iterator.next(); TextLocation location = locationByElement.get(mock); locationByElement.remove(mock); locationByElement.put(replacementMap.get(mock), location); } } } catch (DeferredActionResolvingException e) { List<DeferredModelElementCreationException> causes = e.getCauses(); for (Iterator<DeferredModelElementCreationException> iterator = causes.iterator(); iterator.hasNext();) { DeferredModelElementCreationException modelAdapterException = iterator .next(); addError(new ParsingError(modelAdapterException.getMessage(), getLocationsMap().get(modelAdapterException.getErrorCausingMockObject()))); } } } } } /* (non-Javadoc) * @see com.sap.mi.textual.grammar.impl.IModelInjector#unescapeString(java.lang.String, int) */ @Override public String unescapeString(String s, int delimLength) { StringBuilder ret = new StringBuilder(); // get rid of the starting and ending delimiters (e.g., '\'', '"') s = s.substring(delimLength, s.length()-(delimLength * 2 - 1)); CharacterIterator ci = new StringCharacterIterator(s); char c = ci.first(); while(c != CharacterIterator.DONE) { char tc = 0; switch(c) { case '\\': c = ci.next(); switch(c) { case 'n': tc = '\n'; break; case 'r': tc = '\r'; break; case 't': tc = '\t'; break; case 'b': tc = '\b'; break; case 'f': tc = '\f'; break; case '"': tc = '"'; break; case '\'': tc = '\''; break; case '\\': tc = '\\'; break; case '0': case '1': case '2': case '3': throw new RuntimeException("octal escape sequences not supported yet"); default: throw new RuntimeException("unknown escape sequence: '\\" + c + "'"); } break; default: tc = c; break; } ret.append(tc); c = ci.next(); } return ret.toString(); } /* * (non-Javadoc) * * @see com.sap.mi.textual.grammar.impl.IModelInjector#setLocation(java.lang.Object, * java.lang.String) */ @Override public final void setLocation(Object modelElement, TextLocation location) { super.setLocation(modelElement, location); if (modelElement != null) { if (this.options != null && this.options.isStoreLocationsInModel()) { try { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } getModelAdapter().set(modelElement, options .getLocationsPropertyNameInModel(), location.toString()); } catch (ModelAdapterException e) { } } } } @Override public void unset(Object modelElement, String prop, Object value) { if (getModelAdapter() == null) { throw new IllegalStateException("Cannot use ModelInjector when ModelAdapter is set to null"); } if (modelElement instanceof ModelElementProxy) { ModelElementProxy proxy = (ModelElementProxy) modelElement; // TODO: maybe check here if feature exists for type? proxy.unsetProxyAttribute(prop, value); } else { // may happen for delayed references try { getModelAdapter().unset(modelElement, prop, value); } catch (ModelAdapterException e) { throw new ModelCreationOntheFlyRuntimeException("Exception unsetting feature " + prop + " for object " + modelElement + " to null :" + e.getMessage(), e); } } } }