Java tutorial
/******************************************************************************* * Copyright (c) 2010 Neil Bartlett. * 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: * Neil Bartlett - initial API and implementation *******************************************************************************/ package bndtools.editor; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import org.bndtools.api.ILogger; import org.bndtools.api.Logger; import org.bndtools.api.ResolveMode; import org.bndtools.core.jobs.JobUtil; import org.bndtools.core.resolve.ResolutionResult; import org.bndtools.core.resolve.ResolveJob; import org.bndtools.core.resolve.ui.ResolutionWizard; import org.bndtools.core.ui.ExtendedFormEditor; import org.bndtools.core.ui.IFormPageFactory; import org.bndtools.utils.swt.SWTConcurrencyUtil; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; 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.IStorage; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.window.Window; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.IFormPart; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.editor.IFormPage; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.handlers.IHandlerActivation; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.ide.ResourceUtil; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.IElementStateListener; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.osgi.util.promise.Deferred; import org.osgi.util.promise.Promise; import aQute.bnd.build.Project; import aQute.bnd.build.Run; import aQute.bnd.build.Workspace; import aQute.bnd.build.model.BndEditModel; import aQute.bnd.properties.BadLocationException; import aQute.lib.exceptions.Exceptions; import bndtools.BndConstants; import bndtools.Plugin; import bndtools.central.Central; import bndtools.editor.common.IPriority; import bndtools.editor.model.IDocumentWrapper; import bndtools.editor.pages.BundleContentPage; import bndtools.editor.pages.BundleDescriptionPage; import bndtools.editor.pages.ProjectBuildPage; import bndtools.editor.pages.ProjectRunPage; import bndtools.editor.pages.TestSuitesPage; import bndtools.editor.pages.WorkspacePage; import bndtools.launch.LaunchConstants; import bndtools.preferences.BndPreferences; import bndtools.types.Pair; public class BndEditor extends ExtendedFormEditor implements IResourceChangeListener { private static final ILogger logger = Logger.getLogger(BndEditor.class); public static final String SYNC_MESSAGE = "Workspace is loading, please wait..."; public static final String WORKSPACE_EDITOR = "bndtools.bndWorkspaceConfigEditor"; public static final String SOURCE_PAGE = "__source_page"; public static final String CONTENT_PAGE = "__content_page"; public static final String WORKSPACE_PAGE = "__workspace_page"; public static final String WORKSPACE_EXT_PAGE = "__workspace_ext_page"; public static final String DESCRIPTION_PAGE = "__description_page"; public static final String BUILD_PAGE = "__build_page"; public static final String PROJECT_RUN_PAGE = "__project_run_page"; public static final String BNDRUN_PAGE = "__bndrun_page"; public static final String TEST_SUITES_PAGE = "__test_suites_page"; private final BndEditModel model = new BndEditModel(); private final Map<String, IFormPageFactory> pageFactories = new LinkedHashMap<String, IFormPageFactory>(); private final Image buildFileImg = AbstractUIPlugin .imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/bndtools-logo-16x16.png").createImage(); private BndSourceEditorPage sourcePage; private Promise<Workspace> modelReady; private File inputFile; static Pair<String, String> getFileAndProject(IEditorInput input) { String path; String projectName; if (input instanceof IFileEditorInput) { IFile file = ((IFileEditorInput) input).getFile(); path = file.getProjectRelativePath().toString(); projectName = file.getProject().getName(); } else { path = input.getName(); projectName = null; } return Pair.newInstance(path, projectName); } private void updateIncludedPages() { List<String> requiredPageIds = new LinkedList<String>(); // Need to know the file and project names. Pair<String, String> fileAndProject = getFileAndProject(getEditorInput()); String path = fileAndProject.getFirst(); String projectName = fileAndProject.getSecond(); if (isMainWorkspaceConfig(path, projectName)) { requiredPageIds.add(WORKSPACE_PAGE); } else if (isExtWorkspaceConfig(path, projectName)) { requiredPageIds.add(WORKSPACE_EXT_PAGE); setTitleImage(buildFileImg); } else if (path.endsWith(LaunchConstants.EXT_BNDRUN)) { requiredPageIds.addAll(getPagesBndRun()); } else { requiredPageIds.addAll(getPagesBnd(path)); } requiredPageIds.add(SOURCE_PAGE); // Remove pages no longer required and remember the rest in a map int i = 0; Map<String, IFormPage> pageCache = new HashMap<String, IFormPage>(requiredPageIds.size()); while (i < getPageCount()) { IFormPage current = (IFormPage) pages.get(i); if (!requiredPageIds.contains(current.getId())) removePage(i); else { pageCache.put(current.getId(), current); i++; } } // Cache new pages for (String pageId : requiredPageIds) { if (!pageCache.containsKey(pageId)) { IFormPage page = SOURCE_PAGE.equals(pageId) ? sourcePage : pageFactories.get(pageId).createPage(this, model, pageId); pageCache.put(pageId, page); } } // Add pages back in int requiredPointer = 0; int existingPointer = 0; while (requiredPointer < requiredPageIds.size()) { try { String requiredId = requiredPageIds.get(requiredPointer); if (existingPointer >= getPageCount()) { if (SOURCE_PAGE.equals(requiredId)) addPage(sourcePage, getEditorInput()); else addPage(pageCache.get(requiredId)); } else { IFormPage existingPage = (IFormPage) pages.get(existingPointer); if (!requiredId.equals(existingPage.getId())) { if (SOURCE_PAGE.equals(requiredId)) addPage(existingPointer, sourcePage, getEditorInput()); else addPage(existingPointer, pageCache.get(requiredId)); } } existingPointer++; } catch (PartInitException e) { logger.logError("Error adding page(s) to the editor.", e); } requiredPointer++; } // Set the source page title setPageText(sourcePage.getIndex(), "Source"); } private static boolean isMainWorkspaceConfig(String path, String projectName) { if (Workspace.CNFDIR.equals(projectName) || Workspace.BNDDIR.equals(projectName)) { return Workspace.BUILDFILE.equals(path); } return false; } private static boolean isExtWorkspaceConfig(String path, String projectName) { if (Workspace.CNFDIR.equals(projectName) || Workspace.BNDDIR.equals(projectName)) { return path.startsWith("ext/") && path.endsWith(".bnd"); } return false; } private final AtomicBoolean saving = new AtomicBoolean(false); private IHandlerActivation resolveHandlerActivation; private JobChangeAdapter resolveJobListener; @Override public void doSave(IProgressMonitor monitor) { // Commit dirty pages if (sourcePage.isActive() && sourcePage.isDirty()) { sourcePage.commit(true); } else { commitPages(true); sourcePage.refresh(); } ResolveMode resolveMode = getResolveMode(); // If auto resolve, then resolve and save in background thread. if (resolveMode == ResolveMode.auto && !PlatformUI.getWorkbench().isClosing()) { resolveRunBundles(monitor, true); } else { // Not auto-resolving, just save reallySave(monitor); } } private void reallySave(IProgressMonitor monitor) { // Actually save, via the source editor try { boolean saveLocked = this.saving.compareAndSet(false, true); if (!saveLocked) { logger.logError("Tried to save while already saving", null); return; } sourcePage.doSave(monitor); loadEditModel(); updateIncludedPages(); } catch (Exception e) { ErrorDialog.openError(getEditorSite().getShell(), "Error", null, new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Failed to reload bnd edit model", e)); } finally { this.saving.set(false); } } public Promise<IStatus> resolveRunBundles(IProgressMonitor monitor, boolean onSave) { final Shell shell = getEditorSite().getShell(); final IFile file = ResourceUtil.getFile(getEditorInput()); if (file == null) { MessageDialog.openError(shell, "Resolution Error", "Unable to run resolution because the file is not in the workspace."); reallySave(monitor); return Central.promiseFactory().resolved(Status.CANCEL_STATUS); } Job loadWorkspaceJob = new Job("Loading workspace...") { @Override protected IStatus run(IProgressMonitor monitor) { if (monitor == null) monitor = new NullProgressMonitor(); monitor.beginTask("Loading workspace", 2); try { Central.getWorkspace(); monitor.worked(1); modelReady.getValue(); return Status.OK_STATUS; } catch (Exception e) { return new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Failed to initialize workspace.", e); } finally { monitor.done(); } } }; final UIJob runResolveInUIJob = new UIJob("Resolve") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { // Make sure all the parts of this editor page have committed their // dirty state to the model for (Object pageObj : pages) { if (pageObj instanceof IFormPage) { IFormPage formPage = (IFormPage) pageObj; IManagedForm form = formPage.getManagedForm(); if (form != null) { IFormPart[] formParts = form.getParts(); for (IFormPart formPart : formParts) { if (formPart.isDirty()) formPart.commit(false); } } } } if (sourcePage.isActive() && sourcePage.isDirty()) { sourcePage.commit(false); } // Create resolver job and pre-validate final ResolveJob job = new ResolveJob(model); IStatus validation = job.validateBeforeRun(); if (!validation.isOK()) { if (onSave) reallySave(monitor); return validation; } // Add operation to perform at the end of resolution (i.e. display // results and actually save the file) final UIJob completionJob = new UIJob(shell.getDisplay(), "Display Resolution Results") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { ResolutionResult result = job.getResolutionResult(); ResolutionWizard wizard = new ResolutionWizard(model, file, result); if (onSave) { // We are in auto-resolve-on-save, only show the dialog if there is a problem wizard.setAllowFinishUnresolved(true); wizard.setPreserveRunBundlesUnresolved(true); if (result.getOutcome() != ResolutionResult.Outcome.Resolved) { WizardDialog dialog = new DuringSaveWizardDialog(shell, wizard); dialog.create(); dialog.setErrorMessage( "Resolve Failed! Saving now will not update the Run Bundles."); if (dialog.open() == Window.OK) reallySave(monitor); } else { wizard.performFinish(); reallySave(monitor); } } else { // This is an interactive resolve, always show the dialog boolean dirtyBeforeResolve = isDirty(); WizardDialog dialog = new WizardDialog(shell, wizard); if (dialog.open() == Window.OK && !dirtyBeforeResolve) { // save changes immediately if there were no unsaved changes before the resolve reallySave(monitor); } } return Status.OK_STATUS; } }; job.addJobChangeListener(new JobChangeAdapter() { @Override public void done(IJobChangeEvent event) { completionJob.schedule(); } }); // Start job job.setUser(true); job.schedule(); return Status.OK_STATUS; } }; runResolveInUIJob.setUser(true); return JobUtil.chainJobs(loadWorkspaceJob, runResolveInUIJob); } private ResolveMode getResolveMode() { ResolveMode resolveMode = ResolveMode.manual; try { String str = (String) model.genericGet(BndConstants.RESOLVE_MODE); if (str != null) resolveMode = Enum.valueOf(ResolveMode.class, str); } catch (Exception e) { logger.logError("Error parsing '-resolve' header.", e); } return resolveMode; } protected void ensurePageExists(String pageId, IFormPage page, int index) { IFormPage existingPage = findPage(pageId); if (existingPage != null) return; try { addPage(index, page); } catch (PartInitException e) { ErrorDialog.openError(getSite().getShell(), "Error", null, new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Error adding page to editor.", e)); } } protected void removePage(String pageId) { IFormPage page = findPage(pageId); if (page != null) { removePage(page.getIndex()); } } @Override public void doSaveAs() { } @Override public boolean isSaveAsAllowed() { return false; } @Override protected void handlePropertyChange(int propertyId) { super.handlePropertyChange(propertyId); } @Override protected void addPages() { updateIncludedPages(); showHighestPriorityPage(); if (!Central.isWorkspaceInited()) { IFormPage activePage = getActivePageInstance(); if (activePage != null && activePage.getManagedForm() != null) { ScrolledForm form = activePage.getManagedForm().getForm(); if (form.getMessage() == null) { form.setMessage(SYNC_MESSAGE, IMessageProvider.WARNING); } } } } void showHighestPriorityPage() { int selectedPrio = Integer.MIN_VALUE; String selected = null; BndPreferences prefs = new BndPreferences(); if (prefs.getEditorOpenSourceTab()) { selected = SOURCE_PAGE; selectedPrio = 0; } else { for (Object pageObj : pages) { IFormPage page = (IFormPage) pageObj; int priority = 0; if (page instanceof IPriority) priority = ((IPriority) page).getPriority(); if (priority > selectedPrio) { selected = page.getId(); selectedPrio = priority; } } } if (selected != null) setActivePage(selected); } private List<String> getPagesBnd(String fileName) { List<String> pages = new ArrayList<String>(5); boolean isProjectFile = Project.BNDFILE.equals(fileName); List<String> subBndFiles = model.getSubBndFiles(); boolean isSubBundles = subBndFiles != null && !subBndFiles.isEmpty(); for (Entry<String, IFormPageFactory> pageEntry : pageFactories.entrySet()) { String pageId = pageEntry.getKey(); IFormPageFactory page = pageEntry.getValue(); if (!isSubBundles && page.supportsMode(IFormPageFactory.Mode.bundle)) pages.add(pageId); else if (isProjectFile && page.supportsMode(IFormPageFactory.Mode.project)) pages.add(pageId); } return pages; } private List<String> getPagesBndRun() { List<String> pageIds = new ArrayList<String>(3); for (Entry<String, IFormPageFactory> pageEntry : pageFactories.entrySet()) { if (pageEntry.getValue().supportsMode(IFormPageFactory.Mode.bndrun)) pageIds.add(pageEntry.getKey()); } return pageIds; } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { super.init(site, input); try { // Work out our input file and subscribe to resource changes final String resourceName; IResource inputResource = ResourceUtil.getResource(input); if (inputResource != null) { inputResource.getWorkspace().addResourceChangeListener(this); resourceName = inputResource.getName(); inputFile = inputResource.getLocation().toFile(); } else { IStorage storage = (IStorage) input.getAdapter(IStorage.class); if (storage != null) { resourceName = storage.getName(); } else { resourceName = input.getName(); } inputFile = null; } model.setBndResourceName(resourceName); // Initialise pages and title initPages(site, input); setSourcePage(sourcePage); setPartNameForInput(input); IDocumentProvider docProvider = sourcePage.getDocumentProvider(); // #1625: Ensure the IDocumentProvider is not null. if (docProvider != null) { docProvider.addElementStateListener(new ElementStateListener()); if (!Central.hasWorkspaceDirectory()) { // default ws will be created we can load immediately modelReady = loadEditModel(); } else { // a real ws will be resolved so we need to load async modelReady = Central.onWorkspace(workspace -> loadEditModel()); } } else { modelReady = Central.promiseFactory().failed(new Exception("Model unavailable")); } setupActions(); } catch (Exception e1) { throw Exceptions.duck(e1); } } private void setupActions() { String fileName = getFileAndProject(getEditorInput()).getFirst(); if (fileName.endsWith(LaunchConstants.EXT_BNDRUN)) { final IHandlerService handlerSvc = (IHandlerService) getEditorSite().getService(IHandlerService.class); final AbstractHandler handler = new AbstractHandler() { @Override public Object execute(ExecutionEvent event) throws ExecutionException { resolveRunBundles(new NullProgressMonitor(), false); return null; } }; final IHandlerActivation activation = handlerSvc.activateHandler("bndtools.runEditor.resolve", handler); this.resolveJobListener = new JobChangeAdapter() { @Override public void running(IJobChangeEvent event) { if (event.getJob() instanceof ResolveJob) Display.getDefault().asyncExec(() -> handlerSvc.deactivateHandler(activation)); } @Override public void done(IJobChangeEvent event) { if (event.getJob() instanceof ResolveJob) Display.getDefault().asyncExec(() -> handlerSvc.activateHandler(activation)); } }; Job.getJobManager().addJobChangeListener(resolveJobListener); } } private Promise<Workspace> loadEditModel() throws Exception { // Create the bnd edit model and workspace Workspace ws = Central.getWorkspaceIfPresent(); Project bndProject = Run.createRun(ws, inputFile); model.setWorkspace(bndProject.getWorkspace()); model.setProject(bndProject); // Load content into the edit model Deferred<Workspace> completed = Central.promiseFactory().deferred(); Display.getDefault().asyncExec(() -> { final IDocumentProvider docProvider = sourcePage.getDocumentProvider(); // #1625: Ensure the IDocumentProvider is not null. if (docProvider != null) { try { IDocument document = docProvider.getDocument(getEditorInput()); model.loadFrom(new IDocumentWrapper(document)); model.setBndResource(inputFile); completed.resolve(ws); } catch (IOException e) { logger.logError("Unable to load edit model", e); completed.fail(e); } for (int i = 0; i < getPageCount(); i++) { Control control = getControl(i); if (control instanceof ScrolledForm) { ScrolledForm form = (ScrolledForm) control; if (SYNC_MESSAGE.equals(form.getMessage())) { form.setMessage(null, IMessageProvider.NONE); } } } } else { completed.fail(new Exception("Model unavailable")); } }); return completed.getPromise(); } private void initPages(IEditorSite site, IEditorInput input) throws PartInitException { // Initialise pages sourcePage = new BndSourceEditorPage(SOURCE_PAGE, this); pageFactories.put(WORKSPACE_PAGE, WorkspacePage.MAIN_FACTORY); pageFactories.put(WORKSPACE_EXT_PAGE, WorkspacePage.EXT_FACTORY); pageFactories.put(CONTENT_PAGE, BundleContentPage.FACTORY); pageFactories.put(DESCRIPTION_PAGE, BundleDescriptionPage.FACTORY); pageFactories.put(BUILD_PAGE, ProjectBuildPage.FACTORY); pageFactories.put(PROJECT_RUN_PAGE, ProjectRunPage.FACTORY_PROJECT); pageFactories.put(BNDRUN_PAGE, ProjectRunPage.FACTORY_BNDRUN); pageFactories.put(TEST_SUITES_PAGE, TestSuitesPage.FACTORY); IConfigurationElement[] configElems = Platform.getExtensionRegistry() .getConfigurationElementsFor(Plugin.PLUGIN_ID, "editorPages"); if (configElems != null) for (IConfigurationElement configElem : configElems) { String id = configElem.getAttribute("id"); if (id != null) { if (pageFactories.containsKey(id)) logger.logError("Duplicate form page ID: " + id, null); else pageFactories.put(id, new DelayedPageFactory(configElem)); } } sourcePage.init(site, input); sourcePage.initialize(this); } private void setPartNameForInput(IEditorInput input) { Pair<String, String> fileAndProject = getFileAndProject(input); String path = fileAndProject.getFirst(); String projectName = fileAndProject.getSecond(); String name = input.getName(); if (isMainWorkspaceConfig(path, projectName) || isExtWorkspaceConfig(path, projectName)) { name = path; } else if (Project.BNDFILE.equals(name)) { IResource resource = ResourceUtil.getResource(input); if (resource != null) name = projectName; } else if (name.endsWith(".bnd")) { IResource resource = ResourceUtil.getResource(input); if (resource != null) name = projectName + "." + name.substring(0, name.length() - ".bnd".length()); } else if (name.endsWith(".bndrun")) { name = name.substring(0, name.length() - ".bndrun".length()); } setPartName(name); } @Override public void dispose() { IResource resource = ResourceUtil.getResource(getEditorInput()); super.dispose(); if (resource != null) { resource.getWorkspace().removeResourceChangeListener(this); } buildFileImg.dispose(); if (resolveHandlerActivation != null) { resolveHandlerActivation.getHandlerService().deactivateHandler(resolveHandlerActivation); } if (resolveJobListener != null) { Job.getJobManager().removeJobChangeListener(resolveJobListener); } } public BndEditModel getEditModel() { return this.model; } @Override public void resourceChanged(IResourceChangeEvent event) { IResource myResource = ResourceUtil.getResource(getEditorInput()); IResourceDelta delta = event.getDelta(); if (delta == null) return; IPath fullPath = myResource.getFullPath(); delta = delta.findMember(fullPath); if (delta == null) return; // Delegate to any interested pages for (Object page : pages) { if (page instanceof IResourceChangeListener) { ((IResourceChangeListener) page).resourceChanged(event); } } // Close editor if file removed or switch to new location if file moved if (delta.getKind() == IResourceDelta.REMOVED) { if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) { IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getMovedToPath()); final FileEditorInput newInput = new FileEditorInput(file); setInput(newInput); Display display = getEditorSite().getShell().getDisplay(); if (display != null) { SWTConcurrencyUtil.execForDisplay(display, true, new Runnable() { @Override public void run() { setPartNameForInput(newInput); sourcePage.setInput(newInput); } }); } } else { close(false); } } // File content updated externally => reload all pages else if ((delta.getKind() & IResourceDelta.CHANGED) > 0 && (delta.getFlags() & IResourceDelta.CONTENT) > 0) { if (!saving.get()) { final IDocumentProvider docProvider = sourcePage.getDocumentProvider(); // #1625: Ensure the IDocumentProvider is not null. if (docProvider != null) { final IDocument document = docProvider.getDocument(getEditorInput()); SWTConcurrencyUtil.execForControl(getEditorSite().getShell(), true, new Runnable() { @Override public void run() { try { model.loadFrom(new IDocumentWrapper(document)); updateIncludedPages(); } catch (IOException e) { logger.logError("Failed to reload document", e); } } }); } } } } @Override public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { if (IContentOutlinePage.class == adapter) { return new BndEditorContentOutlinePage(this, model); } return super.getAdapter(adapter); } /** * Ensures the field values are updated if the file content is replaced */ private class ElementStateListener implements IElementStateListener { String savedString = null; @Override public void elementMoved(Object originalElement, Object movedElement) { } @Override public void elementDirtyStateChanged(Object element, boolean isDirty) { } @Override public void elementDeleted(Object element) { } @Override public void elementContentReplaced(Object element) { try { IDocumentProvider docProvider = sourcePage.getDocumentProvider(); // #1625: Ensure the IDocumentProvider is not null. if (docProvider != null) { IDocumentWrapper idoc = new IDocumentWrapper(docProvider.getDocument(element)); if (!saving.get()) { model.loadFrom(idoc); } else { if (savedString != null) { logger.logInfo("Putting back content that we almost lost!", null); try { idoc.replace(0, idoc.getLength(), savedString); } catch (BadLocationException e) { e.printStackTrace(); } } } } } catch (IOException e) { logger.logError("Error loading model from document.", e); } finally { savedString = null; } } @Override public void elementContentAboutToBeReplaced(Object element) { // [cs] This check is here to attempt to save content that would be thrown away by a (misbehaving?) version // control plugin. // Scenario: File is checked out by Perforce plugin. // This causes elementContentAboutToBeReplaced and elementContentReplaced callbacks to be fired. // However -- by the time that elementContentReplaced is called, the content inside of the IDocumentWrapper // is already replaced with the contents of the perforce file being checked out. // To avoid losing changes, we need to save the content here, then put that content BACK on to the document // in elementContentReplaced if (saving.get()) { logger.logInfo("Content about to be replaced... Save it.", null); IDocumentProvider docProvider = sourcePage.getDocumentProvider(); // #1625: Ensure the IDocumentProvider is not null. if (docProvider != null) savedString = new IDocumentWrapper(docProvider.getDocument(element)).get(); } } } }