Merge "Bug 566955 - [Robotics, ROS2] Code generator blocks UI, can't be stopped by user"
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/META-INF/MANIFEST.MF b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/META-INF/MANIFEST.MF
index b2f1b61..812a157 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/META-INF/MANIFEST.MF
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/META-INF/MANIFEST.MF
@@ -17,7 +17,7 @@
  org.eclipse.uml2.uml.profile.standard;bundle-version="1.0.0",
  org.eclipse.papyrus.marte.static.profile;bundle-version="1.2.0",
  org.eclipse.papyrus.designer.transformation.profile;bundle-version="0.7.0",
- org.eclipse.papyrus.designer.transformation.base;bundle-version="0.7.0",
+ org.eclipse.papyrus.designer.transformation.base;bundle-version="0.8.1",
  org.eclipse.papyrus.designer.deployment.tools;bundle-version="0.7.0",
  org.eclipse.papyrus.designer.languages.cpp.codegen;bundle-version="1.1.0",
  org.eclipse.papyrus.designer.transformation.extensions;bundle-version="0.7.0",
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/RosTransformations.xtend b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/RosTransformations.xtend
index b137029..24e6c91 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/RosTransformations.xtend
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/RosTransformations.xtend
@@ -15,15 +15,20 @@
 package org.eclipse.papyrus.robotics.ros2.codegen
 
 import java.util.ArrayList
+import org.eclipse.core.runtime.NullProgressMonitor
 import org.eclipse.papyrus.designer.languages.common.base.file.FileSystemAccessFactory
+import org.eclipse.papyrus.designer.languages.common.base.file.ICleanUntouchedTmp
 import org.eclipse.papyrus.designer.transformation.base.utils.TransformationException
 import org.eclipse.papyrus.designer.transformation.core.m2minterfaces.IM2MTrafoCDP
+import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain
 import org.eclipse.papyrus.designer.transformation.core.transformations.TransformationContext
 import org.eclipse.papyrus.designer.transformation.profile.Transformation.M2MTrafo
 import org.eclipse.papyrus.robotics.profile.robotics.components.ComponentDefinitionModel
 import org.eclipse.papyrus.robotics.profile.robotics.components.SystemComponentArchitectureModel
+import org.eclipse.papyrus.robotics.profile.robotics.services.ServiceDefinitionModel
 import org.eclipse.papyrus.robotics.ros2.codegen.build.CreateCompCMakeLists
 import org.eclipse.papyrus.robotics.ros2.codegen.build.CreateCompPackageXML
+import org.eclipse.papyrus.robotics.ros2.codegen.component.ComponentTransformations
 import org.eclipse.papyrus.robotics.ros2.codegen.message.CreateMsgPackage
 import org.eclipse.papyrus.robotics.ros2.codegen.utils.ApplyProfiles
 import org.eclipse.papyrus.robotics.ros2.codegen.utils.MessageUtils
@@ -33,20 +38,11 @@
 import org.eclipse.uml2.uml.Package
 
 import static extension org.eclipse.papyrus.robotics.core.utils.InstanceUtils.*
-import static extension org.eclipse.papyrus.robotics.ros2.codegen.component.ComponentTransformations.*
 import static extension org.eclipse.papyrus.robotics.ros2.codegen.launch.LaunchScript.generateLaunch
 import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.PackageTools.*
-import org.eclipse.papyrus.robotics.profile.robotics.services.ServiceDefinitionModel
-import org.eclipse.core.runtime.NullProgressMonitor
-import org.eclipse.papyrus.infra.tools.file.ProjectBasedFileAccess
-import org.eclipse.papyrus.designer.languages.common.base.file.ProjectBasedFileAccessTmp
-import org.eclipse.papyrus.designer.languages.common.base.file.ICleanUntouchedTmp
-import org.eclipse.papyrus.robotics.ros2.codegen.component.ComponentTransformations
 
 class RosTransformations implements IM2MTrafoCDP {
 
-	public static val USER_CANCEL = "abort transformation, canceled by user"
-
 	CreateMsgPackage msgPkgCreator
 
 	/**
@@ -80,7 +76,7 @@
 
 			TransformationContext.current.project = project;
 		} else {
-			throw new TransformationException(RosTransformations.USER_CANCEL);
+			throw new TransformationException(ExecuteTransformationChain.USER_CANCEL);
 		}
 	}
 
@@ -129,5 +125,6 @@
 			Activator.log.debug(
 				String.format("model %s is neither a component definition or a component assembly", rootPkg.name));
 		}
+ 		// CCorePlugin.getIndexManager().setDefaultIndexerId(currentIndexer);
 	}
 }
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/component/ComponentTransformations.xtend b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/component/ComponentTransformations.xtend
index 8084aa4..5238ad4 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/component/ComponentTransformations.xtend
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/component/ComponentTransformations.xtend
@@ -48,6 +48,7 @@
 import org.eclipse.emf.ecore.util.EcoreUtil
 import org.eclipse.papyrus.robotics.ros2.codegen.message.CreateMsgPackage
 import org.eclipse.papyrus.infra.tools.file.IPFileSystemAccess
+import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain
 
 class ComponentTransformations {
 
@@ -229,7 +230,7 @@
 		msgPkgCreator.createMessagesOrServices(component)
 	
 		if (genProject === null) {
-			throw new TransformationException(RosTransformations.USER_CANCEL);
+			throw new TransformationException(ExecuteTransformationChain.USER_CANCEL);
 		}
 		component.liftFunctions
 		component.createConstructor
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/component/RoboticsCppCreator.java b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/component/RoboticsCppCreator.java
index 10a50ff..41aab0f 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/component/RoboticsCppCreator.java
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/component/RoboticsCppCreator.java
@@ -14,11 +14,15 @@
 
 package org.eclipse.papyrus.robotics.ros2.codegen.component;
 
-import org.eclipse.core.resources.IFile;
-import org.eclipse.papyrus.designer.languages.common.base.file.ProjectBasedFileAccessTmp;
+import org.eclipse.papyrus.designer.languages.common.base.file.IFileExists;
 import org.eclipse.papyrus.designer.languages.cpp.codegen.transformation.CppModelElementsCreator;
+import org.eclipse.papyrus.designer.transformation.base.utils.TransformationException;
+import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain;
+import org.eclipse.papyrus.designer.transformation.core.transformations.TransformationContext;
 import org.eclipse.papyrus.infra.tools.file.IPFileSystemAccess;
+import org.eclipse.papyrus.robotics.ros2.codegen.utils.ProjectTools;
 import org.eclipse.uml2.uml.NamedElement;
+import org.eclipse.xtext.xbase.lib.Exceptions;
 
 /**
  * A variant of the CppModelElementsCreator that enables putting putting
@@ -51,19 +55,31 @@
 	/**
 	 * If the file is a skeleton file and the user source code file (in src) does not exist yet, generate this
 	 * file as well.
+	 * 
 	 * @see org.eclipse.papyrus.designer.languages.cpp.codegen.transformation.CppModelElementsCreator#generateFile(java.lang.String, java.lang.String)
 	 *
-	 * @param fileName the file to generate
+	 * @param fileName
+	 *            the file to generate
 	 * @param content
 	 */
 	@Override
 	protected void generateFile(String fileName, String content) {
+		// don't generate, if indexer is active (non-idle)
+		if (TransformationContext.monitor.isCanceled()) {
+			// use xtend trick to sneak a non
+			throw Exceptions.sneakyThrow(new TransformationException(ExecuteTransformationChain.USER_CANCEL));
+		}
+		ProjectTools.waitForCDT();
+		TransformationContext.monitor.subTask("generate file " + fileName); //$NON-NLS-1$
+		TransformationContext.monitor.worked(1);
+
 		if (fileName.startsWith(skeletonFolder)) {
-			String srcFileName = fileName.replaceFirst(skeletonFolder,  userSrcFolder);
-			// TODO: potentially unsafe type cast
-			IFile userFile = ((ProjectBasedFileAccessTmp) fileSystemAccess).getFile(srcFileName);
-			if (!userFile.exists()) {
-				super.generateFile(srcFileName, content);
+			String srcFileName = fileName.replaceFirst(skeletonFolder, userSrcFolder);
+			if (fileSystemAccess instanceof IFileExists) {
+				IFileExists existsFSA = (IFileExists) fileSystemAccess;
+				if (!existsFSA.existsFile(srcFileName)) {
+					super.generateFile(srcFileName, content);
+				}
 			}
 		}
 		super.generateFile(fileName, content);
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/handlers/GenerateCodeHandler.java b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/handlers/GenerateCodeHandler.java
index 342ea00..c40a184 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/handlers/GenerateCodeHandler.java
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/handlers/GenerateCodeHandler.java
@@ -23,13 +23,15 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.papyrus.designer.transformation.base.utils.ProjectManagement;
 import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain;
+import org.eclipse.papyrus.designer.transformation.core.transformations.TransformationContext;
 import org.eclipse.papyrus.designer.transformation.profile.Transformation.ExecuteTrafoChain;
 import org.eclipse.papyrus.robotics.ros2.codegen.commands.PrepareCodegenCmd;
 import org.eclipse.papyrus.uml.diagram.common.handlers.CmdHandler;
 import org.eclipse.papyrus.uml.tools.utils.PackageUtil;
-import org.eclipse.ui.progress.UIJob;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.uml2.uml.Classifier;
 import org.eclipse.uml2.uml.Package;
 
@@ -41,7 +43,7 @@
 	@Override
 	public Object execute(ExecutionEvent arg0) throws ExecutionException {
 		updateSelectedEObject();
-		if (!(selectedEObject instanceof Classifier)) {
+		if (!(selectedEObject instanceof Classifier) || isRunning()) {
 			return null;
 		}
 
@@ -50,15 +52,14 @@
 		if (prepareCmd.prepare()) {
 			IProject project = ProjectManagement.getCurrentProject();
 
-			Job job = new UIJob("Generate ROS code") {
+			Job job = new Job("Generate ROS code") {
 
 				@Override
-				public IStatus runInUIThread(IProgressMonitor monitor) {
+				public IStatus run(IProgressMonitor monitor) {
 					// execute the task ...
 					try {
 						new ExecuteTransformationChain(pkg, project).executeTransformation(monitor, 0);
-					}
-					finally {
+					} finally {
 						prepareCmd.undo();
 					}
 					return Status.OK_STATUS;
@@ -69,4 +70,16 @@
 		}
 		return null;
 	}
+
+	/**
+	 * @return true, if code generation is already running
+	 */
+	public static boolean isRunning() {
+		if (TransformationContext.current != null) {
+			MessageDialog.openError(Display.getCurrent().getActiveShell(),
+					"Code generation in progress", "A previous code generation instance is running, please wait until it finishes before starting another one");
+			return true;
+		}
+		return false;
+	}
 }
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/handlers/RewriteCDTHandler.java b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/handlers/RewriteCDTHandler.java
index a8471a6..880eda3 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/handlers/RewriteCDTHandler.java
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/handlers/RewriteCDTHandler.java
@@ -32,8 +32,8 @@
 import org.eclipse.papyrus.robotics.ros2.codegen.commands.PrepareCodegenCmd;
 import org.eclipse.papyrus.uml.diagram.common.handlers.CmdHandler;
 import org.eclipse.papyrus.uml.tools.utils.PackageUtil;
-import org.eclipse.ui.progress.UIJob;
 import org.eclipse.uml2.uml.Class;
+import org.eclipse.uml2.uml.Classifier;
 import org.eclipse.uml2.uml.Package;
 
 @SuppressWarnings("nls")
@@ -49,7 +49,7 @@
 	@Override
 	public Object execute(ExecutionEvent arg0) throws ExecutionException {
 		updateSelectedEObject();
-		if (!(selectedEObject instanceof Class)) {
+		if (!(selectedEObject instanceof Classifier) || GenerateCodeHandler.isRunning()) {
 			return null;
 		}
 
@@ -58,10 +58,10 @@
 		if (prepareCmd.prepare()) {
 			IProject project = ProjectManagement.getCurrentProject();
 
-			Job job = new UIJob("Generate ROS code") {
+			Job job = new Job("Generate ROS code") {
 
 				@Override
-				public IStatus runInUIThread(IProgressMonitor monitor) {
+				public IStatus run(IProgressMonitor monitor) {
 					// execute the task ...
 					rewriteMap = new HashMap<String, Boolean>();
 					cmdActive = true;
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/message/CreateMsgPackage.xtend b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/message/CreateMsgPackage.xtend
index 58c6804..67f3ba9 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/message/CreateMsgPackage.xtend
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/message/CreateMsgPackage.xtend
@@ -35,6 +35,7 @@
 import static extension org.eclipse.papyrus.robotics.ros2.codegen.utils.PackageTools.pkgName
 import org.eclipse.core.runtime.CoreException
 import org.eclipse.core.runtime.NullProgressMonitor
+import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain
 
 /**
  * Handle creation of a ROS2 message package
@@ -162,7 +163,7 @@
 
 		val genProject = ProjectTools.getProject(msgPkgName);
 		if (genProject === null) {
-			throw new TransformationException(RosTransformations.USER_CANCEL);
+			throw new TransformationException(ExecuteTransformationChain.USER_CANCEL);
 		}
 		return genProject
 	}
diff --git a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/utils/ProjectTools.xtend b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/utils/ProjectTools.xtend
index e88f8c5..cc906b0 100644
--- a/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/utils/ProjectTools.xtend
+++ b/plugins/ros2/org.eclipse.papyrus.robotics.ros2.codegen/src/org/eclipse/papyrus/robotics/ros2/codegen/utils/ProjectTools.xtend
@@ -19,6 +19,7 @@
 import java.util.List
 import org.eclipse.cdt.core.CCProjectNature
 import org.eclipse.cdt.core.CCorePlugin
+import org.eclipse.cdt.core.dom.IPDOMManager
 import org.eclipse.cdt.core.envvar.EnvironmentVariable
 import org.eclipse.cdt.core.model.CoreModel
 import org.eclipse.cdt.core.settings.model.CIncludePathEntry
@@ -35,17 +36,16 @@
 import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.External
 import org.eclipse.papyrus.designer.transformation.base.utils.ProjectManagement
 import org.eclipse.papyrus.designer.transformation.base.utils.TransformationException
+import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain
 import org.eclipse.papyrus.designer.transformation.core.transformations.TransformationContext
 import org.eclipse.papyrus.robotics.ros2.base.EnvironmentUtils
 import org.eclipse.papyrus.robotics.ros2.base.Ros2Constants
-import org.eclipse.papyrus.robotics.ros2.codegen.RosTransformations
-import org.eclipse.papyrus.robotics.ros2.codegen.component.CodeSkeleton
-import org.eclipse.papyrus.robotics.ros2.codegen.component.RoboticsCppCreator
+import org.eclipse.papyrus.robotics.ros2.codegen.handlers.RewriteCDTHandler
 import org.eclipse.papyrus.robotics.ros2.preferences.Ros2PreferenceUtils
 import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil
 import org.eclipse.uml2.uml.Class
 import org.eclipse.uml2.uml.Package
-import org.eclipse.papyrus.robotics.ros2.codegen.handlers.RewriteCDTHandler
+import org.eclipse.papyrus.robotics.ros2.codegen.component.RoboticsCppCreator
 
 /** 
  * get or create a CDT project with a given name
@@ -59,16 +59,20 @@
 	 */
 	static def IProject getProject(String projectName) {
 		var genProject = ProjectManagement.getNamedProject(projectName)
-		if ((genProject !== null && genProject.exists() && genProject.getNature(CCProjectNature.CC_NATURE_ID) === null) ||
-			RewriteCDTHandler.rewriteProject(projectName)
-		) {
+		if ((genProject !== null && genProject.exists() &&
+			genProject.getNature(CCProjectNature.CC_NATURE_ID) === null) ||
+			RewriteCDTHandler.rewriteProject(projectName)) {
 			// not a CDT project (or forced rewrite), force conversion into C++
 			genProject = null
 		}
-
+		// genProject = ProjectManagement.getNamedProject(projectName)
 		if ((genProject === null) || !genProject.exists()) {
 			val projectSupport = LanguageProjectSupport.getProjectSupport("C++")
+			val currentIndexer = CCorePlugin.getIndexManager().getDefaultIndexerId();
+			CCorePlugin.indexManager.defaultIndexerId = IPDOMManager.ID_NO_INDEXER
 			genProject = projectSupport.createProject(projectName, TransformationContext.current.modelRoot)
+			CCorePlugin.indexManager.defaultIndexerId = currentIndexer;
+
 			if (genProject !== null && !genProject.exists()) {
 				throw new RuntimeException(String.format(
 					"project does not exist"
@@ -77,7 +81,7 @@
 			if (genProject !== null) {
 				configureCDT(genProject, projectName.toLowerCase)
 			} else {
-				throw new TransformationException(RosTransformations.USER_CANCEL)
+				throw new TransformationException(ExecuteTransformationChain.USER_CANCEL)
 			}
 		}
 		return genProject
@@ -94,9 +98,8 @@
 		try {
 			val env = EnvironmentUtils.getenv()
 			val amentPrefix = new EnvironmentVariable(Ros2Constants.AMENT_PREFIX_PATH,
-					env.get(Ros2Constants.AMENT_PREFIX_PATH))
-			val pythonPath = new EnvironmentVariable(Ros2Constants.PYTHON_PATH,
-					env.get(Ros2Constants.PYTHON_PATH))
+				env.get(Ros2Constants.AMENT_PREFIX_PATH))
+			val pythonPath = new EnvironmentVariable(Ros2Constants.PYTHON_PATH, env.get(Ros2Constants.PYTHON_PATH))
 
 			// loop over all configurations
 			for (configDescr : cdesc.getConfigurations()) {
@@ -104,7 +107,7 @@
 				val contribEnv = CCorePlugin.getDefault().getBuildEnvironmentManager().getContributedEnvironment();
 				contribEnv.addVariable(amentPrefix, configDescr);
 				contribEnv.addVariable(pythonPath, configDescr);
-				
+
 				val main = ManagedBuildManager.getConfigurationForDescription(configDescr)
 
 				main.setBuildCommand("colcon")
@@ -128,13 +131,35 @@
 				main.setManagedBuildOn(false)
 			}
 			mngr.setProjectDescription(project, cdesc, true, null)
+
+			// CoreModel.getDefault.setProjectDescription(project, cdesc);
 			ManagedBuildManager.saveBuildInfo(project, true)
+			TransformationContext.monitor.subTask("waiting for CDT to finish project setup")
+			waitForCDT
 		} catch (CoreException ce) {
 			throw new RuntimeException(ce.getMessage())
 		}
 	}
 
 	/**
+	 * Wait up to 10 seconds for the CDT indexer to finish
+	 */
+	static def waitForCDT() {
+		if (!CCorePlugin.getIndexManager().indexerIdle) {
+			TransformationContext.monitor.subTask("waiting for CDT indexer")
+			var int i = 0;
+			do {
+				try {
+					Thread.sleep(100);
+				} catch (InterruptedException e1) {
+				}
+				i++;
+			}
+			while (!CCorePlugin.getIndexManager().indexerIdle && i < 100);
+		}
+	}
+
+	/**
 	 * Configure the include paths of a CDT project
 	 */
 	static def configureIncludes(IProject project, List<String> depPkgList) {