Java tutorial
/****************************************************************************** * Copyright (c) 2007 g-Eclipse consortium * 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 * * Initial development of the original code was made for * project g-Eclipse founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributor(s): * UCY (http://www.ucy.cs.ac.cy) * - Nicholas Loulloudes (loulloudes.n@cs.ucy.ac.cy) * *****************************************************************************/ package eu.geclipse.batch.ui.editors; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EventObject; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; 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.NullProgressMonitor; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.CommandStackListener; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterFactoryImpl; import org.eclipse.emf.common.ui.MarkerHelper; import org.eclipse.emf.common.ui.editor.ProblemEditorPart; import org.eclipse.emf.common.util.BasicDiagnostic; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EContentAdapter; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.edit.domain.IEditingDomainProvider; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; import org.eclipse.emf.edit.ui.util.EditUIMarkerHelper; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.PartInitException; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.dialogs.SaveAsDialog; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.part.MultiPageEditorSite; import org.eclipse.wst.sse.ui.StructuredTextEditor; import eu.geclipse.batch.model.qdl.QueueType; import eu.geclipse.batch.model.qdl.util.QdlAdapterFactory; import eu.geclipse.batch.ui.internal.Activator; import eu.geclipse.batch.ui.internal.Messages; import eu.geclipse.batch.ui.internal.pages.AdvancedQueueConfigPage; import eu.geclipse.batch.ui.internal.pages.SimpleQueueConfigPage; /** * The QueueEditor class is responsible for instantiating a GUI multi-page editor for * editing files with the *.qdl extension. Such files have an XML content that describe the * configuration of Grid Batch Queues. * * The QueueEditor contains three pages: * 1) a page for manipulating simple Batch Queue configuration settings, * 2) a page for manipulating advanced Batch Queue configuration settings, * 3) a raw source editor for editing the raw XML content of the *.qdl file. * */ public class QueueEditor extends FormEditor implements IEditingDomainProvider { protected Map<Resource, Diagnostic> resourceToDiagnosticMap = new LinkedHashMap<Resource, Diagnostic>(); protected Collection<Resource> savedResources = new ArrayList<Resource>(); protected Collection<Resource> removedResources = new ArrayList<Resource>(); protected Collection<Resource> changedResources = new ArrayList<Resource>(); protected ISelection editorSelection = StructuredSelection.EMPTY; protected MarkerHelper markerHelper = new EditUIMarkerHelper(); protected AdapterFactoryEditingDomain editingDomain; protected boolean updateProblemIndication = true; protected ComposedAdapterFactory adapterFactory; protected QueueType queue = null; protected IResourceChangeListener resourceChangeListener = new IResourceChangeListener() { public void resourceChanged(final IResourceChangeEvent event) { // Only listening to these. // if (event.getType() == IResourceDelta.POST_CHANGE) { IResourceDelta delta = event.getDelta(); try { class ResourceDeltaVisitor implements IResourceDeltaVisitor { protected ResourceSet resourceSet = QueueEditor.this.editingDomain.getResourceSet(); protected Collection<Resource> changedRes = new ArrayList<Resource>(); protected Collection<Resource> removedRes = new ArrayList<Resource>(); public boolean visit(final IResourceDelta deltaIn) { if (deltaIn.getFlags() != IResourceDelta.MARKERS && deltaIn.getResource().getType() == IResource.FILE) { if ((deltaIn.getKind() & (IResourceDelta.CHANGED | IResourceDelta.REMOVED)) != 0) { Resource resource = this.resourceSet .getResource(URI.createURI(deltaIn.getFullPath().toString()), false); if (resource != null) { if ((deltaIn.getKind() & IResourceDelta.REMOVED) != 0) { this.removedRes.add(resource); } else if (!QueueEditor.this.savedResources.remove(resource)) { this.changedRes.add(resource); } } } } return true; } public Collection<Resource> getChangedResources() { return this.changedRes; } public Collection<Resource> getRemovedResources() { return this.removedRes; } } ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(); delta.accept(visitor); if (!visitor.getRemovedResources().isEmpty()) { QueueEditor.this.removedResources.addAll(visitor.getRemovedResources()); if (!isDirty()) { getSite().getShell().getDisplay().asyncExec(new Runnable() { public void run() { getSite().getPage().closeEditor(QueueEditor.this, false); } }); } } if (!visitor.getChangedResources().isEmpty()) { QueueEditor.this.changedResources.addAll(visitor.getChangedResources()); if (getSite().getPage().getActiveEditor() == QueueEditor.this) { getSite().getShell().getDisplay().asyncExec(new Runnable() { public void run() { handleActivate(); } }); } } } catch (CoreException exception) { Activator.logException(exception); } } } }; protected EContentAdapter problemIndicationAdapter = new EContentAdapter() { @Override public void notifyChanged(final Notification notification) { if (notification.getNotifier() instanceof Resource) { switch (notification.getFeatureID(Resource.class)) { case Resource.RESOURCE__IS_LOADED: case Resource.RESOURCE__ERRORS: case Resource.RESOURCE__WARNINGS: { Resource resource = (Resource) notification.getNotifier(); Diagnostic diagnostic = analyzeResourceProblems((Resource) notification.getNotifier(), null); if (diagnostic.getSeverity() != Diagnostic.OK) { QueueEditor.this.resourceToDiagnosticMap.put(resource, diagnostic); } else { QueueEditor.this.resourceToDiagnosticMap.remove(resource); } if (QueueEditor.this.updateProblemIndication) { getSite().getShell().getDisplay().asyncExec(new Runnable() { public void run() { updateProblemIndication(); } }); } } } } else { super.notifyChanged(notification); } } @Override protected void setTarget(final Resource target) { basicSetTarget(target); } @Override protected void unsetTarget(final Resource targetIn) { basicUnsetTarget(targetIn); } }; private StructuredTextEditor editor = null; private int sourcePageIndex; private boolean refreshedModel = false; private boolean isDirtyFlag = false; private SimpleQueueConfigPage simpleQueueConfigPage = new SimpleQueueConfigPage(this); private AdvancedQueueConfigPage advancedQueueConfigPage = new AdvancedQueueConfigPage(this); /** * QueueEditor Default Constructor */ public QueueEditor() { List<AdapterFactoryImpl> factories = new ArrayList<AdapterFactoryImpl>(); factories.add(new ResourceItemProviderAdapterFactory()); factories.add(new QdlAdapterFactory()); factories.add(new ReflectiveItemProviderAdapterFactory()); this.adapterFactory = new ComposedAdapterFactory(factories); /* * Create the command stack that will notify this editor as commands * are executed. */ BasicCommandStack commandStack = new BasicCommandStack(); /* * Add a listener to set the most recent command's affected objects to be * the selection of the viewer with focus. * */ commandStack.addCommandStackListener(new CommandStackListener() { @SuppressWarnings("synthetic-access") public void commandStackChanged(final EventObject event) { Composite container2 = getContainer(); container2.getDisplay().asyncExec(new Runnable() { public void run() { firePropertyChange(IEditorPart.PROP_DIRTY); } }); } }); /* * Create the editing domain with a special command stack. */ this.editingDomain = new AdapterFactoryEditingDomain(this.adapterFactory, commandStack, new HashMap<Resource, Boolean>()); } protected void cleanDirtyState() { this.simpleQueueConfigPage.setDirty(false); this.advancedQueueConfigPage.setDirty(false); } /** * This method set's the dirty status of the editor. * * @param dirtyFlag * If TRUE then the page is Dirty and a Save operation is needed. * */ public void setDirty(final boolean dirtyFlag) { if (this.isDirtyFlag != dirtyFlag) { this.isDirtyFlag = dirtyFlag; this.editorDirtyStateChanged(); } } // End void setDirty() /* (non-Javadoc) * @see org.eclipse.ui.forms.editor.FormEditor#addPages() */ @Override protected void addPages() { getQdlModel(); try { addPage(this.simpleQueueConfigPage); addPage(this.advancedQueueConfigPage); addResourceEditorPage(); pushContentToPages(); } catch (PartInitException e) { Activator.logException(e); } } private void pushContentToPages() { this.simpleQueueConfigPage.setPageContent(this.queue, isModelRefreshed()); this.advancedQueueConfigPage.setPageContent(this.queue, isModelRefreshed()); } protected void refreshEditor() { this.refreshedModel = true; pushContentToPages(); this.refreshedModel = false; } protected void doTextEditorSave() { this.editor.doSave(null); } /* (non-Javadoc) * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor) */ @Override public void doSave(final IProgressMonitor monitor) { /* Do the work within an operation because this is a long running activity * that modifies the workbench. */ WorkspaceModifyOperation operation = new WorkspaceModifyOperation() { // This is the method that gets invoked when the operation runs. @Override public void execute(final IProgressMonitor monitorIn) { // Save the resources to the file system. // boolean first = true; for (Iterator<?> i = QueueEditor.this.editingDomain.getResourceSet().getResources().iterator(); i .hasNext();) { Resource resource = (Resource) i.next(); if ((first || !resource.getContents().isEmpty() || isPersisted(resource)) && !QueueEditor.this.editingDomain.isReadOnly(resource)) { try { QueueEditor.this.savedResources.add(resource); resource.save(Collections.EMPTY_MAP); } catch (Exception exception) { QueueEditor.this.resourceToDiagnosticMap.put(resource, analyzeResourceProblems(resource, exception)); //setDirty( false ); doTextEditorSave(); cleanDirtyState(); refreshEditor(); } first = false; } } } }; this.updateProblemIndication = false; try { // This runs the options, and shows progress. new ProgressMonitorDialog(getSite().getShell()).run(true, false, operation); // Refresh the necessary state. ((BasicCommandStack) this.editingDomain.getCommandStack()).saveIsDone(); //setDirty( false ); doTextEditorSave(); cleanDirtyState(); refreshEditor(); } catch (Exception exception) { // Something went wrong that shouldn't. Activator.logException(exception); } this.updateProblemIndication = true; updateProblemIndication(); } @Override public void dispose() { this.updateProblemIndication = false; ResourcesPlugin.getWorkspace().removeResourceChangeListener(this.resourceChangeListener); this.adapterFactory.dispose(); super.dispose(); if (this.queue != null) { this.queue.eResource().unload(); this.queue = null; } } /* (non-Javadoc) * @see org.eclipse.ui.part.EditorPart#doSaveAs() */ @Override public void doSaveAs() { SaveAsDialog saveAsDialog = new SaveAsDialog(getSite().getShell()); saveAsDialog.open(); IPath path = saveAsDialog.getResult(); if (path != null) { IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); if (file != null) { doSaveAs(URI.createPlatformResourceURI(file.getFullPath().toString(), false), new FileEditorInput(file)); } } } /* Save the QDL file (used as the editor input) as a different name. */ protected void doSaveAs(final URI uri, final IEditorInput editorInput) { this.editingDomain.getResourceSet().getResources().get(0).setURI(uri); setInputWithNotify(editorInput); setPartName(editorInput.getName()); IProgressMonitor progressMonitor = new NullProgressMonitor(); doSave(progressMonitor); } /* (non-Javadoc) * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed() */ @Override public boolean isSaveAsAllowed() { return true; } public EditingDomain getEditingDomain() { return this.editingDomain; } /** * @return true if the the QDL Model was refreshed / changed. * This could be caused by an external editor. */ public boolean isModelRefreshed() { return this.refreshedModel; } /* * This method adds the XML Resource Editor Page to the Queue editor */ private void addResourceEditorPage() throws PartInitException { this.sourcePageIndex = addPage(getSourceEditor(), getEditorInput()); setPageText(this.sourcePageIndex, getEditorInput().getName()); getSourceEditor().setInput(getEditorInput()); } /* * This method returns a Text Editor for addResourceEditorPage method. */ private StructuredTextEditor getSourceEditor() { if (this.editor == null) { this.editor = new StructuredTextEditor(); this.editor.setEditorPart(this); } return this.editor; } /** * @see org.eclipse.ui.part.MultiPageEditorPart#createSite(org.eclipse.ui.IEditorPart) */ @Override protected IEditorSite createSite(final IEditorPart page) { IEditorSite site = null; if (page == this.editor) { site = new MultiPageEditorSite(this, page) { @Override public String getId() { // Sets this ID so nested editor is configured for XML source return "org.eclipse.core.runtime.xml" + ".source"; //$NON-NLS-1$ //$NON-NLS-2$ } }; } else { site = super.createSite(page); } return site; } @Override public void init(final IEditorSite site, final IEditorInput editorInput) { setSite(site); setInputWithNotify(editorInput); setPartName(editorInput.getName()); ResourcesPlugin.getWorkspace().addResourceChangeListener(this.resourceChangeListener, IResourceChangeEvent.POST_CHANGE); } protected void handleActivate() { // Recompute the read only state. if (this.editingDomain.getResourceToReadOnlyMap() != null) { this.editingDomain.getResourceToReadOnlyMap().clear(); // Refresh any actions that may become enabled or disabled. setSelection(getSelection()); } if (!this.removedResources.isEmpty()) { if (handleDirtyConflict()) { getSite().getPage().closeEditor(QueueEditor.this, false); QueueEditor.this.dispose(); } else { this.removedResources.clear(); this.changedResources.clear(); this.savedResources.clear(); } } else if (!this.changedResources.isEmpty()) { this.changedResources.removeAll(this.savedResources); handleChangedResources(); this.changedResources.clear(); this.savedResources.clear(); } } protected void handleChangedResources() { if (!this.changedResources.isEmpty() && (!isDirty() || handleDirtyConflict())) { this.editingDomain.getCommandStack().flush(); this.updateProblemIndication = false; for (Iterator<Resource> i = this.changedResources.iterator(); i.hasNext();) { Resource resource = i.next(); if (resource.isLoaded()) { resource.unload(); try { resource.load(Collections.EMPTY_MAP); } catch (IOException exception) { if (!this.resourceToDiagnosticMap.containsKey(resource)) { this.resourceToDiagnosticMap.put(resource, analyzeResourceProblems(resource, exception)); } } } } this.updateProblemIndication = true; updateProblemIndication(); getQdlModel(); } } protected boolean handleDirtyConflict() { return MessageDialog.openQuestion(getSite().getShell(), "QueueEditor.FileConflict.label", //$NON-NLS-1$ "Queue.WARN.FileConflict"); //$NON-NLS-1$ } /** * @return editorSelection */ public ISelection getSelection() { return this.editorSelection; } /** * @param selection */ public void setSelection(final ISelection selection) { this.editorSelection = selection; } /* Method triggered when there are changes between the form pages.*/ @Override protected void pageChange(final int pageIndex) { super.pageChange(pageIndex); } /** * Responsible for de-serializing the model from the resource file. * The resource is passed to the getResourceRoot method. */ public void getQdlModel() { // Assumes that the input is a file object. // IFileEditorInput modelFile = (IFileEditorInput) getEditorInput(); URI resourceURI = URI.createPlatformResourceURI(modelFile.getFile().getFullPath().toString(), false); Exception exception = null; Resource resource = null; try { // Load the resource through the editing domain. // resource = this.editingDomain.getResourceSet().getResource(resourceURI, true); } catch (Exception e) { exception = e; resource = this.editingDomain.getResourceSet().getResource(resourceURI, false); } Diagnostic diagnostic = analyzeResourceProblems(resource, exception); if (diagnostic.getSeverity() != Diagnostic.OK) { this.resourceToDiagnosticMap.put(resource, analyzeResourceProblems(resource, exception)); } this.editingDomain.getResourceSet().eAdapters().add(this.problemIndicationAdapter); getResourceRoot(resource); // This means the file was edited from an external editor so // push the new QDL model to the pages. if (!this.changedResources.isEmpty()) { refreshEditor(); } } /* This method parses the resource in order to find which QDL types appear. Each QDL type is then passed as a reference parameter (EList) in the appropriate page of the Queue editor. */ private void getResourceRoot(final Resource resource) { // Get an iterator to iterate through all contents of the resource. TreeIterator<EObject> iterator = resource.getAllContents(); while (iterator.hasNext()) { EObject testElement = iterator.next(); /* Instace-of checks for each EObject that appears in the resource. * We want to get the JobDefinition EObject which is the root Element of * a QDL Document. */ if (testElement instanceof QueueType) { this.queue = (QueueType) testElement; } } } /** * This looks up a string in plug-in.properties, making a substitution. * Returns a diagnostic describing the errors and warnings listed in * the resource and the specified exception * @param resource * @param exception * @return Diagnostic */ public Diagnostic analyzeResourceProblems(final Resource resource, final Exception exception) { Diagnostic basicDiagnostic = null; if (!resource.getErrors().isEmpty() || !resource.getWarnings().isEmpty()) { basicDiagnostic = new BasicDiagnostic(Diagnostic.ERROR, Activator.PLUGIN_ID, 0, Messages.getString("QueueEditor.CreateModelErrorMessage"), //$NON-NLS-1$ new Object[] { exception == null ? (Object) resource : exception }); ((BasicDiagnostic) basicDiagnostic).merge(EcoreUtil.computeDiagnostic(resource, true)); } else if (exception != null) { basicDiagnostic = new BasicDiagnostic(Diagnostic.ERROR, Activator.PLUGIN_ID, 0, Messages.getString("QueueEditor.CreateModelErrorMessage"), //$NON-NLS-1$ new Object[] { exception }); } else { basicDiagnostic = Diagnostic.OK_INSTANCE; } return basicDiagnostic; } protected void updateProblemIndication() { if (this.updateProblemIndication) { BasicDiagnostic diagnostic = new BasicDiagnostic(Diagnostic.OK, Activator.PLUGIN_ID, 0, null, new Object[] { this.editingDomain.getResourceSet() }); for (Iterator<Diagnostic> i = this.resourceToDiagnosticMap.values().iterator(); i.hasNext();) { Diagnostic childDiagnostic = i.next(); if (childDiagnostic.getSeverity() != Diagnostic.OK) { diagnostic.add(childDiagnostic); } } int lastEditorPage = getPageCount() - 1; if (lastEditorPage >= 0 && getEditor(lastEditorPage) instanceof ProblemEditorPart) { ((ProblemEditorPart) getEditor(lastEditorPage)).setDiagnostic(diagnostic); if (diagnostic.getSeverity() != Diagnostic.OK) { setActivePage(lastEditorPage); } } else if (diagnostic.getSeverity() != Diagnostic.OK) { ProblemEditorPart problemEditorPart = new ProblemEditorPart(); problemEditorPart.setDiagnostic(diagnostic); problemEditorPart.setMarkerHelper(this.markerHelper); try { addPage(++lastEditorPage, problemEditorPart, getEditorInput()); setPageText(lastEditorPage, problemEditorPart.getPartName()); setActivePage(lastEditorPage); } catch (PartInitException exception) { Activator.logException(exception); } } if (this.markerHelper.hasMarkers(this.editingDomain.getResourceSet())) { this.markerHelper.deleteMarkers(this.editingDomain.getResourceSet()); if (diagnostic.getSeverity() != Diagnostic.OK) { try { this.markerHelper.createMarkers(diagnostic); } catch (CoreException exception) { Activator.logException(exception); } } } } } protected boolean isPersisted(final Resource resource) { boolean result = false; try { InputStream stream = this.editingDomain.getResourceSet().getURIConverter() .createInputStream(resource.getURI()); if (stream != null) { result = true; stream.close(); } } catch (IOException e) { Activator.logException(e); } return result; } } // End QueueEditor Class