/** * Finds the next {@link ForEachExecution} in the collection iterated by <code> * foreachContextIterator</code> that is for the same {@link DelayedReference#getQueryElement() * property init} as the <code>reference</code> and that is for the same {@link * ForEachExecution#getSourceModelElement() source element} as the <code>reference</code>. If no * such element is found, <code>null</code> is returned * * <p>Postcondition: if a non-<code>null</code> result is returned, the <code> * foreachContextIterator</code> is at that element so that calling {@link Iterator#remove()} * removes the element just returned * * @param foreachContextIterator must not be <code>null</code> */ private ForEachExecution getNextForeachContext( Iterator<ForEachExecution> foreachContextIterator, DelayedReference reference) { ForEachExecution result = null; while (foreachContextIterator.hasNext() && result == null) { ForEachExecution fec = foreachContextIterator.next(); if (fec.getForeachPedicatePropertyInit().equals(reference.getQueryElement()) && reference.getModelElement().equals(fec.getSourceModelElement())) { result = fec; } } return result; }
private ForEachExecution produceNewForEachContext( DelayedReference reference, ForeachProductionResult producedResult) { ForEachExecution newContext = TextblocksFactory.eINSTANCE.createForEachExecution(); newContext.setForeachPedicatePropertyInit( (ForeachPredicatePropertyInit) reference.getQueryElement()); newContext.setSourceModelElement((EObject) reference.getModelElement()); newContext.setContextElement( (EObject) producedResult.getForeachExpressionResultForWhichProduced()); newContext.setTemplateUsedForProduction(producedResult.getTemplateUsedForProduction()); newContext.setResultModelElement((EObject) reference.getRealValue()); return newContext; }
private void setReference( DelayedReference reference, Object singleForeachResult, IModelAdapter modelAdapter, ModelElementProxy foreachTargetElement, IModelInjector injector, int position) throws ModelElementCreationException, ModelAdapterException { // add the parsed part to the object first try to resolve if there is a model element that // already // exists and can be reused reference.setRealValue(injector.createOrResolve(foreachTargetElement, null, null)); // by default use partition of reference.getModelElement if (reference.getModelElement() instanceof EObject && reference.getRealValue() instanceof EObject) { ((EObject) reference.getModelElement()) .eResource() .getContents() .add((EObject) reference.getRealValue()); } modelAdapter.set( reference.getModelElement(), reference.getPropertyName(), reference.getRealValue(), position); }
/** * If the <tt>reference</tt>'s {@link DelayedReference#getModelElement() model element} is a * proxy, resolve it first. */ private void resolveModelElementProxy(DelayedReference reference, IModelAdapter modelAdapter) throws ModelAdapterException, ModelElementCreationException { if (reference.getModelElement() instanceof ModelElementProxy) { ModelElementProxy proxy = (ModelElementProxy) reference.getModelElement(); if (proxy.getRealObject() == null) { Object result; result = modelAdapter.createOrResolveElement( proxy.getType(), proxy.getAttributeMap(), null, null, false, true); if (result instanceof EObject) { reference.setModelElement(result); } } else { reference.setModelElement(proxy.getRealObject()); } } }
/** * Tries to find a {@link DelayedReference#getQueryElement() foreach property init} on the delayed * reference <code>ref</code>. Assuming that <code>ref</code> is a {@link * DelayedReference.ReferenceType#TYPE_FOREACH_PREDICATE} reference, its {@link * DelayedReference#getPredicateActionList() predicate list} (the list of when/as/mode clauses} is * used to find the position/index of <code>activePredicateSemantic</code>. At this position, the * {@link com.sap.furcas.metamodel.FURCAS.TCS.PredicateSemantic#getAs() template} to be used for * production is looked up. * * <p>If either the reference doesn't have the property init set in its {@link * DelayedReference#getQueryElement()}, <code>null</code> is returned. This will in particular be * the case if the parser is not configured to produce text blocks. */ private Template getTemplateFromPredicateSemantic( PredicateSemantic activePredicateSemantic, DelayedReference ref) { int index = ref.getPredicateActionList().indexOf(activePredicateSemantic); if (index >= 0 && ((ForeachPredicatePropertyInit) ref.getQueryElement()) != null) { int i = 0; for (com.sap.furcas.metamodel.FURCAS.TCS.PredicateSemantic predSem : ((ForeachPredicatePropertyInit) ref.getQueryElement()).getPredicateSemantic()) { if (i++ == index) { return predSem.getAs(); } } return null; } else { return null; } }
/** * 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 */ @Override public boolean setDelayedReference( DelayedReference reference, IModelAdapter modelAdapter, ContextManager contextManager, ObservableInjectingParser parser) throws ModelAdapterException, ModelElementCreationException { Object contextElement = reference.getContextElement(); if (contextElement instanceof IModelElementProxy) { IModelElementProxy proxyContext = (IModelElementProxy) contextElement; contextElement = proxyContext.getRealObject(); } return setDelayedReferenceWithPredicate( reference, modelAdapter, contextManager, contextElement, parser); }
private PredicateSemantic getActivePredicateFromWhenAsClauses( DelayedReference reference, IModelAdapter modelAdapter, Object contextElement, Object currentForeachElement) throws ModelAdapterException { for (PredicateSemantic nextPred : reference.getPredicateActionList()) { if (nextPred.getWhen() != null) { Collection<?> resultBool = modelAdapter.evaluateOCLQuery( currentForeachElement, reference.getKeyValue(), nextPred.getWhen(), contextElement); if (resultBool.size() == 1) { Iterator<?> resIt = resultBool.iterator(); Object nextBool = resIt.next(); if (nextBool instanceof Boolean && (Boolean) nextBool) { return nextPred; } } } else { return nextPred; // no when-clause means "handle always" } } return null; }
private ModelElementProxy produceSingleForeachUsingParserRule( ObservableInjectingParser parser, DelayedReference reference, Object next, String ruleName) throws NoSuchMethodException, UnknownProductionRuleException, IllegalAccessException, InvocationTargetException, ModelElementCreationException { // invoke the parser to execute the template Method methodToCall = parser.getClass().getMethod(ruleName); // parser.reset(); if (!Modifier.isFinal(methodToCall.getModifiers())) { throw new UnknownProductionRuleException( ruleName + " is not a production rule in generated Parser."); } boolean originalResolveProxiesValue = parser.isResolveProxies(); parser.setResolveProxies(false); DelegationParsingObserver delegator = new DelegationParsingObserver(); IParsingObserver originalObserver = parser.getObserver(); if (originalObserver != null) { delegator.addParsingObserver(originalObserver); } delegator.addParsingObserver(new ForeachParsingObserver((TextBlock) reference.getTextBlock())); parser.setObserver(delegator); IModelElementProxy proxyForContextElement = null; if (reference.getContextElement() instanceof IModelElementProxy) { proxyForContextElement = (IModelElementProxy) reference.getContextElement(); } else { proxyForContextElement = new ResolvedModelElementProxy(reference.getContextElement()); } parser.setCurrentForeachElement(next); if (parser.getContextManager().getContextForElement(reference.getContextElement()) == null) { parser.addContext(proxyForContextElement); if (proxyForContextElement.getRealObject() != null && reference.getContextElement() instanceof EObject) { parser .getContextManager() .notifyProxyResolvedWith( proxyForContextElement, reference.getContextElement(), /* * no creation context element needs to be provided here because the proxy has just been created and has not been * added to any other context */ null); } } else { parser .getCurrentContextStack() .push(proxyForContextElement); // the Context object was already created elsewhere } try { ModelElementProxy parseReturn = (ModelElementProxy) methodToCall.invoke(parser); if (parseReturn == null) { throw new ModelElementCreationException( "Unable to create model element using parse rule " + ruleName + ". Parse errors: " + parser.getInjector().getErrorList()); } return parseReturn; } finally { parser.getCurrentContextStack().pop(); parser.setObserver(originalObserver); parser.setResolveProxies(originalResolveProxiesValue); } }
/** * A <code>foreach</code> expression was (re-)evaluated, and for each result, based on any <code> * when</code>/ <code>as</code> {@link com.sap.furcas.metamodel.FURCAS.TCS.PredicateSemantic} * elements, the corresponding template's production rule was executed by the parser. This * produced a {@link ModelElementProxy} which has not yet been turned into a real {@link EObject} * yet nor has it been entered into the target feature. * * <p>Prior executions of the same <code>foreach</code> predicate on the same source model element * may have happened. In this case, there will be {@link ForEachExecution} records documenting * this. If no such records are found, all proxies produced will be materialized as {@link * EObject}s and {@link #setReference(DelayedReference, Object, IModelAdapter, ModelElementProxy, * IModelInjector, int) added/set} to/into the target feature described by the <code>reference * </code>. * * <p>If {@link ForEachExecution} objects are found for the source element and <code>foreach * </code> predicate, their {@link ForEachExecution#getResultModelElement() result elements} are * obtained and compared to the new {@link ForeachProductionResult production instructions} which * also contain the {@link Template} used to produce the target element. The template is used to * compare the element types in order to try to re-use already existing elements. * * <p>In case a target object from a prior evaluation can be re-used, it is set as the proxy's * {@link ModelElementProxy#setRealObject(Object) real object}, and no new target object is * created for this <code>foreach</code> result. Otherwise, a new target object is created. The * re-used or created object is then used to replace (single multiplicity) or to add to * (many-multiplicity) the target feature. * * <p>As to the re-use strategy, we distinguish between re-using the {@link ForEachExecution} hull * only and the {@link ForEachExecution#getResultModelElement() result element} that was produced * in an earlier run. Two element collections are compared, namely those listed as {@link * ForEachExecution#getResultModelElement() result elements} of earlier evaluations on the <code> * reference</code>'s {@link DelayedReference#getTextBlock() text block} for the same <code> * foreach</code> predicate; and those described by <code>producedResults</code> which contains * proxies and the templates telling the types and modes with which they were created. The two * collections are iterated in parallel. If the {@link ForEachExecution}'s template matches that * in the current <code>producedResults</code> element, the {@link * ForEachExecution#getResultModelElement() old result element} is re-used by {@link * ModelElementProxy#setRealObject(Object) setting} it on the proxy. Otherwise, a new element is * produced using {@link ModelInjector#createOrResolve(ModelElementProxy, ANTLR3LocationToken, * ANTLR3LocationToken)} and the existing {@link ForEachExecution} is updated for the new result * element, template, etc. If the collection of {@link ForEachExecution} elements is shorter than * that of the <code>producedResults</code>, additional {@link ForEachExecution} elements are * added to the <code>reference</code>'s {@link TextBlock#getForEachExecutions() textblock's * foreach context list}. Extraneous elements are deleted from it. * * <p>It's obvious that there may be more sophisticated re-use strategies based on longest * sequence etc., but those may be added later. * * @param injector TODO * @param producedResults the {@link ForeachProductionResult#getTemplateUsedForProduction() * templates} need to be set only if text blocks are being produced by the parser run */ private void setReferenceAndUpdateForeachContexts( DelayedReference reference, IModelAdapter modelAdapter, IModelInjector injector, List<ForeachProductionResult> producedResults) throws ModelElementCreationException, ModelAdapterException { Iterator<ForEachExecution> foreachContextIterator = null; ForEachExecution nextOldForeachContext = null; if (reference.getTextBlock() != null) { foreachContextIterator = ((TextBlock) reference.getTextBlock()).getForEachExecutions().iterator(); nextOldForeachContext = getNextForeachContext(foreachContextIterator, reference); } int i = 0; for (ForeachProductionResult producedResult : producedResults) { // check for a re-usable target element of ForEachContext with its result element if (nextOldForeachContext != null && isTargetElementReusable(nextOldForeachContext, producedResult)) { // re-use the target object referenced by the ForEachContext and the ForEachContext itself producedResult .getProducedProxy() .setRealObject(nextOldForeachContext.getResultModelElement()); if (producedResult.getForeachExpressionResultForWhichProduced() instanceof EObject) { nextOldForeachContext.setContextElement( (EObject) producedResult.getForeachExpressionResultForWhichProduced()); } else if (producedResult.getForeachExpressionResultForWhichProduced() instanceof EObject) { nextOldForeachContext.setContextString( (String) producedResult.getForeachExpressionResultForWhichProduced()); } reference.setRealValue(nextOldForeachContext.getResultModelElement()); } else { // target element is not re-usable, a new one needs to be produced setReference( reference, producedResult.getForeachExpressionResultForWhichProduced(), modelAdapter, producedResult.getProducedProxy(), injector, i); if (reference.getTextBlock() != null && producedResult.getTemplateUsedForProduction() != null) { // if we have a ForEachContext element for the current source element and foreach // predicate, // re-use and update it: if (nextOldForeachContext == null) { // no ForEachContext element; produce a new one and append ForEachExecution newContext = produceNewForEachContext(reference, producedResult); ((TextBlock) reference.getTextBlock()).getForEachExecutions().add(newContext); } else { // ForEachContext names a template/element that can't be re-used; update it // correspondingly Object result = producedResult.getForeachExpressionResultForWhichProduced(); if (result instanceof EObject) { nextOldForeachContext.setContextElement( (EObject) producedResult.getForeachExpressionResultForWhichProduced()); } else if (result instanceof String) { nextOldForeachContext.setContextString( (String) producedResult.getForeachExpressionResultForWhichProduced()); } nextOldForeachContext.setTemplateUsedForProduction( producedResult.getTemplateUsedForProduction()); nextOldForeachContext.setResultModelElement( (EObject) producedResult.getProducedProxy().getRealObject()); } } } if (nextOldForeachContext != null && foreachContextIterator != null && foreachContextIterator.hasNext()) { nextOldForeachContext = foreachContextIterator.next(); } else { nextOldForeachContext = null; } i++; } // delete remaining old ForEachContext entries from reference's textblock while (nextOldForeachContext != null) { foreachContextIterator.remove(); nextOldForeachContext = getNextForeachContext(foreachContextIterator, reference); } }
private boolean setDelayedReferenceWithPredicate( DelayedReference reference, IModelAdapter modelAdapter, ContextManager contextManager, Object contextElement, ObservableInjectingParser parser) throws ModelAdapterException { try { contextElement = DelayedReferencesHelper.getNavigatedContextElementFromReference( reference, modelAdapter, contextManager, contextElement); // when the element is a Proxy resolve it first resolveModelElementProxy(reference, modelAdapter); if (reference.getOclQuery() == null) { parser .getInjector() .addError(new ParsingError("You must specify an OCL query.", reference.getToken())); return false; } Collection<?> result = DelayedReferencesHelper.evaluateForeachOcl( (EObject) reference.getModelElement(), reference, modelAdapter, contextElement); List<ForeachProductionResult> producedResults = new ArrayList<ForeachProductionResult>(); // loop over the results to handle them one by one, // delete all elements that were created by this foreach but are // not valid anymore String mode = reference.getMode(); IRuleName ruleNameFinder = reference.getRuleNameFinder(); for (Object singleForeachResult : result) { if (!(singleForeachResult instanceof Boolean) || ((Boolean) singleForeachResult).booleanValue()) { // look if there are possible when/as constructs PredicateSemantic activePredicateSemantic = getActivePredicateFromWhenAsClauses( reference, modelAdapter, contextElement, singleForeachResult); Template templateUsedForProduction = getTemplateFromPredicateSemantic(activePredicateSemantic, reference); // TODO it would be nice to compute the rule name from the template; however, if no // textblocks are being produced, the reference doesn't have the link to the // ForeachPropertyInit set, so the template can't be determined and isn't passed in by the // parser run; only the rule name is passed String parserRuleNameToUseForProduction = computeRuleName( parser, singleForeachResult, activePredicateSemantic, mode, ruleNameFinder); if (parserRuleNameToUseForProduction == null) { throw new UnknownProductionRuleException( "At least one as parameter is needed in that case."); } ModelElementProxy foreachTargetElement = produceForOneForeachResult( reference, modelAdapter, contextElement, parser, singleForeachResult, activePredicateSemantic, parserRuleNameToUseForProduction); ForeachProductionResult resultObject = new ForeachProductionResult( foreachTargetElement, templateUsedForProduction, singleForeachResult); producedResults.add(resultObject); } } setReferenceAndUpdateForeachContexts( reference, modelAdapter, parser.getInjector(), producedResults); } catch (Exception e) { parser.getInjector().addError(new ParsingError(e.getMessage(), reference.getToken())); return false; } return true; }