/******************************************************************************* * Copyright (c) 2016 Pivotal, Inc. * 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: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.cloudfoundry.deployment; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IContainer; 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.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.DialogSettings; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.reconciler.IReconcilingStrategy; import org.eclipse.jface.text.source.CompositeRuler; import org.eclipse.jface.text.source.ISharedTextColors; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.OverviewRuler; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Sash; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.handlers.IHandlerActivation; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.model.BaseWorkbenchContentProvider; import org.eclipse.ui.model.WorkbenchLabelProvider; import org.eclipse.ui.texteditor.AnnotationPreference; import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.springframework.ide.eclipse.boot.dash.BootDashActivator; import org.springframework.ide.eclipse.boot.dash.dialogs.DeploymentPropertiesDialogModel; import org.springframework.ide.eclipse.boot.dash.dialogs.DeploymentPropertiesDialogModel.ManifestType; import org.springframework.ide.eclipse.cloudfoundry.manifest.editor.ManifestYamlSourceViewerConfiguration; import org.springframework.ide.eclipse.editor.support.ForceableReconciler; import org.springframework.ide.eclipse.editor.support.util.ShellProviders; import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression; import org.springsource.ide.eclipse.commons.livexp.core.UIValueListener; import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult; import org.springsource.ide.eclipse.commons.livexp.core.ValueListener; /** * Cloud Foundry Application deployment properties dialog. Allows user to select * manifest YAML file or enter deployment manifest YAML manually. * * @author Alex Boyko * */ public class DeploymentPropertiesDialog extends TitleAreaDialog { final static private String DIALOG_LIST_HEIGHT_SETTING = "ManifestFileDialog.listHeight"; //$NON-NLS-1$ final static private String YML_EXTENSION = "yml"; //$NON-NLS-1$ final static private String[] FILE_FILTER_NAMES = new String[] {"Manifest YAML files - *manifest*.yml", "YAML files - *.yml", "All files - *.*"}; private static abstract class DeepFileFilter extends ViewerFilter { @Override public boolean select(Viewer viewer, Object parent, Object element) { if (element instanceof IResource && !((IResource)element).isDerived()) { if (element instanceof IFile) { return acceptFile((IFile)element); } if (element instanceof IContainer) { try { IContainer container = (IContainer) element; for (IResource resource : container.members()) { boolean select = select(viewer, container, resource); if (select) { return true; } } } catch (CoreException e) { // ignore } } } return false; } abstract protected boolean acceptFile(IFile file); } final static private ViewerFilter YAML_FILE_FILTER = new DeepFileFilter() { @Override protected boolean acceptFile(IFile file) { return YML_EXTENSION.equals(file.getFileExtension()); } }; final static private ViewerFilter MANIFEST_YAML_FILE_FILTER = new DeepFileFilter() { @Override protected boolean acceptFile(IFile file) { return file.getName().toLowerCase().contains("manifest") && YML_EXTENSION.equals(file.getFileExtension()); } }; final static private ViewerFilter ALL_FILES = new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parent, Object element) { return (element instanceof IResource) && !((IResource)element).isDerived(); } }; final static private ViewerFilter[][] RESOURCE_FILTERS = new ViewerFilter[][] { {MANIFEST_YAML_FILE_FILTER}, {YAML_FILE_FILTER}, {ALL_FILES} }; final static private int DEFAULT_WORKSPACE_GROUP_HEIGHT = 200; private SourceViewerDecorationSupport fileYamlDecorationSupport; private SourceViewerDecorationSupport manualYamlDecorationSupport; private Label fileLabel; private Sash resizeSash; private SourceViewer fileYamlViewer; private SourceViewer manualYamlViewer; private AppNameAnnotationSupport fileYamlAppNameAnnotationSupport; private AppNameAnnotationSupport manualYamlAppNameAnnotationSupport; private TreeViewer workspaceViewer; private Button refreshButton; private Button buttonFileManifest; private Button buttonManualManifest; private Group fileGroup; private Group yamlGroup; private Composite fileYamlComposite; private Composite manualYamlComposite; private Combo fileFilterCombo; private IHandlerService service; private List<IHandlerActivation> activations; private EditorActionHandler[] handlers = new EditorActionHandler[] { new EditorActionHandler(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, SourceViewer.CONTENTASSIST_PROPOSALS), new EditorActionHandler(IWorkbenchCommandConstants.EDIT_UNDO, SourceViewer.UNDO), new EditorActionHandler(IWorkbenchCommandConstants.EDIT_REDO, SourceViewer.REDO), }; private ISharedTextColors colorsCache = new ISharedTextColors() { private Map<RGB, Color> colors = new HashMap<>(); @Override public Color getColor(RGB rgb) { Color color = colors.get(rgb); if (color == null) { color = new Color(getShell().getDisplay(), rgb); colors.put(rgb, color); } return color; } @Override public void dispose() { for (Color color : colors.values()) { if (!color.isDisposed()) { color.dispose(); } } colors.clear(); } }; private ISelectionChangedListener selectionListener = new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent e) { IResource resource = (IResource) getStructuredSelection(workspaceViewer).getFirstElement(); model.setSelectedManifest(resource); } }; final private DeploymentPropertiesDialogModel model; public DeploymentPropertiesDialog(Shell parentShell, DeploymentPropertiesDialogModel model) { super(parentShell); this.model = model; this.service = (IHandlerService) PlatformUI.getWorkbench().getAdapter(IHandlerService.class); this.activations = new ArrayList<>(handlers.length); } @Override protected Control createDialogArea(Composite parent) { setTitle("Select Deployment Manifest for project '" + model.getProjectName() + "'"); Composite container = (Composite) super.createDialogArea(parent); final Composite composite = new Composite(container, parent.getStyle()); composite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); composite.setLayout(new GridLayout()); createModeSwitchGroup(composite); createFileGroup(composite); createResizeSash(composite); createYamlContentsGroup(composite); activateHanlders(); model.type.addListener(new ValueListener<ManifestType>() { @Override public void gotValue(LiveExpression<ManifestType> exp, ManifestType type) { GridData gridData; boolean isFile = type == ManifestType.FILE; buttonFileManifest.setSelection(isFile); buttonManualManifest.setSelection(!isFile); refreshButton.setEnabled(isFile && !workspaceViewer.getSelection().isEmpty()); workspaceViewer.getControl().setEnabled(isFile); fileLabel.setEnabled(isFile); gridData = GridDataFactory.copyData((GridData) fileGroup.getLayoutData()); gridData.exclude = !isFile; fileGroup.setVisible(isFile); fileGroup.setLayoutData(gridData); gridData = GridDataFactory.copyData((GridData) resizeSash.getLayoutData()); gridData.exclude = !isFile; resizeSash.setVisible(isFile); resizeSash.setLayoutData(gridData); fileGroup.getParent().layout(); fileYamlComposite.setVisible(isFile); gridData = GridDataFactory.copyData((GridData) fileYamlComposite.getLayoutData()); gridData.exclude = !isFile; fileYamlComposite.setLayoutData(gridData); manualYamlComposite.setVisible(!isFile); gridData = GridDataFactory.copyData((GridData) manualYamlComposite.getLayoutData()); gridData.exclude = isFile; manualYamlComposite.setLayoutData(gridData); yamlGroup.layout(); yamlGroup.getParent().layout(); } }); model.getValidator().addListener(new UIValueListener<ValidationResult>() { @Override protected void uiGotValue(LiveExpression<ValidationResult> exp, ValidationResult value) { ValidationResult result = exp.getValue(); if (getButton(IDialogConstants.OK_ID) != null) { getButton(IDialogConstants.OK_ID).setEnabled(result.status != IStatus.ERROR); } setMessage(result.msg, result.getMessageProviderStatus()); } }); parent.pack(true); /* * Reveal the selected manifest file in the workspace viewer now when * controls are created and laid out */ if (!workspaceViewer.getSelection().isEmpty()) { workspaceViewer.setSelection(workspaceViewer.getSelection(), true); } return container; } private void createModeSwitchGroup(Composite composite) { Group typeGroup = new Group(composite, SWT.NONE); typeGroup.setText("Manifest Type"); typeGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create()); typeGroup.setLayout(new GridLayout(2, true)); buttonFileManifest = new Button(typeGroup, SWT.RADIO); buttonFileManifest.setText("File"); buttonFileManifest.setSelection(model.isFileManifestType()); buttonFileManifest.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (buttonFileManifest.getSelection()) { model.setManifestType(ManifestType.FILE); } } }); buttonFileManifest.setLayoutData(GridDataFactory.fillDefaults().create()); buttonManualManifest = new Button(typeGroup, SWT.RADIO); buttonManualManifest.setText("Manual"); buttonManualManifest.setSelection(model.isManualManifestType()); buttonManualManifest.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (buttonManualManifest.getSelection()) { model.setManifestType(ManifestType.MANUAL); } } }); buttonManualManifest.setLayoutData(GridDataFactory.fillDefaults().create()); } private void createFileGroup(Composite composite) { fileGroup = new Group(composite, SWT.NONE); fileGroup.setText("Workspace File"); fileGroup.setLayout(new GridLayout(2, false)); int height = DEFAULT_WORKSPACE_GROUP_HEIGHT; try { height = getDialogBoundsSettings().getInt(DIALOG_LIST_HEIGHT_SETTING); } catch (NumberFormatException e) { // ignore exception } fileGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, height).create()); workspaceViewer = new TreeViewer(fileGroup); workspaceViewer.setContentProvider(new BaseWorkbenchContentProvider()); workspaceViewer.setLabelProvider(new WorkbenchLabelProvider()); workspaceViewer.setInput(ResourcesPlugin.getWorkspace().getRoot()); workspaceViewer.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); /* * Do not set the selection based on manifest file changes outside of UI. Bad. SWT doesn't like it. */ if (model.getSelectedManifest() != null) { workspaceViewer.setSelection(new StructuredSelection(new Object[] { model.getSelectedManifest() }), true); } workspaceViewer.addSelectionChangedListener(selectionListener); Composite fileButtonsComposite = new Composite(fileGroup, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.horizontalSpacing = 0; layout.verticalSpacing = 0; fileButtonsComposite.setLayout(layout); fileButtonsComposite.setLayoutData(GridDataFactory.fillDefaults().create()); refreshButton = new Button(fileButtonsComposite, SWT.PUSH); refreshButton.setImage(BootDashActivator.getDefault().getImageRegistry().get(BootDashActivator.REFRESH_ICON)); refreshButton.setText("Refresh"); refreshButton.setEnabled(false); refreshButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { refreshManifests(); } }); refreshButton.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create()); fileFilterCombo = new Combo(fileGroup, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY); fileFilterCombo.setItems(FILE_FILTER_NAMES); int selectionIndex = 0; IResource manifestFile = model.getSelectedManifest(); if (manifestFile != null) { selectionIndex = RESOURCE_FILTERS.length - 1; for (int i = 0; i < RESOURCE_FILTERS.length; i++) { boolean accept = true; for (ViewerFilter filter : RESOURCE_FILTERS[i]) { accept = filter.select(null, null, manifestFile); if (!accept) { break; } } if (accept) { selectionIndex = i; break; } } } workspaceViewer.setFilters(RESOURCE_FILTERS[selectionIndex]); fileFilterCombo.select(selectionIndex); fileFilterCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // Remove selection listener to set selection from current pathModel value workspaceViewer.removeSelectionChangedListener(selectionListener); workspaceViewer.setFilters(RESOURCE_FILTERS[fileFilterCombo.getSelectionIndex()]); // Add the selection listener back after the initial value has been set workspaceViewer.addSelectionChangedListener(selectionListener); } }); } private void createResizeSash(Composite composite) { resizeSash = new Sash(composite, SWT.HORIZONTAL); resizeSash.setLayoutData(GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 4).grab(true, false).create()); resizeSash.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { GridData listLayoutData = (GridData) fileGroup.getLayoutData(); int newHeight = listLayoutData.heightHint + e.y - resizeSash.getBounds().y; if (newHeight < listLayoutData.minimumHeight) { newHeight = listLayoutData.minimumHeight; e.doit = false; } listLayoutData.heightHint = newHeight; fileGroup.setLayoutData(listLayoutData); fileGroup.getParent().layout(); } }); } private void createYamlContentsGroup(Composite composite) { yamlGroup = new Group(composite, SWT.NONE); yamlGroup.setText("YAML Content"); yamlGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); yamlGroup.setLayout(new GridLayout()); fileYamlComposite = new Composite(yamlGroup, SWT.NONE); fileYamlComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); GridLayout layout = new GridLayout(3, false); layout.marginWidth = 0; fileYamlComposite.setLayout(layout); fileLabel = new Label(fileYamlComposite, SWT.WRAP); fileLabel.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.CENTER).span(3, SWT.DEFAULT).create()); model.getFileLabel().addListener(new UIValueListener<String>() { @Override protected void uiGotValue(LiveExpression<String> exp, String value) { if (!fileLabel.isDisposed()) { fileLabel.setText(exp.getValue()); } } }); DefaultMarkerAnnotationAccess fileMarkerAnnotationAccess = new DefaultMarkerAnnotationAccess(); OverviewRuler fileOverviewRuler = new OverviewRuler(fileMarkerAnnotationAccess, 10, colorsCache); String appName = model.getDeployedAppName(); IVerticalRuler fileVerticalRuler = appName == null ? new CompositeRuler() : /*new VerticalRuler(16, fileMarkerAnnotationAccess)*/ null; fileYamlViewer = new SourceViewer(fileYamlComposite, fileVerticalRuler, fileOverviewRuler, true, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION); ManifestYamlSourceViewerConfiguration fileYamlSourceViewerConfiguration = new ManifestYamlSourceViewerConfiguration(ShellProviders.from(composite)) { @Override protected IReconcilingStrategy createReconcilerStrategy(ISourceViewer viewer) { CompositeReconcilingStrategy strategy = new CompositeReconcilingStrategy(); strategy.setReconcilingStrategies(new IReconcilingStrategy[] { super.createReconcilerStrategy(viewer), new AppNameReconcilingStrategy(viewer, getAstProvider(), appName) }); return strategy; } @Override protected ForceableReconciler createReconciler(ISourceViewer sourceViewer) { IReconcilingStrategy strategy = createReconcilerStrategy(sourceViewer); if (strategy!=null) { InstantForceableReconciler reconciler = new InstantForceableReconciler(strategy); reconciler.setDelay(500); return reconciler; } return null; } }; fileYamlViewer.configure(fileYamlSourceViewerConfiguration); fileYamlViewer.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 200).create()); fileYamlDecorationSupport = new SourceViewerDecorationSupport(fileYamlViewer, fileOverviewRuler, fileMarkerAnnotationAccess, colorsCache); fileYamlAppNameAnnotationSupport = new AppNameAnnotationSupport(fileYamlViewer, fileMarkerAnnotationAccess, colorsCache); model.getFileDocument().addListener(new UIValueListener<IDocument>() { protected void uiGotValue(LiveExpression<IDocument> exp, IDocument value) { updateManifestFile(); } }); manualYamlComposite = new Composite(yamlGroup, SWT.NONE); manualYamlComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); layout = new GridLayout(); layout.marginWidth = 0; manualYamlComposite.setLayout(layout); Label manualYamlDescriptionLabel = new Label(manualYamlComposite, SWT.WRAP); manualYamlDescriptionLabel.setText(model.isManualManifestReadOnly() ? "Preview of the contents of the auto-generated deployment manifest:" : "Edit deployment manifest contents:"); manualYamlDescriptionLabel.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create()); DefaultMarkerAnnotationAccess manualMarkerAnnotationAccess = new DefaultMarkerAnnotationAccess(); OverviewRuler manualOverviewRuler = new OverviewRuler(manualMarkerAnnotationAccess, 10, colorsCache); IVerticalRuler manualVerticalRuler = appName == null ? new CompositeRuler() : /*new VerticalRuler(16, manualMarkerAnnotationAccess)*/ null; manualYamlViewer = new SourceViewer(manualYamlComposite, manualVerticalRuler, manualOverviewRuler, true, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION); ManifestYamlSourceViewerConfiguration manualSourceViewerConfiguration = new ManifestYamlSourceViewerConfiguration(ShellProviders.from(composite)) { @Override protected IReconcilingStrategy createReconcilerStrategy(ISourceViewer viewer) { CompositeReconcilingStrategy strategy = new CompositeReconcilingStrategy(); strategy.setReconcilingStrategies(new IReconcilingStrategy[] { super.createReconcilerStrategy(viewer), new AppNameReconcilingStrategy(viewer, getAstProvider(), appName) }); return strategy; } }; manualYamlViewer.configure(manualSourceViewerConfiguration); manualYamlViewer.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 200).create()); if (model.isManualManifestReadOnly()) { manualYamlViewer.setEditable(false); manualYamlViewer.getTextWidget().setCaret(null); manualYamlViewer.getTextWidget().setCursor(getShell().getDisplay().getSystemCursor(SWT.CURSOR_ARROW)); } manualYamlDecorationSupport = new SourceViewerDecorationSupport(manualYamlViewer, manualOverviewRuler, manualMarkerAnnotationAccess, colorsCache); manualYamlAppNameAnnotationSupport = new AppNameAnnotationSupport(manualYamlViewer, manualMarkerAnnotationAccess, colorsCache); manualYamlViewer.setDocument(model.getManualDocument(), model.getManualAnnotationModel()); /* * Set preferences for viewers decoration support */ for (AnnotationPreference preference : (List<AnnotationPreference>) new MarkerAnnotationPreferences().getAnnotationPreferences()) { manualYamlDecorationSupport.setAnnotationPreference(preference); fileYamlDecorationSupport.setAnnotationPreference(preference); } manualYamlDecorationSupport.install(EditorsUI.getPreferenceStore()); fileYamlDecorationSupport.install(EditorsUI.getPreferenceStore()); /* * Set the proper Font on the YAML viewers */ fileYamlViewer.getTextWidget().setFont(JFaceResources.getTextFont()); manualYamlViewer.getTextWidget().setFont(JFaceResources.getTextFont()); /* * Set App Name annotation model on the dialog's model */ model.setManualAppNameAnnotationModel(AppNameAnnotationSupport.getAppNameAnnotationModel(manualYamlViewer)); /* * Set YAML validation annotation model (resource marker annotation model) */ model.setManualResourceAnnotationModel(manualYamlViewer.getAnnotationModel()); } private void activateHanlders() { if (service != null) { for (EditorActionHandler handler : handlers) { activations.add(service.activateHandler(handler.getActionId(), handler)); } } } private void deactivateHandlers() { if (service != null) { for (IHandlerActivation activation : activations) { service.deactivateHandler(activation); } activations.clear(); } } private void refreshManifests() { IResource selectedResource = (IResource) ((IStructuredSelection) workspaceViewer.getSelection()).getFirstElement(); final IResource resourceToRefresh = selectedResource instanceof IFile ? selectedResource.getParent() : selectedResource; Job job = new Job("Refreshing resources for '" + resourceToRefresh.getName() + "'") { @Override protected IStatus run(IProgressMonitor monitor) { IStatus status = Status.OK_STATUS; try { resourceToRefresh.refreshLocal(IResource.DEPTH_INFINITE, monitor); } catch (CoreException e) { status = e.getStatus(); } getParentShell().getDisplay().asyncExec(new Runnable() { @Override public void run() { // Remove selection listener to set selection from current pathModel value workspaceViewer.removeSelectionChangedListener(selectionListener); workspaceViewer.refresh(resourceToRefresh); updateManifestFile(); // Add the selection listener back after the initial value has been set workspaceViewer.addSelectionChangedListener(selectionListener); } }); return status; } }; job.setRule(resourceToRefresh); job.schedule(); } private void updateManifestFile() { IDocument document = model.getFileDocument().getValue(); fileYamlViewer.getControl().setEnabled(document!=null); fileYamlViewer.setDocument(document, model.getFileAnnotationModel()); /* * Set New App Name annotation model on the dialog's model */ model.setFileAppNameAnnotationModel(AppNameAnnotationSupport.getAppNameAnnotationModel(fileYamlViewer)); /* * Set YAML validation annotation model (resource marker annotation model) */ model.setFileResourceAnnotationModel(fileYamlViewer.getAnnotationModel()); } @Override protected boolean isResizable() { return true; } @Override protected IDialogSettings getDialogBoundsSettings() { return DialogSettings.getOrCreateSection(BootDashActivator.getDefault().getDialogSettings(), "ManifestFileDialog"); } @Override public boolean close() { if (getReturnCode() == IDialogConstants.CANCEL_ID) { model.cancelPressed(); } else { if (!model.okPressed()) { return false; } } getDialogBoundsSettings().put(DIALOG_LIST_HEIGHT_SETTING, ((GridData)fileGroup.getLayoutData()).heightHint); boolean close = super.close(); dispose(); return close; } protected void dispose() { model.dispose(); /* * Deactivate handlers for key bindings */ deactivateHandlers(); /* * dispose SWT and JFace resources */ colorsCache.dispose(); if (manualYamlDecorationSupport != null) { manualYamlDecorationSupport.dispose(); } if (fileYamlDecorationSupport != null) { fileYamlDecorationSupport.dispose(); } if (manualYamlAppNameAnnotationSupport != null) { manualYamlAppNameAnnotationSupport.dispose(); } if (fileYamlAppNameAnnotationSupport != null) { fileYamlAppNameAnnotationSupport.dispose(); } } // TODO: this should be replaced with TreeViewer.getStructuredSelection once we drop support for Eclipse 4.4 // the TreeViewer.getStructuredSelection() API got introduced in Eclipse 4.5 private ITreeSelection getStructuredSelection(TreeViewer treeViewer) { ISelection selection = treeViewer.getSelection(); if (selection instanceof ITreeSelection) { return (ITreeSelection) selection; } throw new ClassCastException("AbstractTreeViewer should return an instance of ITreeSelection from its getSelection() method."); //$NON-NLS-1$ } @Override protected int getDialogBoundsStrategy() { return DIALOG_PERSISTSIZE; } @Override protected Point getInitialSize() { Point size = super.getInitialSize(); /* * If manual mode is selected fileGroup is missing and not accounted in * the size of the dialog shell. Add its height here manually if dialog * size was not persisted previously */ GridData fileGroupLayoutData = (GridData)fileGroup.getLayoutData(); if (fileGroupLayoutData.exclude) { try { /* * Hack: check if dialog width/height was persisted. If * persisted then no need to calculate dialog size */ getDialogBoundsSettings().getInt("DIALOG_WIDTH"); } catch (NumberFormatException e) { /* * Exception is thrown if dialog width/height cannot be read * from storage */ size.y += fileGroupLayoutData.heightHint; } } return size; } private class EditorActionHandler extends AbstractHandler { private String actionId; private int operationId; public EditorActionHandler(String actionId, int operationId) { super(); this.actionId = actionId; this.operationId = operationId; } public String getActionId() { return actionId; } @Override public Object execute(ExecutionEvent arg0) throws ExecutionException { if (manualYamlViewer.isEditable() && manualYamlViewer.getControl().isVisible() && manualYamlViewer.getTextWidget().isFocusControl()) { manualYamlViewer.doOperation(operationId); } else if (fileYamlViewer.isEditable() && fileYamlViewer.getControl().isVisible() && fileYamlViewer.getTextWidget().isFocusControl()) { fileYamlViewer.doOperation(operationId); } return null; } } public static IFile findManifestYamlFile(IProject project) { if (project == null) { return null; } IFile file = project.getFile("manifest.yml"); if (file.exists()) { return file; } IFile yamlFile = null; try { for (IResource r : project.members()) { if (r instanceof IFile) { file = (IFile) r; if (MANIFEST_YAML_FILE_FILTER.select(null, project, file)) { return file; } else if (YAML_FILE_FILTER.select(null, project, file)) { yamlFile = file; } } } } catch (CoreException e) { // ignore } return yamlFile; } }