Incremental Gen. throws nullPointer [message #639400] |
Tue, 16 November 2010 07:32  |
Eclipse User |
|
|
|
Hi,
I am using the following gen. setup (from the documentation) to generate incrementally. I have also created the sample xpand project AS IS and using the Model.xmi.backup and Model.trace as old model and trace files.
<?xml version="1.0"?>
<workflow>
<property name="model" value="my.generator.project/src/Model.xmi" />
<property name="src-gen" value="src-gen" />
<!-- set up EMF for standalone execution -->
<bean class="org.eclipse.emf.mwe.utils.StandaloneSetup" >
<platformUri value=".."/>
</bean>
<!-- instantiate metamodel -->
<bean id="mm_emf" class="org.eclipse.xtend.typesystem.emf.EmfRegistryMetaModel"/>
<!-- load model and store it in slot 'model' -->
<!-- read new model -->
<component id="modelreader" class="org.eclipse.emf.mwe.utils.Reader"
uri="platform:/resource/${model}"
firstElementOnly="true"
modelSlot="model"
/>
<!-- read old model, copied from previous run. may not exist, so ignore missing model -->
<component id="oldmodelreader" class="org.eclipse.emf.mwe.utils.Reader"
uri="src/Model.xmi.backup"
firstElementOnly="true"
ignoreMissingModel="true"
modelSlot="oldmodel"
/>
<!-- compute diff. -->
<component id="compare" class="org.eclipse.xpand2.incremental.compare.EmfCompare"
oldModelSlot="oldmodel"
newModelSlot="model"
diffModelSlot="diff"
/>
<!-- read trace model, produced by previous run. may not exist, so ignore missing model -->
<component id="tracemodelreader" class="org.eclipse.emf.mwe.utils.Reader"
uri="src/Model.trace"
firstElementOnly="true"
ignoreMissingModel="true"
modelSlot="oldtrace"
/>
<!-- this is the actual incremental generation callback -->
<component id="incremental"
class="org.eclipse.xpand2.incremental.IncrementalGenerationCallback"
diffModelSlot="diff"
oldTraceModelSlot="oldtrace"
newTraceModelSlot="trace"
/>
<!-- generate code -->
<component id="generator" class="org.eclipse.xpand2.Generator">
<metaModel idRef="mm_emf"/>
<vetoableCallback idRef="incremental" />
<expand
value="template::Template::main FOR model" />
<outlet path="${src-gen}" >
<postprocessor class="org.eclipse.xpand2.output.JavaBeautifier" />
</outlet>
</component>
<!-- clean obsolete files
<component id="cleaner" class="org.eclipse.xpand2.incremental.FileCleaner">
<oldTraceModelSlot value="oldtrace" />
<newTraceModelSlot value="trace" />
<outlet path="somewhere/" overwrite="true"/>
</component>
-->
<!-- write trace model -->
<component id="tracemodelwriter" class="org.eclipse.emf.mwe.utils.Writer"
modelSlot="trace"
uri="src/Model.trace"
/>
<!-- make backup copy of model -->
<component id="copier" class="org.eclipse.emf.mwe.utils.FileCopy"
sourceFile="src/Model.xmi"
targetFile="src/Model.xmi.backup"
/>
</workflow>
The first generation is ok. But on the second run (which should be the incremental) i get the following exception:
1809 ERROR WorkflowRunner -
java.lang.NullPointerException
at org.eclipse.xpand2.incremental.IncrementalGenerationCallback.hasRelevantChangeForFile(IncrementalGenerationCallback.java:181)
at org.eclipse.xpand2.incremental.IncrementalGenerationCallback.handleFileStatement(IncrementalGenerationCallback.java:143)
at org.eclipse.xpand2.incremental.IncrementalGenerationCallback.pre(IncrementalGenerationCallback.java:123)
at org.eclipse.internal.xpand2.ast.Statement.evaluate(Statement.java:35)
at org.eclipse.internal.xpand2.ast.AbstractDefinition.evaluate(AbstractDefinition.java:180)
at org.eclipse.internal.xpand2.ast.ExpandStatement.invokeDefinition(ExpandStatement.java:246)
at org.eclipse.internal.xpand2.ast.ExpandStatement.evaluateInternal(ExpandStatement.java:191)
at org.eclipse.internal.xpand2.ast.Statement.evaluate(Statement.java:41)
at org.eclipse.internal.xpand2.ast.AbstractDefinition.evaluate(AbstractDefinition.java:180)
at org.eclipse.internal.xpand2.ast.ExpandStatement.invokeDefinition(ExpandStatement.java:246)
at org.eclipse.internal.xpand2.ast.ExpandStatement.evaluateInternal(ExpandStatement.java:226)
at org.eclipse.internal.xpand2.ast.Statement.evaluate(Statement.java:41)
at org.eclipse.xpand2.Generator.invokeInternal2(Generator.java:333)
at org.eclipse.xtend.expression.AbstractExpressionsUsingWorkflowComponent.invokeInternal(AbstractExpressionsUsingWorkflowComponent.java:239)
at org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent.invoke(AbstractWorkflowComponent.java:126)
at org.eclipse.emf.mwe.core.container.CompositeComponent.internalInvoke(CompositeComponent.java:104)
at org.eclipse.emf.mwe.core.container.CompositeComponent.invoke(CompositeComponent.java:89)
at org.eclipse.emf.mwe.core.WorkflowRunner.executeWorkflow(WorkflowRunner.java:408)
at org.eclipse.emf.mwe.core.WorkflowRunner.run(WorkflowRunner.java:293)
at org.eclipse.emf.mwe.core.WorkflowRunner.main(WorkflowRunner.java:241)
1811 ERROR WorkflowRunner - [ERROR](Element: org.eclipse.internal.xpand2.ast.FileStatement@495b0e2c; Reported by: Generator(generator): generating 'template::Template::main FOR model' => src-gen)
1812 ERROR WorkflowRunner - [ERROR](Element: javaClass : Entity; Reported by: Generator(generator): generating 'template::Template::main FOR model' => src-gen)
1812 ERROR WorkflowRunner - [ERROR](Element: EXPAND javaClass FOREACH entities(); Reported by: Generator(generator): generating 'template::Template::main FOR model' => src-gen)
1812 ERROR WorkflowRunner - [ERROR](Element: main : Model; Reported by: Generator(generator): generating 'template::Template::main FOR model' => src-gen)
1812 ERROR WorkflowRunner - [ERROR](Element: EXPAND template::Template::main FOR model; Reported by: Generator(generator): generating 'template::Template::main FOR model' => src-gen)
Any ideas?
I think it should be quite easy to reproduce it.
Thanks
attila
|
|
|
Re: Incremental Gen. throws nullPointer [message #639547 is a reply to message #639400] |
Tue, 16 November 2010 15:42   |
Eclipse User |
|
|
|
Hi,
you stumbled into https://bugs.eclipse.org/bugs/show_bug.cgi?id=326025
You can try if this patched file works for you
/*******************************************************************************
* Copyright (c) 2005, 2009 eXXcellent solution gmbh and others.
* 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:
* Achim Demelt - initial API and implementation
*******************************************************************************/
package org.eclipse.xpand2.incremental;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.compare.diff.metamodel.AttributeChange;
import org.eclipse.emf.compare.diff.metamodel.ModelElementChangeLeftTarget;
import org.eclipse.emf.compare.diff.metamodel.ModelElementChangeRightTarget;
import org.eclipse.emf.compare.diff.metamodel.ReferenceChange;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import org.eclipse.emf.ecore.util.EcoreUtil.EqualityHelper;
import org.eclipse.emf.mwe.core.WorkflowContext;
import org.eclipse.emf.mwe.core.issues.Issues;
import org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent2;
import org.eclipse.emf.mwe.core.monitor.ProgressMonitor;
import org.eclipse.internal.xpand2.ast.FileStatement;
import org.eclipse.internal.xtend.expression.ast.FeatureCall;
import org.eclipse.internal.xtend.expression.ast.Identifier;
import org.eclipse.internal.xtend.expression.ast.SyntaxElement;
import org.eclipse.xpand.incremental.trace.InputElement;
import org.eclipse.xpand.incremental.trace.OutputFile;
import org.eclipse.xpand.incremental.trace.Trace;
import org.eclipse.xpand.incremental.trace.TraceFactory;
import org.eclipse.xtend.expression.ExecutionContext;
import org.eclipse.xtend.expression.VetoableCallback;
public class IncrementalGenerationCallback extends AbstractWorkflowComponent2 implements VetoableCallback {
private String diffModelSlot;
private String oldTraceModelSlot;
private String newTraceModelSlot;
private EObject diffModel;
private Trace oldTraceModel;
private Trace newTraceModel;
private OutputFile currentOutputFile;
// an array of EObject[2] where target object at index 0 and feature is at index 1
private EObject[][] changedElements;
public void setDiffModelSlot(String diffModelSlot) {
this.diffModelSlot = diffModelSlot;
}
public void setOldTraceModelSlot(String oldTraceModelSlot) {
this.oldTraceModelSlot = oldTraceModelSlot;
}
public void setNewTraceModelSlot(String newTraceModelSlot) {
this.newTraceModelSlot = newTraceModelSlot;
}
@Override
protected void checkConfigurationInternal(Issues issues) {
super.checkConfigurationInternal(issues);
if (diffModelSlot == null) {
issues.addError("No diffModelSlot given. Cannot do incremental generation.");
}
if (oldTraceModelSlot == null) {
issues.addError("No oldTraceModelSlot given. Cannot do incremental generation.");
}
if (newTraceModelSlot == null) {
issues.addError("No newTraceModelSlot given. Cannot do incremental generation.");
}
}
@Override
public void invokeInternal(WorkflowContext workflowContext, ProgressMonitor monitor, Issues issues) {
diffModel = (EObject) workflowContext.get(diffModelSlot);
if (diffModel == null) {
issues.addWarning("No diffModel in slot " + diffModelSlot + ". Cannot do incremental generation.");
} else {
prepareChangedElements();
}
oldTraceModel = (Trace) workflowContext.get(oldTraceModelSlot);
if (oldTraceModel == null) {
issues.addWarning("No oldTraceModel in slot " + oldTraceModelSlot + ". Cannot do incremental generation.");
}
newTraceModel = TraceFactory.eINSTANCE.createTrace();
workflowContext.set(newTraceModelSlot, newTraceModel);
}
/**
* Collects the relevant information out of the diff model. This speeds up later processing.
*/
private void prepareChangedElements() {
List<EObject[]> elements = new ArrayList<EObject[]>();
for (Iterator<EObject> i = diffModel.eAllContents(); i.hasNext();) {
EObject diff = i.next();
if (diff instanceof AttributeChange) {
AttributeChange change = (AttributeChange) diff;
elements.add(new EObject[] {change.getLeftElement(), change.getAttribute()});
} else if (diff instanceof ReferenceChange) {
ReferenceChange change = (ReferenceChange) diff;
elements.add(new EObject[] {change.getLeftElement(), change.getReference()});
} else if (diff instanceof ModelElementChangeLeftTarget) {
ModelElementChangeLeftTarget change = (ModelElementChangeLeftTarget) diff;
elements.add(new EObject[] {change.getLeftElement().eContainer(), change.getLeftElement().eContainingFeature()});
} else if (diff instanceof ModelElementChangeRightTarget) {
ModelElementChangeRightTarget change = (ModelElementChangeRightTarget) diff;
elements.add(new EObject[] {change.getLeftParent(), change.getRightElement().eContainingFeature()});
}
}
changedElements = elements.toArray(new EObject[0][0]);
}
public boolean pre(SyntaxElement element, ExecutionContext ctx) {
if (element instanceof FileStatement) {
return handleFileStatement((FileStatement)element, ctx);
}
if (element instanceof FeatureCall) {
return handleFeatureCall((FeatureCall)element, ctx);
}
return true;
}
private boolean handleFileStatement(FileStatement fileStatement, ExecutionContext ctx) {
String fileName = fileStatement.getTargetFileName().evaluate(ctx).toString();
String outletName = fileStatement.getOutletName();
// sanity check:
if (fileName == null) {
return true;
}
// check if we have an existing trace for that file
// and if so, was there a change that requires the file to be generated
OutputFile oldFileTrace = getOldTrace(fileName, outletName);
if (oldFileTrace == null || hasRelevantChangeForFile(oldFileTrace)) {
// no old trace, or we found a relevant change => generate the file and create new trace for that
currentOutputFile = TraceFactory.eINSTANCE.createOutputFile();
currentOutputFile.setFileName(fileName);
currentOutputFile.setOutlet(outletName);
currentOutputFile.setTargetObject((EObject) ctx.getVariable(ExecutionContext.IMPLICIT_VARIABLE).getValue());
newTraceModel.getOutputFiles().add(currentOutputFile);
// yes, we definitely want to generate this file.
return true;
} else {
// it seems we don't have to generate this file
// copy the trace information over into the new trace model
Copier copier = new Copier();
OutputFile copyOfFileTrace = (OutputFile) copier.copy(oldFileTrace);
copier.copyReferences();
newTraceModel.getOutputFiles().add(copyOfFileTrace);
// now ignore upcoming features
currentOutputFile = null;
// and definitely don't generate the file
return false;
}
}
/**
* Checks if the changes contained in the diff imply that the given file
* has to be generated.
*
* @param oldFileTrace The old trace information for the file to check. Must not be null.
* @return true if the file has to be generated, false if not.
*/
private boolean hasRelevantChangeForFile(OutputFile oldFileTrace) {
// fast exit: no diff model ;-)
if (changedElements == null) {
return true;
}
for (EObject[] diff : changedElements) {
for (InputElement input : oldFileTrace.getInputElements()) {
if (new EqualityHelper().equals(diff[0], input.getModelElement()) &&
new EqualityHelper().equals(diff[1], input.getFeature())) {
// abort at first change
return true;
}
}
}
// no change found
return false;
}
/**
* Finds an OutletFile in the old trace model for the given parameter.
* @param fileName The name of the file too look up. Must not be null.
* @param outletName The name of the outlet the file is located in. May be null.
* @return The requested OutletFile or null if none was found.
*/
private OutputFile getOldTrace(String fileName, String outletName) {
// quick exit. no trace model ;-)
if (oldTraceModel == null) {
return null;
}
// now check each file in the trace
for (OutputFile f : oldTraceModel.getOutputFiles()) {
if (f.getFileName().equals(fileName) &&
(outletName == null ? f.getOutlet() == null : outletName.equals(f.getOutlet()))) {
return f;
}
}
return null;
}
private boolean handleFeatureCall(FeatureCall featureCall, ExecutionContext ctx) {
if (currentOutputFile != null) {
Identifier currentFeature = featureCall.getName();
// get target of feature call
Object target = null;
if (featureCall.getTarget() != null) {
target = featureCall.getTarget().evaluate(ctx);
}
if (target == null && ctx.getVariable(ExecutionContext.IMPLICIT_VARIABLE) != null) {
target = ctx.getVariable(ExecutionContext.IMPLICIT_VARIABLE).getValue();
}
String featureName = currentFeature.getNameString(ctx);
if (target instanceof EObject) {
EObject targetObject = (EObject) target;
addTraceForObject(featureName, targetObject);
}
if (target instanceof Collection<?>) {
@SuppressWarnings("unchecked")
Collection<EObject> targetCollection = (Collection<EObject>) target;
for (EObject targetObject : targetCollection) {
addTraceForObject(featureName, targetObject);
}
}
}
return true;
}
private void addTraceForObject(String featureName, EObject targetObject) {
EStructuralFeature feature = targetObject.eClass().getEStructuralFeature(featureName);
if (feature != null) {
addInputElement(targetObject, feature);
}
}
private void addInputElement(EObject target, EStructuralFeature feature) {
// check for duplicates first
for (InputElement e : currentOutputFile.getInputElements()) {
if (e.getModelElement() == target && e.getFeature() == feature) {
return;
}
}
// okay, new element => add that
InputElement inputElement = TraceFactory.eINSTANCE.createInputElement();
inputElement.setModelElement(target);
inputElement.setFeature(feature);
currentOutputFile.getInputElements().add(inputElement);
}
public void post(SyntaxElement element, ExecutionContext ctx, Object expressionResult) {
// don't register any subsequent changes for the file after the FILE statement has been processed
if (element instanceof FileStatement) {
currentOutputFile = null;
}
}
}
~Christian
|
|
|
|
Powered by
FUDForum. Page generated in 0.03656 seconds