/** * Copyright (c) 2011 Cloudsmith Inc. and other contributors, as listed below. * 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: * Cloudsmith * */ package org.cloudsmith.geppetto.ui.editor; import static org.cloudsmith.geppetto.forge.Forge.METADATA_JSON_NAME; import static org.cloudsmith.geppetto.forge.Forge.MODULEFILE_NAME; import java.util.Iterator; import org.cloudsmith.geppetto.diagnostic.Diagnostic; import org.cloudsmith.geppetto.forge.Forge; import org.cloudsmith.geppetto.pp.dsl.ui.PPUiConstants; import org.cloudsmith.geppetto.ui.UIPlugin; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IShowEditorInput; import org.eclipse.ui.PartInitException; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.ide.IGotoMarker; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.MarkerAnnotation; import com.google.inject.Inject; public class ModuleMetadataEditor extends FormEditor implements IGotoMarker, IShowEditorInput, IResourceChangeListener { static class DiagnosticAnnotation extends Annotation { private static final String INFO_TYPE = "org.eclipse.ui.workbench.texteditor.info"; //$NON-NLS-1$ private static final String ERROR_TYPE = "org.eclipse.ui.workbench.texteditor.error"; //$NON-NLS-1$ private static final String WARNING_TYPE = "org.eclipse.ui.workbench.texteditor.warning"; //$NON-NLS-1$ static void add(Diagnostic diag, IAnnotationModel model, IDocument text) { IRegion region; Position position; try { int line = 0; if(diag.getLineNumber() > 0) line = diag.getLineNumber() - 1; region = text.getLineInformation(line); position = new Position(region.getOffset(), region.getLength()); } catch(BadLocationException e) { position = new Position(0, text.getLength()); } model.addAnnotation(new DiagnosticAnnotation(diag), position); } public static void clearBuilderAnnotations(IAnnotationModel model) { Iterator<?> iter = model.getAnnotationIterator(); while(iter.hasNext()) { Object a = iter.next(); if(a instanceof MarkerAnnotation) { try { // Remove this marker from the transient document model. It stems from the persisted content // and may since have been corrected. If not, it will be added as a DiagnosticAnnotation if(((MarkerAnnotation) a).getMarker().getType().equals( PPUiConstants.PUPPET_MODULE_PROBLEM_MARKER_TYPE)) model.removeAnnotation((Annotation) a); } catch(CoreException e) { } } } } public static void clearDiagnosticAnnotations(IAnnotationModel model) { Iterator<?> iter = model.getAnnotationIterator(); while(iter.hasNext()) { Object a = iter.next(); if(a instanceof DiagnosticAnnotation) model.removeAnnotation((Annotation) a); } } static String getAnnotationType(Diagnostic diag) { switch(diag.getSeverity()) { case Diagnostic.FATAL: case Diagnostic.ERROR: return ERROR_TYPE; case Diagnostic.WARNING: return WARNING_TYPE; } return INFO_TYPE; } DiagnosticAnnotation(Diagnostic diag) { super(getAnnotationType(diag), false, diag.getMessage()); } } @Inject private Forge forge; private ModuleOverviewPage overviewPage; private ModuleDependenciesPage dependenciesPage; private ModuleSourcePage sourcePage; private TextEditor derivedJSON; private boolean stale = false; private final MetadataModel model = new MetadataModel(); @Override protected void addPages() { try { overviewPage = new ModuleOverviewPage(this, "overview", UIPlugin.getLocalString("_UI_Overview_title")); //$NON-NLS-1$ //$NON-NLS-2$ addPage(overviewPage); dependenciesPage = new ModuleDependenciesPage( this, "dependencies", UIPlugin.getLocalString("_UI_Dependencies_title")); //$NON-NLS-1$ //$NON-NLS-2$ addPage(dependenciesPage); sourcePage = new ModuleSourcePage(this); int sourcePageIdx = addPage(sourcePage, getEditorInput()); setPageText(sourcePageIdx, UIPlugin.getLocalString("_UI_Source_title")); refreshModel(); sourcePage.initialize(); String name = getModuleName(); if(name != null) setPartName(name); IFile file = getFile(); if(file == null) { setPageText(sourcePageIdx, UIPlugin.getLocalString("_UI_Source_title")); } else if(MODULEFILE_NAME.equals(file.getName())) { setPageText(sourcePageIdx, UIPlugin.getLocalString("_UI_Source_title")); IFile metadataJSON = file.getParent().getFile(Path.fromPortableString(METADATA_JSON_NAME)); if(metadataJSON.exists() && metadataJSON.isDerived()) { FileEditorInput metadataInput = new FileEditorInput(metadataJSON); derivedJSON = new TextEditor() { @Override public boolean isEditable() { return false; } }; setPageText( addPage(derivedJSON, metadataInput), UIPlugin.getLocalString("_UI_JSON_Derived_title")); } } else setPageText(sourcePageIdx, UIPlugin.getLocalString("_UI_JSON_title")); } catch(Exception e) { UIPlugin.getInstance().log(e); } } @Override public void dispose() { ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); super.dispose(); } @Override public void doSave(IProgressMonitor monitor) { sourcePage.doSave(monitor); String name = getModuleName(); if(name != null) setPartName(name); } @Override public void doSaveAs() { // do nothing } ModuleDependenciesPage getDependenciesPage() { return dependenciesPage; } IDocument getDocument() { if(sourcePage != null) { IDocumentProvider dp = sourcePage.getDocumentProvider(); if(dp != null) return dp.getDocument(getEditorInput()); } return null; } IFile getFile() { IEditorInput input = getEditorInput(); return input == null ? null : (IFile) input.getAdapter(IFile.class); } Forge getForge() { return forge; } MetadataModel getModel() { if(stale) refreshModel(); return model; } String getModuleName() { return getModel().getModuleName(); } ModuleOverviewPage getOverviewPage() { return overviewPage; } IPath getPath() { IFile currentFile = getFile(); return currentFile == null ? null : currentFile.getLocation(); } ModuleSourcePage getSourcePage() { return sourcePage; } @Override public void gotoMarker(IMarker marker) { int line = marker.getAttribute(IMarker.LINE_NUMBER, 0); if(line > 0) { ((IGotoMarker) sourcePage.getAdapter(IGotoMarker.class)).gotoMarker(marker); setActiveEditor(sourcePage); } } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { if(input instanceof IFileEditorInput) { IFile file = ((IFileEditorInput) input).getFile(); if(file.isDerived() && METADATA_JSON_NAME.equals(file.getName())) { // We prefer to open the Modulefile editor here. It will show the // derived JSON content in a read-only tab IFile moduleFile = file.getParent().getFile(Path.fromPortableString(MODULEFILE_NAME)); if(moduleFile.exists()) input = new FileEditorInput(moduleFile); } // We want to listen to build events since they might change our conditions (dependencies // might come and go for instance). file.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_BUILD); } super.init(site, input); } @Override public boolean isSaveAsAllowed() { return false; } void markStale() { stale = true; overviewPage.markStale(); dependenciesPage.markStale(); } private void refreshModel() { Diagnostic chain = new Diagnostic(); IPath path = getPath(); if(path == null) return; model.setDocument(getDocument(), path, chain); sourcePage.updateDiagnosticAnnotations(chain); stale = false; } @Override public void resourceChanged(IResourceChangeEvent event) { if(event.getType() == IResourceChangeEvent.POST_BUILD) { // Ensure that our markers is as aligned as they can possibly be // with the current state of the workspace IFile file = getFile(); if(file != null && file.getProject() == event.getSource()) { markStale(); refreshModel(); } } } @Override public void showEditorInput(IEditorInput input) { if(input instanceof IFileEditorInput) { IFile file = ((IFileEditorInput) input).getFile(); if(file.isDerived()) { if(derivedJSON != null && METADATA_JSON_NAME.equals(file.getName())) setActiveEditor(derivedJSON); } else if(derivedJSON == getActiveEditor()) setActivePage(overviewPage.getIndex()); } } }