/******************************************************************************* * 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.internal.command; import gov.redhawk.ide.codegen.CodegenUtil; import gov.redhawk.ide.codegen.WaveDevSettings; import gov.redhawk.ide.codegen.ui.GenerateCode; import gov.redhawk.ide.codegen.ui.RedhawkCodegenUiActivator; import gov.redhawk.ide.codegen.ui.internal.ManualGeneratorUtil; import gov.redhawk.ide.codegen.ui.internal.WaveDevUtil; import gov.redhawk.ide.codegen.ui.internal.upgrade.DeprecatedCodegenUtil; import gov.redhawk.ide.codegen.ui.internal.upgrade.PropertyKindUtil; import gov.redhawk.model.sca.util.ModelUtil; import gov.redhawk.ui.RedhawkUiActivator; import gov.redhawk.ui.editor.SCAFormEditor; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import mil.jpeojtrs.sca.prf.PrfPackage; import mil.jpeojtrs.sca.spd.Implementation; import mil.jpeojtrs.sca.spd.SoftPkg; import mil.jpeojtrs.sca.spd.SpdPackage; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.IHandler; import org.eclipse.core.expressions.IEvaluationContext; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.util.URI; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.ISaveablePart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.handlers.HandlerUtil; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.statushandlers.StatusManager; /** * This handler is the main entry point to code generation in the UI. It performs several checks, possibly upgrading * project file(s) or settings, and then invokes the code generator. */ public class GenerateCodeHandler extends AbstractHandler implements IHandler { @Override public Object execute(final ExecutionEvent event) throws ExecutionException { // GenerateCode Class simply takes an object to generate from. It should be either a list of implementations, an // implementation or IFile // If the user used a context menu, generate code on the selection(s) final ISelection selection = HandlerUtil.getActiveMenuSelection(event); if (selection != null && !selection.isEmpty()) { handleMenuSelection(event, selection); return null; } // If the user clicked the generate code button in an editor, generate code on the editor input final IEditorPart editor = HandlerUtil.getActiveEditor(event); if (editor != null) { handleEditor(event, editor); return null; } // If we get here, somehow the generate code handler was triggered from somewhere it shouldn't be - log this RedhawkCodegenUiActivator.logError("Generate code handler was triggered without a valid selection", null); return null; } /** * Handle invoking codegen from a context menu selection in either the Project Explorer or an implementation on the * implementations tab * @param event * @param selection The current selection */ private void handleMenuSelection(final ExecutionEvent event, final ISelection selection) { if (!(selection instanceof IStructuredSelection)) { RedhawkCodegenUiActivator.logError("Generate code handler was triggered with an invalid selection", null); return; } final IStructuredSelection ss = (IStructuredSelection) selection; for (Object obj : ss.toList()) { if (obj instanceof IFile && isSpdFile((IFile) obj)) { IFile spdFile = (IFile) obj; try { handleSpdFile(spdFile, spdFile.getProject(), HandlerUtil.getActiveShell(event)); } catch (CoreException e) { StatusManager.getManager().handle(new Status(e.getStatus().getSeverity(), RedhawkCodegenUiActivator.PLUGIN_ID, e.getLocalizedMessage(), e), StatusManager.SHOW | StatusManager.LOG); return; } } else if (obj instanceof Implementation) { Implementation impl = (Implementation) obj; String platformURI = impl.eResource().getURI().toPlatformString(true); IResource spdFile = ResourcesPlugin.getWorkspace().getRoot().findMember(platformURI); if (spdFile instanceof IFile && isSpdFile((IFile) spdFile)) { try { handleImplementations(Collections.singletonList(impl), ((IFile) spdFile).getProject(), HandlerUtil.getActiveShell(event)); } catch (CoreException e) { StatusManager.getManager().handle( new Status(e.getStatus().getSeverity(), RedhawkCodegenUiActivator.PLUGIN_ID, e.getLocalizedMessage(), e), StatusManager.SHOW | StatusManager.LOG); return; } } } } return; } /** * Handle invoking codegen by clicking the generate code button on an editor. * @param event * @param editor The current editor */ private void handleEditor(final ExecutionEvent event, final IEditorPart editor) { if (editor instanceof SCAFormEditor) { SCAFormEditor scaEditor = (SCAFormEditor) editor; // Get SoftPkg from the editor SoftPkg spd = SoftPkg.Util.getSoftPkg(scaEditor.getMainResource()); if (spd == null) { RedhawkCodegenUiActivator.logError("Couldn't get SPD from editor in generate code handler", null); return; } IProject project = ModelUtil.getProject(spd); try { handleImplementations(spd.getImplementation(), project, HandlerUtil.getActiveShell(event)); } catch (CoreException e) { StatusManager.getManager().handle(new Status(e.getStatus().getSeverity(), RedhawkCodegenUiActivator.PLUGIN_ID, e.getLocalizedMessage(), e), StatusManager.SHOW | StatusManager.LOG); } } else { final IEditorInput editorInput = editor.getEditorInput(); if (!(editorInput instanceof IFileEditorInput)) { RedhawkCodegenUiActivator.logError("Couldn't determine input from editor in generate code handler", null); return; } final IFile f = ((IFileEditorInput) editorInput).getFile(); if (!isSpdFile(f)) { RedhawkCodegenUiActivator.logError("Editor input is not an SPD file in generate code handler", null); return; } try { handleSpdFile(f, f.getProject(), HandlerUtil.getActiveShell(event)); } catch (CoreException e) { StatusManager.getManager().handle(new Status(e.getStatus().getSeverity(), RedhawkCodegenUiActivator.PLUGIN_ID, e.getLocalizedMessage(), e), StatusManager.SHOW | StatusManager.LOG); } } } /** * See {@link #handleImplementations(List, IProject, Shell)} * @param spdFile * @param parentProject * @param shell * @throws CoreException */ private void handleSpdFile(IFile spdFile, IProject parentProject, Shell shell) throws CoreException { // The selected object should be an IFile for the SPD final URI spdURI = URI.createPlatformResourceURI(spdFile.getFullPath().toString(), false); final SoftPkg softpkg = ModelUtil.loadSoftPkg(spdURI); handleImplementations(softpkg.getImplementation(), parentProject, shell); } /** * First, saves resources in the project with unsaved changes. Performs several upgrade checks, and finally invokes * code generation. * @param impls The implementation(s) for which to generate code * @param parentProject The IProject which contains all the implementation(s) * @param shell The current shell for user interaction * @throws CoreException */ private void handleImplementations(List<Implementation> impls, IProject parentProject, Shell shell) throws CoreException { if (impls == null || impls.size() == 0) { return; } // Prompt to save (if necessary) before continuing if (!saveRelatedResources(shell, parentProject)) { return; } // Ensure there's a .wavedev file final SoftPkg spd = (SoftPkg) impls.get(0).eContainer(); WaveDevSettings waveDev = CodegenUtil.loadWaveDevSettings(spd); if (waveDev == null) { waveDev = WaveDevUtil.generateWaveDev(spd); } if (waveDev == null) { String message = "Failed to find implementation settings in " + spd.getName() + ".wavedev file"; throw new CoreException(new Status(Status.ERROR, RedhawkUiActivator.PLUGIN_ID, message)); } boolean shouldGenerate = true; try { DeprecatedCodegenUtil.checkDeprecated(shell, impls); PropertyKindUtil.checkProperties(shell, parentProject, impls); shouldGenerate = ManualGeneratorUtil.checkManualGenerator(shell, impls); } catch (OperationCanceledException e) { return; } if (shouldGenerate) { GenerateCode.generate(shell, impls); } } /** * Tries to save the resources which are in the same project as the editorFile provided. The user is prompted to * save if any related unsaved resources are present. * @param event Handler event * @param editorFile File who's project we are using to find related editor pages. * @return True if everything saved correctly. False otherwise. * @throws CoreException */ private boolean saveRelatedResources(final Shell shell, final IProject parentProject) throws CoreException { final Set<ISaveablePart> dirtyPartsSet = getRelatedDirtyParts(parentProject); // If there were unsaved parts in this project. if (!dirtyPartsSet.isEmpty()) { // Prompt the user that they MUST save before generation if (MessageDialog.openQuestion(shell, "File Changed", "Resources in the project '" + parentProject.getName() + "' have unsaved changes. Changes must be saved prior to code generation.\n\nDo you want to save these changes now?")) { ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell); try { dialog.run(false, true, new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) { monitor.beginTask("Saving Resources ...", dirtyPartsSet.size()); // Go through and save each of the parts that were previously identified. for (ISaveablePart dirtyPart : dirtyPartsSet) { dirtyPart.doSave(monitor); monitor.worked(1); if (monitor.isCanceled()) { break; } } monitor.done(); } }); } catch (InvocationTargetException e) { throw new CoreException(new Status(Status.ERROR, RedhawkUiActivator.PLUGIN_ID, "Error while attempting to save editors", e.getCause())); } catch (InterruptedException e) { return false; // The user canceled this save dialog. } if (dialog.getProgressMonitor().isCanceled()) { return false; // The user has canceled another dialog which was passed our monitor. } return true; // User saved all unsaved parts with no errors, generate code. } // User canceled the save dialog do not generate return false; } // Resources don't need to be saved return true; } /** * Returns any ISavableParts which are part of the same project as the given editor file. * @param editorFile The editor file who's project you want to find the other dirty parts of * @return A set of dirty ISavableParts from the same project as the editorFile. */ private Set<ISaveablePart> getRelatedDirtyParts(final IProject project) { final Set<ISaveablePart> dirtyPartsSet = new HashSet<ISaveablePart>(); // Go through each of the workbench windows pages for (IWorkbenchPage page : PlatformUI.getWorkbench().getActiveWorkbenchWindow().getPages()) { // If the page contains at least one dirty editor if (page.getDirtyEditors().length != 0) { // Go through each of the dirty editor parts and see if they belong to the referenced project. for (IWorkbenchPart dirtyPart : page.getDirtyEditors()) { if (dirtyPart instanceof IEditorPart && ((IEditorPart) dirtyPart).getEditorInput() instanceof IFileEditorInput) { IFileEditorInput input = (IFileEditorInput) ((IEditorPart) dirtyPart).getEditorInput(); if (input.getFile().getProject().equals(project)) { if (dirtyPart instanceof ISaveablePart) { dirtyPartsSet.add((ISaveablePart) dirtyPart); } } } } } } return dirtyPartsSet; } /** * Checks the file extension to see if it ends with the SpdPackage extension of ".spd.xml". Returns false if file is * null. * @param file The file under test * @return True if filename ends with .spd.xml false otherwise or if null. */ private boolean isSpdFile(final IFile file) { if (file == null) { return false; } return (file.getName().endsWith(SpdPackage.FILE_EXTENSION)); } @Override public void setEnabled(final Object evaluationContext) { final IEvaluationContext context = (IEvaluationContext) evaluationContext; final IWorkbenchWindow window = (IWorkbenchWindow) context.getVariable("activeWorkbenchWindow"); if (window == null) { setBaseEnabled(false); return; } // We check two things. // 1.) If the user has right clicked on an item that is appropriate // 2.) If not then we check the open active editor. // The highest priority check is the right click action. If they have right clicked on something that object is // the proper context // so we check that first. if (context.getVariable("activeMenuSelection") != null && context.getVariable("activeMenuSelection") instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection) context.getVariable("activeMenuSelection"); for (Object selection : ss.toList()) { if (selection instanceof IFile && isSpdFile((IFile) selection) && waveDevFileExists((IFile) selection)) { continue; } else if (selection instanceof Implementation) { Implementation impl = (Implementation) selection; WaveDevSettings wavDev = CodegenUtil.getWaveDevSettings(impl); if (wavDev != null) { continue; } } // One of the selections did not meet the tests above. setBaseEnabled(false); return; } // If we made it through the for loop then all the objects passed. setBaseEnabled(true); return; } else if (window.getActivePage() != null) { // If we've gotten this far we know they have not right clicked on anything so all we need to check is the // open active editor IWorkbenchPage activePage = window.getActivePage(); IEditorPart activeEditor = activePage.getActiveEditor(); if (activeEditor != null) { if ((activeEditor.getEditorInput() instanceof FileEditorInput)) { IFile spdFile = ((FileEditorInput) activeEditor.getEditorInput()).getFile(); if (isSpdFile(spdFile)) { setBaseEnabled(waveDevFileExists(spdFile)); return; } } } } // Set to false if none of the appropriate situations are met setBaseEnabled(false); return; } /** * Helper method where we attempt to find the WavedevSettings file given either a PRF or SPD file * * @param file An IFile element, either PRF or SPD * @return The associated WavedevSettings file should we be able to find it */ private Boolean waveDevFileExists(final IFile file) { if (file != null && file.exists()) { String filePath = ""; if (file.getFullPath().toString().endsWith(SpdPackage.FILE_EXTENSION)) { filePath = file.getFullPath().toString(); } else if (file.getFullPath().toString().endsWith(PrfPackage.FILE_EXTENSION)) { filePath = file.getFullPath().toString().replace(PrfPackage.FILE_EXTENSION, SpdPackage.FILE_EXTENSION); } else { return false; } final URI spdUri = URI.createPlatformResourceURI(filePath, true).appendFragment(SoftPkg.EOBJECT_PATH); final URI waveUri = URI.createPlatformPluginURI(CodegenUtil.getWaveDevSettingsURI(spdUri).lastSegment(), true).appendFragment("/"); final IFile waveDevFile = file.getProject().getFile(waveUri.lastSegment()); return waveDevFile.isSynchronized(IResource.DEPTH_INFINITE) && waveDevFile.exists(); } return false; } }