Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » M2T (model-to-text transformation) » Incremental Gen. throws nullPointer(Incremental Gen. throws nullPointer)
Incremental Gen. throws nullPointer [message #639400] Tue, 16 November 2010 12:32 Go to next message
Bak Attila is currently offline Bak AttilaFriend
Messages: 29
Registered: December 2009
Junior Member
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 20:42 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14720
Registered: July 2009
Senior Member
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


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Day Job: https://www.everest-systems.com
Re: Incremental Gen. throws nullPointer [message #639643 is a reply to message #639400] Wed, 17 November 2010 10:28 Go to previous message
Bak Attila is currently offline Bak AttilaFriend
Messages: 29
Registered: December 2009
Junior Member
Hi,

Yes, it works.

In case you are interested what i have found out:
The problem is after some debugging:

This works:
changedElements = elements.toArray(new EObject[0][0]);


This not:
changedElements = elements.toArray(new EObject[2][0]);


The initialization is different there. You could of course always use a List there Smile
Thanks and
Regards
attila
Previous Topic:[Xpand] Template could not be found
Next Topic:[Acceleo 3 - ocl] a "loop" question
Goto Forum:
  


Current Time: Fri Oct 04 14:00:35 GMT 2024

Powered by FUDForum. Page generated in 0.03383 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top