/******************************************************************************* * This file is protected by Copyright. * Please refer to the COPYRIGHT file distributed with this source distribution. * * This file is part of REDHAWK IDE. * * 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 *******************************************************************************/ package gov.redhawk.ide.codegen.ui; import gov.redhawk.ide.codegen.CodegenPackage; import gov.redhawk.ide.codegen.CodegenUtil; import gov.redhawk.ide.codegen.FileStatus; import gov.redhawk.ide.codegen.FileToCRCMap; import gov.redhawk.ide.codegen.ICodeGeneratorDescriptor; import gov.redhawk.ide.codegen.IScaComponentCodegen; import gov.redhawk.ide.codegen.ITemplateDesc; import gov.redhawk.ide.codegen.ImplementationSettings; import gov.redhawk.ide.codegen.RedhawkCodegenActivator; import gov.redhawk.ide.codegen.WaveDevSettings; import gov.redhawk.ide.codegen.ui.internal.GenerateFilesDialog; import gov.redhawk.ide.codegen.ui.internal.GeneratorConsole; import gov.redhawk.ide.codegen.ui.internal.GeneratorUtil; import gov.redhawk.ide.codegen.ui.internal.WaveDevUtil; import gov.redhawk.ide.codegen.ui.preferences.CodegenPreferenceConstants; import gov.redhawk.ide.codegen.util.PropertyUtil; import gov.redhawk.model.sca.commands.ScaModelCommand; import gov.redhawk.model.sca.util.ModelUtil; import gov.redhawk.sca.util.SubMonitor; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import mil.jpeojtrs.sca.spd.CodeFileType; import mil.jpeojtrs.sca.spd.Implementation; import mil.jpeojtrs.sca.spd.SoftPkg; import mil.jpeojtrs.sca.util.NamedThreadFactory; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.CompoundCommand; import org.eclipse.emf.common.util.EMap; import org.eclipse.emf.edit.command.AddCommand; import org.eclipse.emf.edit.command.SetCommand; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.transaction.RunnableWithResult; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.emf.transaction.util.TransactionUtil; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.console.IConsole; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.ide.ResourceUtil; import org.eclipse.ui.progress.WorkbenchJob; import org.osgi.framework.Version; /** * This class is the primary entry point to code generation. * @since 7.0 */ public final class GenerateCode { private GenerateCode() { } private static final ExecutorService EXECUTOR_POOL = Executors.newSingleThreadExecutor(new NamedThreadFactory(GenerateCode.class.getName())); /** * Performs the code generation process for the specified implementation(s). The process may prompt the user for * input. The process occurs in a job and is thus asynchronous. * <p/> * This entry point does not perform any deprecation checks, upgrades, etc. For that, see * {@link gov.redhawk.ide.codegen.ui.internal.command.GenerateCodeHandler}. * @since 8.0 */ public static void generate(final Shell shell, final List<Implementation> impls) { if (impls.isEmpty()) { return; } Job getFilesJob = new Job("Calculating files to generate...") { @Override protected IStatus run(IProgressMonitor monitor) { // Map of Implementation -> ( map of FileName relative to output location -> true will regenerate, // false wants to regenerate but contents different SubMonitor progress = SubMonitor.convert(monitor, "Calculating files to generate", impls.size()); final Map<Implementation, Set<FileStatus>> implMap = new HashMap<Implementation, Set<FileStatus>>(); for (Implementation impl : impls) { try { Set<FileStatus> resultSet = getFilesToGenerate(progress.newChild(1, SubMonitor.SUPPRESS_NONE), impl); implMap.put(impl, resultSet); } catch (CoreException e) { return new Status(e.getStatus().getSeverity(), RedhawkCodegenUiActivator.PLUGIN_ID, "Failed to calculate files to generate", e); } } progress.done(); WorkbenchJob checkFilesJob = new WorkbenchJob("Check files") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { Set<FileStatus> aggregate = new HashSet<FileStatus>(); for (Set<FileStatus> v : implMap.values()) { aggregate.addAll(v); } final IPreferenceStore store = RedhawkCodegenUiActivator.getDefault().getPreferenceStore(); List<String> filesToGenerate = new ArrayList<String>(); boolean showDialog = false; boolean generateDefault = store.getBoolean(CodegenPreferenceConstants.P_ALWAYS_GENERATE_DEFAULTS); if (generateDefault) { for (FileStatus s : aggregate) { if (!s.isDoIt() && s.getType() != FileStatus.Type.USER) { showDialog = true; break; } } } else { showDialog = true; } if (showDialog) { GenerateFilesDialog dialog = new GenerateFilesDialog(shell, aggregate); dialog.setBlockOnOpen(true); if (dialog.open() == Window.OK) { String[] result = dialog.getFilesToGenerate(); if (result != null) { filesToGenerate.addAll(Arrays.asList(result)); } } else { return Status.CANCEL_STATUS; } } else { for (FileStatus s : aggregate) { if (s.isDoIt()) { filesToGenerate.add(s.getFilename()); } } } final Map<Implementation, String[]> implFileMap = new HashMap<Implementation, String[]>(); for (Map.Entry<Implementation, Set<FileStatus>> entry : implMap.entrySet()) { Set<String> subsetFilesToGenerate = new HashSet<String>(); for (FileStatus s : entry.getValue()) { subsetFilesToGenerate.add(s.getFilename()); } Set<String> filesToRemove = new HashSet<String>(subsetFilesToGenerate); filesToRemove.removeAll(filesToGenerate); subsetFilesToGenerate.removeAll(filesToRemove); implFileMap.put(entry.getKey(), subsetFilesToGenerate.toArray(new String[subsetFilesToGenerate.size()])); } WorkspaceJob processJob = new WorkspaceJob("Generating...") { @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { return processImpls(implFileMap, monitor); } }; processJob.setUser(true); processJob.schedule(); return Status.OK_STATUS; } }; checkFilesJob.setUser(false); checkFilesJob.setSystem(true); checkFilesJob.schedule(); return Status.OK_STATUS; } }; getFilesJob.setUser(true); getFilesJob.schedule(); } private static IStatus processImpls(Map<Implementation, String[]> implMap, IProgressMonitor monitor) throws CoreException { try { SubMonitor progress = SubMonitor.convert(monitor, "Generating...", implMap.size() + 2); final SoftPkg softPkg = (SoftPkg) implMap.entrySet().iterator().next().getKey().eContainer(); final TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(softPkg); final IProject project = ModelUtil.getProject(softPkg); final WaveDevSettings waveDev = CodegenUtil.loadWaveDevSettings(softPkg); // Refresh project before generating code project.refreshLocal(IResource.DEPTH_INFINITE, null); final MultiStatus retStatus = new MultiStatus(RedhawkCodegenUiActivator.PLUGIN_ID, IStatus.OK, "Problems while generating code", null); for (Map.Entry<Implementation, String[]> entry : implMap.entrySet()) { if (progress.isCanceled()) { return Status.CANCEL_STATUS; } SubMonitor implGenerateWork = progress.newChild(1); implGenerateWork.beginTask("Generating " + entry.getKey().getId(), 1); final Implementation impl = entry.getKey(); IStatus status = validate(project, softPkg, impl, waveDev); if (!status.isOK()) { retStatus.add(status); if (retStatus.getSeverity() == IStatus.ERROR) { return retStatus; } } // Generate code for each implementation final EMap<String, ImplementationSettings> implSet = waveDev.getImplSettings(); // Generate code for implementation final ImplementationSettings settings = implSet.get(impl.getId()); final ArrayList<FileToCRCMap> mapping = new ArrayList<FileToCRCMap>(); String[] filesToGenerate = entry.getValue(); status = generateImplementation(filesToGenerate, impl, settings, implGenerateWork.newChild(1), softPkg, mapping); if (!status.isOK()) { retStatus.add(status); if (status.getSeverity() == IStatus.ERROR) { return retStatus; } } // Update CRCs for implementation try { updateCRCs(domain, settings, mapping); } catch (final IOException e) { retStatus.add(new Status(IStatus.WARNING, RedhawkCodegenUiActivator.PLUGIN_ID, "Problem while generating CRCs for implementations", e)); } ImplementationSettings implSettings = WaveDevUtil.getImplSettings(impl); final IScaComponentCodegen generator = GeneratorUtil.getGenerator(implSettings); final Version codeGenVersion = generator.getCodegenVersion(); if (new Version(1, 10, 0).compareTo(codeGenVersion) <= 0) { // Set the version ScaModelCommand.execute(softPkg, new ScaModelCommand() { @Override public void execute() { softPkg.setType(generator.getCodegenVersion().toString()); } }); } } // Save updates to the SPD (codegen version) and wavedev (historically, file CRCs) // Our model object may / most likely belongs to an editor progress.setTaskName("Saving resource changes"); RunnableWithResult<Boolean> saveViaEditor = new RunnableWithResult.Impl<Boolean>() { @Override public void run() { IEditorPart editorPart = ResourceUtil.findEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), project.getFile(softPkg.eResource().getURI().lastSegment())); if (editorPart != null && editorPart.isDirty()) { editorPart.doSave(new NullProgressMonitor()); setResult(true); } } }; Display.getDefault().syncExec(saveViaEditor); // If we were unable to save via editor, save the resources directly if (saveViaEditor.getResult() == null) { try { softPkg.eResource().save(null); } catch (IOException e) { retStatus.add(new Status(Status.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Error when updating generator version", e)); } try { waveDev.eResource().save(null); if (domain != null) { ((BasicCommandStack) domain.getCommandStack()).saveIsDone(); domain.getCommandStack().flush(); } } catch (IOException e) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Unable to save the updated implementation settings", e)); } } // Add the top-level build.sh script builder only for projects that are deprecated (i.e. 1.8) // We should remove this section and associated code when we no longer support codegen of 1.8 projects progress.setTaskName("Adding builders"); boolean isDeprecated = false; if (waveDev != null) { for (final ImplementationSettings implSettings : waveDev.getImplSettings().values()) { if (implSettings != null) { ICodeGeneratorDescriptor generator = RedhawkCodegenActivator.getCodeGeneratorsRegistry().findCodegen(implSettings.getGeneratorId()); if (generator != null && generator.isDeprecated()) { isDeprecated = true; break; } } } } if (isDeprecated) { CodegenUtil.addTopLevelBuildScriptBuilder(project, progress.newChild(1)); } else { CodegenUtil.removeDeprecatedBuilders(project, progress.newChild(1)); } // Refresh project after generating code progress.setTaskName("Refreshing project"); project.refreshLocal(IResource.DEPTH_INFINITE, progress.newChild(1)); if (ResourcesPlugin.getWorkspace().getDescription().isAutoBuilding()) { // Schedule a new job which will run a full build; this should ensure all resource change // notifications are dispatched before beginning the build final WorkspaceJob buildJob = new WorkspaceJob("Building Project " + project.getName()) { @Override public IStatus runInWorkspace(final IProgressMonitor monitor) throws CoreException { final int CLEAN_WORK = 15; final int BUILD_WORK = 85; SubMonitor progress = SubMonitor.convert(monitor, CLEAN_WORK + BUILD_WORK); project.build(IncrementalProjectBuilder.CLEAN_BUILD, progress.newChild(CLEAN_WORK)); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } project.build(IncrementalProjectBuilder.FULL_BUILD, progress.newChild(BUILD_WORK)); return Status.OK_STATUS; } }; buildJob.setPriority(Job.LONG); buildJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); buildJob.schedule(); } return retStatus; } finally { monitor.done(); } } private static IStatus generateImplementation(final String[] files, final Implementation impl, final ImplementationSettings settings, final IProgressMonitor monitor, final SoftPkg softpkg, final List<FileToCRCMap> crcMap) { if (settings == null) { return new Status(IStatus.WARNING, RedhawkCodegenUiActivator.PLUGIN_ID, "Unable to find settings (wavedev) for " + impl.getId() + ", skipping generation"); } final String implId = impl.getId(); final MultiStatus retStatus = new MultiStatus(RedhawkCodegenUiActivator.PLUGIN_ID, IStatus.OK, "Problems while generating implementation " + implId, null); final SubMonitor progress = SubMonitor.convert(monitor, 1); progress.setTaskName("Generating implementation " + implId); if (settings.getGeneratorId() != null) { final String codegenId = settings.getGeneratorId(); final ICodeGeneratorDescriptor codeGenDesc = RedhawkCodegenActivator.getCodeGeneratorsRegistry().findCodegen(codegenId); if (codeGenDesc == null) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "The code generator specified in the settings (wavedev) could not be found. Check your generator selection for the implementation")); return retStatus; } // Find the code generator console, or create it if necessary GeneratorConsole genConsole = null; final IConsole[] consoles = ConsolePlugin.getDefault().getConsoleManager().getConsoles(); for (final IConsole console : consoles) { if (console instanceof GeneratorConsole && console.getType().equals(codeGenDesc.getId())) { genConsole = (GeneratorConsole) console; break; } } if (genConsole == null) { genConsole = new GeneratorConsole(codeGenDesc); ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[] { genConsole }); } try { // Get the specific code generator final IScaComponentCodegen generator = codeGenDesc.getGenerator(); // Validate that we can perform code generation IStatus status = generator.validate(); if (!status.isOK()) { retStatus.add(status); if (status.getSeverity() == IStatus.ERROR) { return retStatus; } } final IFile mainFile = generator.getDefaultFile(impl, settings); boolean openEditor = (mainFile == null || !mainFile.exists()); status = generator.generate(settings, impl, genConsole.getOutStream(), genConsole.getErrStream(), progress.newChild(1), files, generator.shouldGenerate(), crcMap); if (!status.isOK()) { retStatus.add(status); if (status.getSeverity() == IStatus.ERROR) { return retStatus; } } // Update last generated date final WaveDevSettings wavedev = CodegenUtil.loadWaveDevSettings(softpkg); PropertyUtil.setLastGenerated(wavedev, settings, new Date(System.currentTimeMillis())); if (openEditor && (mainFile != null) && mainFile.exists()) { progress.subTask("Opening editor for main file"); // Open the selected editor final WorkbenchJob openJob = new WorkbenchJob("Open editor") { @Override public IStatus runInUIThread(final IProgressMonitor monitor) { try { IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), mainFile, true); } catch (final PartInitException p) { return new Status(IStatus.WARNING, RedhawkCodegenUiActivator.PLUGIN_ID, "Unable to open main file for editing."); } return new Status(IStatus.OK, RedhawkCodegenUiActivator.PLUGIN_ID, ""); } }; openJob.setPriority(Job.SHORT); openJob.schedule(); } if (retStatus.isOK()) { return new Status(IStatus.OK, RedhawkCodegenUiActivator.PLUGIN_ID, "Succeeded generating code for implementation"); } } catch (final CoreException e) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Unexpected error", e)); } } else { retStatus.add(new Status( IStatus.WARNING, RedhawkCodegenUiActivator.PLUGIN_ID, "No code generator is specified in the settings (wavedev). Code generation was skipped for the implementation. Check your generator selection for the implementation.")); } return retStatus; } /** * Figures out which files will be generated, possibly prompting the user to * confirm generation if files have been modified since they were originally * generated. * * @param generator The code generator to use * @param implSettings The settings for the implementation * @param softpkg The SPD * @return An array of the files which are to be generated * @throws CoreException A problem occurs while determining which files to generate * @since 8.0 */ private static Set<FileStatus> getFilesToGenerate(IProgressMonitor monitor, Implementation impl) throws CoreException { SubMonitor subMonitor = SubMonitor.convert(monitor, "Calculating files for implementation " + impl.getId(), IProgressMonitor.UNKNOWN); try { final ImplementationSettings implSettings = WaveDevUtil.getImplSettings(impl); final IScaComponentCodegen generator = GeneratorUtil.getGenerator(implSettings); final SoftPkg softpkg = (SoftPkg) impl.eContainer(); if (generator.shouldGenerate()) { Future<Set<FileStatus>> future = EXECUTOR_POOL.submit(new Callable<Set<FileStatus>>() { @Override public Set<FileStatus> call() throws Exception { return generator.getGeneratedFilesStatus(implSettings, softpkg); } }); Set<FileStatus> retVal; while (true) { try { retVal = future.get(1, TimeUnit.SECONDS); break; } catch (InterruptedException e) { throw new CoreException(Status.CANCEL_STATUS); } catch (ExecutionException e) { if (e.getCause() instanceof CoreException) { throw ((CoreException) e.getCause()); } else { throw new CoreException(new Status(Status.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Failed in calling generator get generated files.", e)); } } catch (TimeoutException e) { if (subMonitor.isCanceled()) { future.cancel(true); throw new OperationCanceledException(); } } } return retVal; } else { return Collections.emptySet(); } } finally { subMonitor.done(); } } /** * Performs several validations to detect problems prior to code generation. * * @param project The project to validate * @param softPkg The {@link SoftPkg} to validate * @param impls The {@link Implementation}s to be generated * @param waveDev The wavedev to validate * @return An {@link IStatus} indicating any issues found; problems should be of severity {@link IStatus#ERROR} to * prevent code generation */ private static IStatus validate(final IProject project, final SoftPkg softPkg, Implementation impl, final WaveDevSettings waveDev) { final MultiStatus retStatus = new MultiStatus(RedhawkCodegenUiActivator.PLUGIN_ID, IStatus.OK, "Validation problems prior to generating code", null); CodeFileType projectType = softPkg.getImplementation().get(0).getCode().getType(); if (project == null) { return new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Project does not exist"); } // Check XML files try { // Check SPD file final String spdFileName = ModelUtil.getSpdFileName(softPkg); if (spdFileName == null) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Unable to determine SPD filename")); } else { final IFile file = project.getFile(spdFileName); if (!file.exists()) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Cannot locate SPD file")); } else { for (final IMarker mark : file.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE)) { if (mark.getAttribute(IMarker.SEVERITY, 0) == IMarker.SEVERITY_ERROR) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "The SPD file contains errors")); break; } } } } // Don't validate SCD or PRF if project is a Shared Library if (!impl.isSharedLibrary()) { // Check SCD file final String scdFileName = ModelUtil.getScdFileName(softPkg); if (scdFileName == null) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Unable to determine SCD filename")); } else { final IFile file = project.getFile(scdFileName); if (!file.exists()) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Cannot locate SCD file")); } else { for (final IMarker mark : file.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE)) { if (mark.getAttribute(IMarker.SEVERITY, 0) == IMarker.SEVERITY_ERROR) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "The SCD file contains errors")); break; } } } } // Check PRF file final String prfFileName = ModelUtil.getPrfFileName(softPkg.getPropertyFile()); if (prfFileName != null) { final IFile file = project.getFile(prfFileName); if (!file.exists()) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Cannot locate PRF file")); } else { for (final IMarker mark : file.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE)) { if (mark.getAttribute(IMarker.SEVERITY, 0) == IMarker.SEVERITY_ERROR) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "The PRF file contains errors")); break; } } } } } } catch (final CoreException e) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "A problem occurred while verifying the XML files", e)); } // Wavedev checks if (waveDev == null) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Unable to find project settings (wavedev) file. Cannot generate code.")); } else { ImplementationSettings implSettings = waveDev.getImplSettings().get(impl.getId()); if (implSettings == null) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Unable to find settings in wavedev file for implementation " + impl.getId())); } else { String templateId = implSettings.getTemplate(); ITemplateDesc template = RedhawkCodegenActivator.getCodeGeneratorTemplatesRegistry().findTemplate(templateId); if (template == null) { retStatus.add(new Status(IStatus.ERROR, RedhawkCodegenUiActivator.PLUGIN_ID, "Unable to find code generator template" + templateId)); } } } return retStatus; } private static void updateCRCs(EditingDomain domain, final ImplementationSettings implSettings, final List<FileToCRCMap> crcMap) throws IOException { final Map<FileToCRCMap, FileToCRCMap> foundCRCs = new HashMap<FileToCRCMap, FileToCRCMap>(); for (final FileToCRCMap entry : implSettings.getGeneratedFileCRCs()) { for (final FileToCRCMap currCRC : crcMap) { if (entry.getFile().equals(currCRC.getFile())) { foundCRCs.put(entry, currCRC); crcMap.remove(currCRC); break; } } } if (domain != null) { final CompoundCommand updateCommand = new CompoundCommand(); for (final FileToCRCMap crc : crcMap) { final AddCommand cmd = new AddCommand(domain, implSettings.getGeneratedFileCRCs(), crc); updateCommand.append(cmd); } for (final Entry<FileToCRCMap, FileToCRCMap> crcEntry : foundCRCs.entrySet()) { final SetCommand cmd = new SetCommand(domain, crcEntry.getKey(), CodegenPackage.Literals.FILE_TO_CRC_MAP__CRC, crcEntry.getValue().getCrc()); updateCommand.append(cmd); } if (!updateCommand.isEmpty()) { domain.getCommandStack().execute(updateCommand); } } else { for (final FileToCRCMap crc : crcMap) { implSettings.getGeneratedFileCRCs().add(crc); } for (final Entry<FileToCRCMap, FileToCRCMap> crcEntry : foundCRCs.entrySet()) { crcEntry.getKey().setCrc(crcEntry.getValue().getCrc()); } } } }