Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.apache.directory.studio.entryeditors; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.directory.api.ldap.model.name.Dn; import org.apache.directory.studio.connection.core.Connection; import org.apache.directory.studio.connection.core.event.ConnectionEventRegistry; import org.apache.directory.studio.connection.core.event.ConnectionUpdateAdapter; import org.apache.directory.studio.connection.core.event.ConnectionUpdateListener; import org.apache.directory.studio.connection.ui.ConnectionUIPlugin; import org.apache.directory.studio.connection.ui.RunnableContextRunner; import org.apache.directory.studio.ldapbrowser.common.BrowserCommonActivator; import org.apache.directory.studio.ldapbrowser.core.events.EntryModificationEvent; import org.apache.directory.studio.ldapbrowser.core.events.EntryUpdateListener; import org.apache.directory.studio.ldapbrowser.core.events.EventRegistry; import org.apache.directory.studio.ldapbrowser.core.events.ValueAddedEvent; import org.apache.directory.studio.ldapbrowser.core.events.ValueDeletedEvent; import org.apache.directory.studio.ldapbrowser.core.events.ValueModifiedEvent; import org.apache.directory.studio.ldapbrowser.core.events.ValueMultiModificationEvent; import org.apache.directory.studio.ldapbrowser.core.events.ValueRenamedEvent; import org.apache.directory.studio.ldapbrowser.core.jobs.StudioBrowserJob; import org.apache.directory.studio.ldapbrowser.core.jobs.UpdateEntryRunnable; import org.apache.directory.studio.ldapbrowser.core.model.IAttribute; import org.apache.directory.studio.ldapbrowser.core.model.IBookmark; import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection; import org.apache.directory.studio.ldapbrowser.core.model.IEntry; import org.apache.directory.studio.ldapbrowser.core.model.ISearchResult; import org.apache.directory.studio.ldapbrowser.core.model.IValue; import org.apache.directory.studio.ldapbrowser.core.utils.CompoundModification; import org.apache.directory.studio.ldapbrowser.core.utils.Utils; import org.apache.directory.studio.ldapbrowser.ui.BrowserUIConstants; import org.apache.directory.studio.ldapbrowser.ui.BrowserUIPlugin; import org.apache.directory.studio.ldifparser.LdifFormatParameters; import org.apache.directory.studio.ldifparser.model.LdifFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * A EntryEditorManager is used to manage entry editors. It provides methods to get * the best or alternative entry editors for a given entry. * * The available value editors are specified by the extension point * <code>org.apache.directory.studio.entryeditors</code>. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public class EntryEditorManager { private static final String ID_ATTR = "id"; //$NON-NLS-1$ private static final String NAME_ATTR = "name"; //$NON-NLS-1$ private static final String DESCRIPTION_ATTR = "description"; //$NON-NLS-1$ private static final String ICON_ATTR = "icon"; //$NON-NLS-1$ private static final String CLASS_ATTR = "class"; //$NON-NLS-1$ private static final String EDITOR_ID_ATTR = "editorId"; //$NON-NLS-1$ private static final String MULTI_WINDOW_ATTR = "multiWindow"; //$NON-NLS-1$ private static final String PRIORITY_ATTR = "priority"; //$NON-NLS-1$ /** The priorities separator */ public static final String PRIORITIES_SEPARATOR = ","; //$NON-NLS-1$ /** The list of entry editors */ private Map<String, EntryEditorExtension> entryEditorExtensions = new HashMap<>(); /** The shared reference copies for open-save-close editors; original entry -> reference copy */ private Map<IEntry, IEntry> oscSharedReferenceCopies = new HashMap<>(); /** The shared working copies for open-save-close editors; original entry -> working copy */ private Map<IEntry, IEntry> oscSharedWorkingCopies = new HashMap<>(); /** The shared reference copies for auto-save editors; original entry -> reference copy */ private Map<IEntry, IEntry> autoSaveSharedReferenceCopies = new HashMap<>(); /** The shared working copies for auto-save editors; original entry -> working copy */ private Map<IEntry, IEntry> autoSaveSharedWorkingCopies = new HashMap<>(); /** The comparator for entry editors */ private Comparator<EntryEditorExtension> entryEditorComparator = new Comparator<EntryEditorExtension>() { @Override public int compare(EntryEditorExtension o1, EntryEditorExtension o2) { if (o1 == null) { return (o2 == null) ? 0 : -1; } if (o2 == null) { return 1; } // Getting priorities int o1Priority = o1.getPriority(); int o2Priority = o2.getPriority(); if (o1Priority != o2Priority) { return (o1Priority > o2Priority) ? -1 : 1; } // Getting names String o1Name = o1.getName(); String o2Name = o2.getName(); if (o1Name == null) { return (o2Name == null) ? 0 : -1; } return o1Name.compareTo(o2Name); } }; /** The listener for workbench part update */ private IPartListener2 partListener = new IPartListener2() { @Override public void partActivated(IWorkbenchPartReference partRef) { cleanupCopies(partRef.getPage()); IEntryEditor editor = getEntryEditor(partRef); if (editor != null) { EntryEditorInput eei = editor.getEntryEditorInput(); IEntry originalEntry = eei.getResolvedEntry(); IEntry oscSharedReferenceCopy = oscSharedReferenceCopies.get(originalEntry); IEntry oscSharedWorkingCopy = oscSharedWorkingCopies.get(originalEntry); if (editor.isAutoSave()) { // check if the same entry is used in an OSC editor and is dirty -> should save first? if (oscSharedReferenceCopy != null && oscSharedWorkingCopy != null) { LdifFile diff = Utils.computeDiff(oscSharedReferenceCopy, oscSharedWorkingCopy); if (diff != null) { MessageDialog dialog = new MessageDialog(partRef.getPart(false).getSite().getShell(), Messages.getString("EntryEditorManager.SaveChanges"), null, //$NON-NLS-1$ Messages.getString("EntryEditorManager.SaveChangesDescription"), //$NON-NLS-1$ MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 0); int result = dialog.open(); if (result == 0) { saveSharedWorkingCopy(originalEntry, true, null); } } } } else { // check if original entry was updated if ((oscSharedReferenceCopy != null) && (oscSharedWorkingCopy != null)) { LdifFile refDiff = Utils.computeDiff(originalEntry, oscSharedReferenceCopy); if (refDiff != null) { // check if we could just update the working copy LdifFile workDiff = Utils.computeDiff(oscSharedReferenceCopy, oscSharedWorkingCopy); if (workDiff != null) { askUpdateSharedWorkingCopy(partRef, originalEntry, oscSharedWorkingCopy, null); } } } } } } @Override public void partOpened(IWorkbenchPartReference partRef) { } @Override public void partClosed(IWorkbenchPartReference partRef) { cleanupCopies(partRef.getPage()); } @Override public void partInputChanged(IWorkbenchPartReference partRef) { cleanupCopies(partRef.getPage()); } @Override public void partHidden(IWorkbenchPartReference partRef) { } @Override public void partDeactivated(IWorkbenchPartReference partRef) { } @Override public void partBroughtToTop(IWorkbenchPartReference partRef) { } @Override public void partVisible(IWorkbenchPartReference partRef) { } }; /** The listener for entry update */ private EntryUpdateListener entryUpdateListener = new EntryUpdateListener() { @Override public void entryUpdated(EntryModificationEvent event) { IEntry modifiedEntry = event.getModifiedEntry(); IBrowserConnection browserConnection = modifiedEntry.getBrowserConnection(); IEntry originalEntry = browserConnection.getEntryFromCache(modifiedEntry.getDn()); if (modifiedEntry == originalEntry) { // an original entry has been modified, check if we could update the editors // if the OSC editor is not dirty we could update the working copy IEntry oscSharedReferenceCopy = oscSharedReferenceCopies.get(originalEntry); IEntry oscSharedWorkingCopy = oscSharedWorkingCopies.get(originalEntry); if ((oscSharedReferenceCopy != null) && (oscSharedWorkingCopy != null)) { LdifFile refDiff = Utils.computeDiff(originalEntry, oscSharedReferenceCopy); if (refDiff != null) { // diff between original entry and reference copy LdifFile workDiff = Utils.computeDiff(oscSharedReferenceCopy, oscSharedWorkingCopy); if (workDiff == null) { // no changes on working copy, update updateOscSharedReferenceCopy(originalEntry); updateOscSharedWorkingCopy(originalEntry); // inform all OSC editors List<IEntryEditor> oscEditors = getOscEditors(oscSharedWorkingCopy); for (IEntryEditor editor : oscEditors) { editor.workingCopyModified(event.getSource()); } } else { // changes on working copy, ask before update IWorkbenchPartReference reference = getActivePartRef( getOscEditors(oscSharedWorkingCopy)); if (reference != null) { askUpdateSharedWorkingCopy(reference, originalEntry, oscSharedWorkingCopy, event.getSource()); } } } else { // no diff betweeen original entry and reference copy, check if editor is dirty LdifFile workDiff = Utils.computeDiff(oscSharedReferenceCopy, oscSharedWorkingCopy); if (workDiff != null) { // changes on working copy, ask before update IWorkbenchPartReference reference = getActivePartRef( getOscEditors(oscSharedWorkingCopy)); if (reference != null) { askUpdateSharedWorkingCopy(reference, originalEntry, oscSharedWorkingCopy, event.getSource()); } } } } // always update auto-save working copies, if necessary IEntry autoSaveSharedReferenceCopy = autoSaveSharedReferenceCopies.get(originalEntry); IEntry autoSaveSharedWorkingCopy = autoSaveSharedWorkingCopies.get(originalEntry); if ((autoSaveSharedReferenceCopy != null) && (autoSaveSharedWorkingCopy != null)) { LdifFile diff = Utils.computeDiff(originalEntry, autoSaveSharedReferenceCopy); if (diff != null) { updateAutoSaveSharedReferenceCopy(originalEntry); updateAutoSaveSharedWorkingCopy(originalEntry); List<IEntryEditor> editors = getAutoSaveEditors(autoSaveSharedWorkingCopy); for (IEntryEditor editor : editors) { editor.workingCopyModified(event.getSource()); } } } // check all editors: if the input does not exist any more then close the editor IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); // Collecting editor references to close List<IEditorReference> editorReferences = new ArrayList<>(); for (IEditorReference ref : activePage.getEditorReferences()) { IEntryEditor editor = getEntryEditor(ref); if (editor != null) { EntryEditorInput entryEditorInput = editor.getEntryEditorInput(); if (entryEditorInput != null) { IEntry resolvedEntry = entryEditorInput.getResolvedEntry(); if ((editor != null) && (resolvedEntry != null)) { IBrowserConnection bc = resolvedEntry.getBrowserConnection(); Dn dn = resolvedEntry.getDn(); if (bc.getEntryFromCache(dn) == null) { editorReferences.add(ref); } } } } } // Closing the corresponding editor references if (!editorReferences.isEmpty()) { activePage.closeEditors(editorReferences.toArray(new IEditorReference[0]), false); } } else if (oscSharedWorkingCopies.containsKey(originalEntry) && (oscSharedWorkingCopies.get(originalEntry) == modifiedEntry)) { // OSC working copy has been modified: inform OSC editors IEntry oscSharedWorkingCopy = oscSharedWorkingCopies.get(originalEntry); List<IEntryEditor> oscEditors = getOscEditors(oscSharedWorkingCopy); for (IEntryEditor editor : oscEditors) { editor.workingCopyModified(event.getSource()); } } else if (autoSaveSharedWorkingCopies.containsValue(originalEntry) && (autoSaveSharedWorkingCopies.get(originalEntry) == modifiedEntry)) { // auto-save working copy has been modified: save and inform all auto-save editors IEntry autoSaveSharedReferenceCopy = autoSaveSharedReferenceCopies.get(originalEntry); IEntry autoSaveSharedWorkingCopy = autoSaveSharedWorkingCopies.get(originalEntry); // sanity check: never save if event source is the EntryEditorManager if (event.getSource() instanceof EntryEditorManager) { return; } // only save if we receive a real value modification event if (!((event instanceof ValueAddedEvent) || (event instanceof ValueDeletedEvent) || (event instanceof ValueModifiedEvent) || (event instanceof ValueRenamedEvent) || (event instanceof ValueMultiModificationEvent))) { return; } // consistency check: don't save if there is an empty value, silently return in that case for (IAttribute attribute : autoSaveSharedWorkingCopy.getAttributes()) { for (IValue value : attribute.getValues()) { if (value.isEmpty()) { return; } } } LdifFile diff = Utils.computeDiff(autoSaveSharedReferenceCopy, autoSaveSharedWorkingCopy); if (diff != null) { // remove entry from map, reduces number of fired events autoSaveSharedReferenceCopies.remove(originalEntry); autoSaveSharedWorkingCopies.remove(originalEntry); UpdateEntryRunnable runnable = new UpdateEntryRunnable(originalEntry, diff.toFormattedString(LdifFormatParameters.DEFAULT)); RunnableContextRunner.execute(runnable, null, true); // put entry back to map autoSaveSharedReferenceCopies.put(originalEntry, autoSaveSharedReferenceCopy); autoSaveSharedWorkingCopies.put(originalEntry, autoSaveSharedWorkingCopy); // don't care if status is ok or not: always update updateAutoSaveSharedReferenceCopy(originalEntry); updateAutoSaveSharedWorkingCopy(originalEntry); List<IEntryEditor> editors = getAutoSaveEditors(autoSaveSharedWorkingCopy); for (IEntryEditor editor : editors) { editor.workingCopyModified(event.getSource()); } } } } }; /** The listener for connection update */ private ConnectionUpdateListener connectionUpdateListener = new ConnectionUpdateAdapter() { @Override public void connectionClosed(Connection connection) { closeEditorsBelongingToConnection(connection); } @Override public void connectionRemoved(Connection connection) { closeEditorsBelongingToConnection(connection); } }; /** * Creates a new instance of EntryEditorManager. */ public EntryEditorManager() { if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) { getEditorManager(); } } /** * Get the EditorManager instance */ public void getEditorManager() { initEntryEditorExtensions(); PlatformUI.getWorkbench().getActiveWorkbenchWindow().getPartService().addPartListener(partListener); EventRegistry.addEntryUpdateListener(entryUpdateListener, BrowserCommonActivator.getDefault().getEventRunner()); ConnectionEventRegistry.addConnectionUpdateListener(connectionUpdateListener, ConnectionUIPlugin.getDefault().getEventRunner()); } /** * Initializes the entry editors extensions. */ private void initEntryEditorExtensions() { entryEditorExtensions = new HashMap<>(); IExtensionRegistry registry = Platform.getExtensionRegistry(); IExtensionPoint extensionPoint = registry .getExtensionPoint(BrowserUIConstants.ENTRY_EDITOR_EXTENSION_POINT); IConfigurationElement[] members = extensionPoint.getConfigurationElements(); // For each extension: for (IConfigurationElement member : members) { EntryEditorExtension bean = new EntryEditorExtension(); IExtension extension = member.getDeclaringExtension(); String extendingPluginId = extension.getNamespaceIdentifier(); bean.setId(member.getAttribute(ID_ATTR)); bean.setName(member.getAttribute(NAME_ATTR)); bean.setDescription(member.getAttribute(DESCRIPTION_ATTR)); String iconPath = member.getAttribute(ICON_ATTR); ImageDescriptor icon = AbstractUIPlugin.imageDescriptorFromPlugin(extendingPluginId, iconPath); if (icon == null) { icon = ImageDescriptor.getMissingImageDescriptor(); } bean.setIcon(icon); bean.setClassName(member.getAttribute(CLASS_ATTR)); bean.setEditorId(member.getAttribute(EDITOR_ID_ATTR)); bean.setMultiWindow("true".equalsIgnoreCase(member.getAttribute(MULTI_WINDOW_ATTR))); //$NON-NLS-1$ bean.setPriority(Integer.parseInt(member.getAttribute(PRIORITY_ATTR))); try { bean.setEditorInstance((IEntryEditor) member.createExecutableExtension(CLASS_ATTR)); } catch (CoreException e) { // Will never happen } entryEditorExtensions.put(bean.getId(), bean); } } public void dispose() { IWorkbenchWindow ww = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (ww != null) { ww.getPartService().removePartListener(partListener); EventRegistry.removeEntryUpdateListener(entryUpdateListener); } } /** * Gets the entry editor extensions. * * @return the entry editor extensions */ public Collection<EntryEditorExtension> getEntryEditorExtensions() { return entryEditorExtensions.values(); } /** * Gets the entry editor extension. * * @param id the entry editor extension id * * @return the entry editor extension, null if none found */ public EntryEditorExtension getEntryEditorExtension(String id) { return entryEditorExtensions.get(id); } /** * Gets the sorted entry editor extensions. * * @return * the sorted entry editor extensions */ public Collection<EntryEditorExtension> getSortedEntryEditorExtensions() { boolean useUserPriority = BrowserUIPlugin.getDefault().getPluginPreferences() .getBoolean(BrowserUIConstants.PREFERENCE_ENTRYEDITORS_USE_USER_PRIORITIES); if (useUserPriority) { return getEntryEditorExtensionsSortedByUserPriority(); } else { return getEntryEditorExtensionsSortedByDefaultPriority(); } } /** * Gets the entry editor extensions sorted by default priority. * * @return * the entry editor extensions sorted by default priority */ public Collection<EntryEditorExtension> getEntryEditorExtensionsSortedByDefaultPriority() { // Getting all entry editors Collection<EntryEditorExtension> entryEditorExtensions = getEntryEditorExtensions(); // Creating the sorted entry editors list ArrayList<EntryEditorExtension> sortedEntryEditorsList = new ArrayList<>(entryEditorExtensions.size()); // Adding the remaining entry editors for (EntryEditorExtension entryEditorExtension : entryEditorExtensions) { sortedEntryEditorsList.add(entryEditorExtension); } // Sorting the remaining entry editors based on their priority Collections.sort(sortedEntryEditorsList, entryEditorComparator); return sortedEntryEditorsList; } /** * Gets the entry editor extensions sorted by user's priority. * * @return * the entry editor extensions sorted by user's priority */ public Collection<EntryEditorExtension> getEntryEditorExtensionsSortedByUserPriority() { // Getting all entry editors Collection<EntryEditorExtension> entryEditorExtensions = BrowserUIPlugin.getDefault() .getEntryEditorManager().getEntryEditorExtensions(); // Creating the sorted entry editors list Collection<EntryEditorExtension> sortedEntryEditorsList = new ArrayList<>(entryEditorExtensions.size()); // Getting the user's priorities String userPriorities = BrowserUIPlugin.getDefault().getPluginPreferences() .getString(BrowserUIConstants.PREFERENCE_ENTRYEDITORS_USER_PRIORITIES); if ((userPriorities != null) && (!"".equals(userPriorities))) //$NON-NLS-1$ { String[] splittedUserPriorities = userPriorities.split(PRIORITIES_SEPARATOR); if ((splittedUserPriorities != null) && (splittedUserPriorities.length > 0)) { // Creating a map where entry editors are accessible via their ID Map<String, EntryEditorExtension> entryEditorsMap = new HashMap<>(); for (EntryEditorExtension entryEditorExtension : entryEditorExtensions) { entryEditorsMap.put(entryEditorExtension.getId(), entryEditorExtension); } // Adding the entry editors according to the user's priority for (String entryEditorId : splittedUserPriorities) { // Verifying the entry editor is present in the map if (entryEditorsMap.containsKey(entryEditorId)) { // Adding it to the sorted list sortedEntryEditorsList.add(entryEditorsMap.get(entryEditorId)); } } } // If some new plugins have been added recently, their new // entry editors may not be present in the string stored in // the preferences. // We are then adding them at the end of the sorted list. // Creating a list of remaining entry editors List<EntryEditorExtension> remainingEntryEditors = new ArrayList<>(); for (EntryEditorExtension entryEditorExtension : entryEditorExtensions) { // Verifying the entry editor is present in the sorted list if (!sortedEntryEditorsList.contains(entryEditorExtension)) { // Adding it to the remaining list remainingEntryEditors.add(entryEditorExtension); } } // Sorting the remaining entry editors based on their priority Collections.sort(remainingEntryEditors, entryEditorComparator); // Adding the remaining entry editors for (EntryEditorExtension entryEditorExtension : remainingEntryEditors) { sortedEntryEditorsList.add(entryEditorExtension); } } return sortedEntryEditorsList; } /** * Closes the open editors belonging to the given connection. * * @param connection * the connection */ private void closeEditorsBelongingToConnection(Connection connection) { if (connection != null) { IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); // Collecting editor references to close List<IEditorReference> editorReferences = new ArrayList<>(); for (IEditorReference ref : activePage.getEditorReferences()) { IEntryEditor editor = getEntryEditor(ref); if ((editor != null) && (editor.getEntryEditorInput().getResolvedEntry() != null)) { IBrowserConnection bc = editor.getEntryEditorInput().getResolvedEntry().getBrowserConnection(); if (connection.equals(bc.getConnection())) { editorReferences.add(ref); } } } // Closing the corresponding editor references if (!editorReferences.isEmpty()) { activePage.closeEditors(editorReferences.toArray(new IEditorReference[0]), false); } } } /** * Opens an entry editor with the given entry editor extension and one of * the given entries, search results or bookmarks. * * @param extension the entry editor extension * @param entries an array of entries * @param searchResults an array of search results * @param bookmarks an arrays of bookmarks */ public void openEntryEditor(EntryEditorExtension extension, IEntry[] entries, ISearchResult[] searchResults, IBookmark[] bookmarks) { OpenEntryEditorRunnable runnable = new OpenEntryEditorRunnable(extension, entries, searchResults, bookmarks); StudioBrowserJob job = new StudioBrowserJob(runnable); job.setPriority(Job.INTERACTIVE); // Highest priority (just in case) job.execute(); } /** * Opens an entry editor with one of the given entries, search results or bookmarks. * * @param entries an array of entries * @param searchResults an array of search results * @param bookmarks an arrays of bookmarks */ public void openEntryEditor(IEntry[] entries, ISearchResult[] searchResults, IBookmark[] bookmarks) { openEntryEditor(null, entries, searchResults, bookmarks); } private void updateOscSharedReferenceCopy(IEntry entry) { IEntry referenceCopy = oscSharedReferenceCopies.remove(entry); if (referenceCopy != null) { EventRegistry.suspendEventFiringInCurrentThread(); EntryEditorUtils.ensureAttributesInitialized(entry); new CompoundModification().replaceAttributes(entry, referenceCopy, this); EventRegistry.resumeEventFiringInCurrentThread(); oscSharedReferenceCopies.put(entry, referenceCopy); } } private void updateOscSharedWorkingCopy(IEntry entry) { IEntry workingCopy = oscSharedWorkingCopies.get(entry); if (workingCopy != null) { EntryEditorUtils.ensureAttributesInitialized(entry); new CompoundModification().replaceAttributes(entry, workingCopy, this); } } private void updateAutoSaveSharedReferenceCopy(IEntry entry) { EventRegistry.suspendEventFiringInCurrentThread(); EntryEditorUtils.ensureAttributesInitialized(entry); IEntry workingCopy = autoSaveSharedReferenceCopies.get(entry); new CompoundModification().replaceAttributes(entry, workingCopy, this); EventRegistry.resumeEventFiringInCurrentThread(); } private void updateAutoSaveSharedWorkingCopy(IEntry entry) { EntryEditorUtils.ensureAttributesInitialized(entry); IEntry workingCopy = autoSaveSharedWorkingCopies.get(entry); new CompoundModification().replaceAttributes(entry, workingCopy, this); } private List<IEntryEditor> getOscEditors(IEntry workingCopy) { List<IEntryEditor> oscEditors = new ArrayList<>(); IEditorReference[] editorReferences = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage() .getEditorReferences(); for (IEditorReference ref : editorReferences) { IEntryEditor editor = getEntryEditor(ref); if ((editor != null) && !editor.isAutoSave() && ((workingCopy == null) || (editor.getEntryEditorInput().getSharedWorkingCopy(editor) == workingCopy))) { oscEditors.add(editor); } } return oscEditors; } private List<IEntryEditor> getAutoSaveEditors(IEntry workingCopy) { List<IEntryEditor> autoSaveEditors = new ArrayList<>(); IEditorReference[] editorReferences = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage() .getEditorReferences(); for (IEditorReference ref : editorReferences) { IEntryEditor editor = getEntryEditor(ref); if ((editor != null) && editor.isAutoSave() && (editor.getEntryEditorInput().getSharedWorkingCopy(editor) == workingCopy)) { autoSaveEditors.add(editor); } } return autoSaveEditors; } private IEntryEditor getEntryEditor(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (part instanceof IEntryEditor) { return (IEntryEditor) part; } return null; } private IWorkbenchPartReference getActivePartRef(List<IEntryEditor> editors) { for (IEntryEditor editor : editors) { IWorkbenchPart part = (IWorkbenchPart) editor; IEditorPart activeEditor = part.getSite().getPage().getActiveEditor(); if (part == activeEditor) { return part.getSite().getPage().getReference(part); } } return null; } IEntry getSharedWorkingCopy(IEntry originalEntry, IEntryEditor editor) { cleanupCopies(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()); EntryEditorUtils.ensureAttributesInitialized(originalEntry); if (editor.isAutoSave()) { if (!autoSaveSharedReferenceCopies.containsKey(originalEntry)) { autoSaveSharedReferenceCopies.put(originalEntry, new CompoundModification().cloneEntry(originalEntry)); } if (!autoSaveSharedWorkingCopies.containsKey(originalEntry)) { IEntry referenceCopy = autoSaveSharedReferenceCopies.get(originalEntry); autoSaveSharedWorkingCopies.put(originalEntry, new CompoundModification().cloneEntry(referenceCopy)); } return autoSaveSharedWorkingCopies.get(originalEntry); } else { if (!oscSharedReferenceCopies.containsKey(originalEntry)) { oscSharedReferenceCopies.put(originalEntry, new CompoundModification().cloneEntry(originalEntry)); } if (!oscSharedWorkingCopies.containsKey(originalEntry)) { IEntry referenceCopy = oscSharedReferenceCopies.get(originalEntry); oscSharedWorkingCopies.put(originalEntry, new CompoundModification().cloneEntry(referenceCopy)); } return oscSharedWorkingCopies.get(originalEntry); } } boolean isSharedWorkingCopyDirty(IEntry originalEntry, IEntryEditor editor) { if (editor.isAutoSave()) { return false; } else { IEntry referenceCopy = oscSharedReferenceCopies.get(originalEntry); IEntry workingCopy = oscSharedWorkingCopies.get(originalEntry); if ((referenceCopy != null) && (workingCopy != null)) { LdifFile diff = Utils.computeDiff(referenceCopy, workingCopy); return diff != null; } return false; } } IStatus saveSharedWorkingCopy(IEntry originalEntry, boolean handleError, IEntryEditor editor) { if ((editor == null) || !editor.isAutoSave()) { IEntry referenceCopy = oscSharedReferenceCopies.get(originalEntry); IEntry workingCopy = oscSharedWorkingCopies.get(originalEntry); if ((referenceCopy != null) && (workingCopy != null)) { // consistency check: don't save if there is an empty value, throw an exception as the user pressed 'save' for (IAttribute attribute : workingCopy.getAttributes()) { for (IValue value : attribute.getValues()) { if (value.isEmpty()) { throw new RuntimeException( NLS.bind(Messages.getString("EntryEditorManager.EmptyValueInAttribute"), //$NON-NLS-1$ attribute.getDescription())); } } } LdifFile diff = Utils.computeDiff(referenceCopy, workingCopy); if (diff != null) { // remove entry from map, reduces number of fired events oscSharedReferenceCopies.remove(originalEntry); oscSharedWorkingCopies.remove(originalEntry); // save by executing the LDIF UpdateEntryRunnable runnable = new UpdateEntryRunnable(originalEntry, diff.toFormattedString(LdifFormatParameters.DEFAULT)); IStatus status = RunnableContextRunner.execute(runnable, null, handleError); // put entry back to map oscSharedReferenceCopies.put(originalEntry, referenceCopy); oscSharedWorkingCopies.put(originalEntry, workingCopy); if (status.isOK()) { updateOscSharedReferenceCopy(originalEntry); updateOscSharedWorkingCopy(originalEntry); } return status; } } } return null; } void resetSharedWorkingCopy(IEntry originalEntry, IEntryEditor editor) { if ((editor == null) || !editor.isAutoSave()) { IEntry referenceCopy = oscSharedReferenceCopies.get(originalEntry); IEntry workingCopy = oscSharedWorkingCopies.get(originalEntry); if ((referenceCopy != null) && (workingCopy != null)) { updateOscSharedReferenceCopy(originalEntry); updateOscSharedWorkingCopy(originalEntry); } } } private void askUpdateSharedWorkingCopy(IWorkbenchPartReference partRef, IEntry originalEntry, IEntry oscSharedWorkingCopy, Object source) { MessageDialog dialog = new MessageDialog(partRef.getPart(false).getSite().getShell(), Messages.getString("EntryEditorManager.EntryChanged"), null, Messages //$NON-NLS-1$ .getString("EntryEditorManager.EntryChangedDescription"), //$NON-NLS-1$ MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 0); int result = dialog.open(); if (result == 0) { // update reference copy and working copy updateOscSharedReferenceCopy(originalEntry); updateOscSharedWorkingCopy(originalEntry); // inform all OSC editors List<IEntryEditor> oscEditors = getOscEditors(oscSharedWorkingCopy); for (IEntryEditor oscEditor : oscEditors) { oscEditor.workingCopyModified(source); } } } private void cleanupCopies(IWorkbenchPage page) { // cleanup unused copies (OSC + auto-save) Set<IEntry> oscEntries = new HashSet<>(); Set<IEntry> autoSaveEntries = new HashSet<>(); IEditorReference[] editorReferences = page.getEditorReferences(); for (IEditorReference ref : editorReferences) { IEntryEditor editor = getEntryEditor(ref); if (editor != null) { EntryEditorInput input = editor.getEntryEditorInput(); if ((input != null) && (input.getResolvedEntry() != null)) { IEntry entry = input.getResolvedEntry(); if (editor.isAutoSave()) { autoSaveEntries.add(entry); } else { oscEntries.add(entry); } } } } for (Iterator<IEntry> it = oscSharedReferenceCopies.keySet().iterator(); it.hasNext();) { IEntry entry = it.next(); if (!oscEntries.contains(entry)) { it.remove(); oscSharedWorkingCopies.remove(entry); } } for (Iterator<IEntry> it = oscSharedWorkingCopies.keySet().iterator(); it.hasNext();) { IEntry entry = it.next(); if (!oscEntries.contains(entry)) { it.remove(); } } for (Iterator<IEntry> it = autoSaveSharedReferenceCopies.keySet().iterator(); it.hasNext();) { IEntry entry = it.next(); if (!autoSaveEntries.contains(entry)) { it.remove(); } } for (Iterator<IEntry> it = autoSaveSharedWorkingCopies.keySet().iterator(); it.hasNext();) { IEntry entry = it.next(); if (!autoSaveEntries.contains(entry)) { it.remove(); } } } }