From 431f70ba8c34a3e393c3bd909d52e79247f90ae2 Mon Sep 17 00:00:00 2001 From: David Dykstal Date: Thu, 26 Oct 2006 02:29:28 +0000 Subject: [PATCH] Bug 160286 - NPE when deleting a profile in the team view. The NPE is caused by bringing up the context menu on an empty selection under certain cirmcumstances. Fixing that revealed that profiles were being deleted in the model, but not on disk. Fixed that as well -- added methods to delete profiles by name and adjusted a method to include the ability to persist a deletion of a profile. --- .../persistence/PropertyFileProvider.java | 103 +++++++++++++++++- .../persistence/RSEPersistenceManager.java | 17 +++ .../persistence/SerializingProvider.java | 15 +++ .../persistence/IRSEPersistenceManager.java | 7 ++ .../persistence/IRSEPersistenceProvider.java | 8 ++ .../rse/core/model/ISystemProfileManager.java | 4 +- .../rse/ui/view/team/SystemTeamViewPart.java | 83 ++++++-------- .../internal/model/SystemProfileManager.java | 10 +- .../org/eclipse/rse/model/SystemRegistry.java | 4 +- .../rse/core/SystemAdapterHelpers.java | 27 ++--- 10 files changed, 206 insertions(+), 72 deletions(-) diff --git a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/PropertyFileProvider.java b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/PropertyFileProvider.java index 4a374ebcf21..d56fe5bd076 100644 --- a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/PropertyFileProvider.java +++ b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/PropertyFileProvider.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.resources.IContainer; @@ -39,7 +40,9 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; import org.eclipse.rse.persistence.IRSEPersistenceProvider; import org.eclipse.rse.persistence.dom.IRSEDOMConstants; import org.eclipse.rse.persistence.dom.RSEDOM; @@ -99,7 +102,7 @@ public class PropertyFileProvider implements IRSEPersistenceProvider { String candidateName = profileCandidate.getName(); String[] parts = split(candidateName, 2); if (parts[0].equals(AB_PROFILE)) { - String name = parts[1]; + String name = thaw(parts[1]); names.add(name); } } @@ -132,6 +135,23 @@ public class PropertyFileProvider implements IRSEPersistenceProvider { return true; } + /* (non-Javadoc) + * @see org.eclipse.rse.persistence.IRSEPersistenceProvider#deleteProfile(java.lang.String, org.eclipse.core.runtime.IProgressMonitor) + */ + public IStatus deleteProfile(String profileName, IProgressMonitor monitor) { + IStatus result = Status.OK_STATUS; + IFolder profileFolder = getProfileFolder(profileName); + if (profileFolder.exists()) { + try { + profileFolder.delete(IResource.FORCE, monitor); + } catch (CoreException e) { + result = new Status(IStatus.ERROR, null, 0, "Unexpected Exception", e); + } + } + return result; + } + + /** * Saves a node from the DOM to the file system. * @param node The node to save. @@ -190,8 +210,6 @@ public class PropertyFileProvider implements IRSEPersistenceProvider { } } - private static final String VALID = "abcdefghijklmnopqrstuvwxyz0123456789-._"; - private static final String UPPER = "ABCDEFGHIJKLMNOPQRTSUVWXYZ"; /** * Returns the name of a folder that can be used to store a node of a particular * type. Since this is a folder, its name must conform to the rules of the file @@ -205,6 +223,24 @@ public class PropertyFileProvider implements IRSEPersistenceProvider { String type = node.getType(); type = (String) typeQualifiers.get(type); String name = node.getName(); + name = freeze(name); + String result = combine(type, name); + return result; + } + + private static final String VALID = "abcdefghijklmnopqrstuvwxyz0123456789-._"; + private static final String UPPER = "ABCDEFGHIJKLMNOPQRTSUVWXYZ"; + /** + * Transforms an arbitrary name into one that can be used in any file system + * that supports long names. The transformation appends a number to the name + * that captures the case of the letters in the name. If a character falls + * outside the range of understood characters, it is converted to its hexadecimal unicode + * equivalent. Spaces are converted to underscores. + * @param name The name to be transformed + * @return The transformed name + * @see #thaw(String) + */ + private String freeze(String name) { int p = name.indexOf(':'); if (p >= 0) { name = name.substring(p + 1); @@ -230,7 +266,64 @@ public class PropertyFileProvider implements IRSEPersistenceProvider { } } name = buf.toString() + "_" + Long.toString(suffix); - String result = combine(type, name); + return name; + } + + /** + * Recovers an arbitrary name from its frozen counterpart. + * @param name The name to be transformed + * @return The transformed name + * @see #freeze(String) + */ + private String thaw(String name) { + String result = name; + Pattern suffixPattern = Pattern.compile("_(\\d+)$"); + Matcher m = suffixPattern.matcher(name); + if (m.find()) { + String root = name.substring(0, m.start()); + String suffix = m.group(1); + long caseCode = Long.parseLong(suffix); + root = thawUnicode(root); + root = thawCase(root, caseCode); + result = root; + } + return result; + } + + private String thawUnicode(String name) { + Pattern unicodePattern = Pattern.compile("#(\\p{XDigit}+)#"); + Matcher m = unicodePattern.matcher(name); + StringBuffer b = new StringBuffer(); + int p0 = 0; + while (m.find()) { + int p1 = m.start(); + String chunk0 = name.substring(p0, p1); + String digits = m.group(1); + int codePoint = Integer.valueOf(digits, 16).intValue(); + char ch = (char) codePoint; + String chunk1 = Character.toString(ch); + b.append(chunk0); + b.append(chunk1); + p0 = m.end(); + } + b.append(name.substring(p0)); + String result = b.toString(); + return result; + } + + private String thawCase(String name, long caseCode) { + StringBuffer b = new StringBuffer(); + char[] chars = name.toCharArray(); + for (int i = chars.length - 1; i >= 0; i--) { + char ch = chars[i]; + boolean shift = (caseCode & 1L) == 1; + if (shift) { + ch = (ch == '_') ? ' ' : Character.toUpperCase(ch); + } + b.append(ch); + caseCode = caseCode >> 1; + } + String result = b.reverse().toString(); return result; } @@ -672,7 +765,7 @@ public class PropertyFileProvider implements IRSEPersistenceProvider { * @return The folder that was created or found. */ private IFolder getProfileFolder(String profileName) { - String profileFolderName = combine(AB_PROFILE, profileName); + String profileFolderName = combine(AB_PROFILE, freeze(profileName)); IFolder providerFolder = getProviderFolder(); IFolder profileFolder = getFolder(providerFolder, profileFolderName); return profileFolder; diff --git a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/RSEPersistenceManager.java b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/RSEPersistenceManager.java index e43b3fc0f33..1cde267ebd8 100644 --- a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/RSEPersistenceManager.java +++ b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/RSEPersistenceManager.java @@ -25,7 +25,10 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.rse.core.RSECorePlugin; import org.eclipse.rse.core.filters.ISystemFilter; @@ -392,6 +395,20 @@ public class RSEPersistenceManager implements IRSEPersistenceManager { } return result; } + + /* (non-Javadoc) + * @see org.eclipse.rse.persistence.IRSEPersistenceManager#deleteProfile(java.lang.String) + */ + public void deleteProfile(final String profileName) { + Job job = new Job("delete profile") { + protected IStatus run(IProgressMonitor monitor) { + IRSEPersistenceProvider provider = getRSEPersistenceProvider(); + IStatus result = provider.deleteProfile(profileName, monitor); + return result; + } + }; + job.schedule(); + } public boolean isExporting() { return _currentState == STATE_EXPORTING; diff --git a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/SerializingProvider.java b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/SerializingProvider.java index 7c8c3eedd30..c6c3087f7b3 100644 --- a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/SerializingProvider.java +++ b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/internal/persistence/SerializingProvider.java @@ -31,6 +31,8 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; 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.rse.persistence.IRSEPersistenceProvider; import org.eclipse.rse.persistence.dom.RSEDOM; @@ -176,4 +178,17 @@ public class SerializingProvider implements IRSEPersistenceProvider return true; } + + public IStatus deleteProfile(String profileName, IProgressMonitor monitor) { + IStatus result = Status.OK_STATUS; + IFile profileFile = getProfileFile(profileName, monitor); + if (profileFile.exists()) { + try { + profileFile.delete(IResource.FORCE | IResource.KEEP_HISTORY, monitor); + } catch (CoreException e) { + result = new Status(IStatus.ERROR, null, 0, "Unexpected Exception", e); + } + } + return result; + } } \ No newline at end of file diff --git a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/persistence/IRSEPersistenceManager.java b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/persistence/IRSEPersistenceManager.java index c63dd1a9686..6bf36cfc8b0 100644 --- a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/persistence/IRSEPersistenceManager.java +++ b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/persistence/IRSEPersistenceManager.java @@ -16,6 +16,7 @@ package org.eclipse.rse.persistence; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.rse.core.filters.ISystemFilter; import org.eclipse.rse.core.filters.ISystemFilterPool; import org.eclipse.rse.core.filters.ISystemFilterPoolManager; @@ -116,4 +117,10 @@ public interface IRSEPersistenceManager public boolean isExporting(); public boolean isImporting(); + + /** + * Delete the persistent form of a profile. + * @param profileName The name of the profile to delete + */ + public void deleteProfile(String profileName); } \ No newline at end of file diff --git a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/persistence/IRSEPersistenceProvider.java b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/persistence/IRSEPersistenceProvider.java index 1b7942a854b..e366bc0e138 100644 --- a/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/persistence/IRSEPersistenceProvider.java +++ b/rse/plugins/org.eclipse.rse.core/persistence/org/eclipse/rse/persistence/IRSEPersistenceProvider.java @@ -17,6 +17,7 @@ package org.eclipse.rse.persistence; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; import org.eclipse.rse.persistence.dom.RSEDOM; @@ -52,4 +53,11 @@ public interface IRSEPersistenceProvider * @return The names of the profiles that have been saved by this persistence provider. */ public String[] getSavedProfileNames(); + + /** + * Removes a profile. Does nothing if the profile is not found. + * @param profileName the name of the profile to remove + * @param monitor the monitor for the operation + */ + public IStatus deleteProfile(String profileName, IProgressMonitor monitor); } \ No newline at end of file diff --git a/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/model/ISystemProfileManager.java b/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/model/ISystemProfileManager.java index a9586a806b9..9749ae846a5 100644 --- a/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/model/ISystemProfileManager.java +++ b/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/model/ISystemProfileManager.java @@ -104,8 +104,10 @@ public interface ISystemProfileManager { /** * Delete the given profile * @param profile the name of the profile to delete. + * @param persist true if the deletion is meant to be persisted as well, false if the deletion is just in the + * model. */ - public void deleteSystemProfile(ISystemProfile profile); + public void deleteSystemProfile(ISystemProfile profile, boolean persist); /** * Clone the given profile diff --git a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/view/team/SystemTeamViewPart.java b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/view/team/SystemTeamViewPart.java index c02cbf240a3..03db718cab0 100644 --- a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/view/team/SystemTeamViewPart.java +++ b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/view/team/SystemTeamViewPart.java @@ -29,6 +29,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; @@ -39,7 +40,6 @@ import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.util.ListenerList; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.IOpenListener; @@ -118,7 +118,7 @@ public class SystemTeamViewPart extends ViewPart implements ISetSelectionTarget, ISelectionProvider, ISystemModelChangeListener, ISystemMessageLine, ISelectionChangedListener, - ISystemDeleteTarget, ISystemRenameTarget, IMenuListener, IRSEViewPart + ISystemDeleteTarget, ISystemRenameTarget, IRSEViewPart { private boolean menuListenerAdded; @@ -132,10 +132,8 @@ public class SystemTeamViewPart private SystemMessage sysErrorMessage; // selectionChangedListeners - private ListenerList selectionChangedListeners = new ListenerList(6); - - private boolean privateProfileStillExists = false; - + private ListenerList selectionChangedListeners = new ListenerList(ListenerList.IDENTITY); + // context menu actions for project... protected SystemTeamReloadAction reloadRSEAction; protected SystemNewProfileAction newProfileAction; @@ -247,11 +245,21 @@ public class SystemTeamViewPart menuMgr.setRemoveAllWhenShown(true); Menu menu = menuMgr.createContextMenu(treeViewer.getTree()); treeViewer.getTree().setMenu(menu); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + fillContextMenu(manager); + addMenuListener(manager); + } + }); getSite().registerContextMenu(menuMgr, treeViewer); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + scrubOtherContributions(manager); + } + }); // important to add our listener after registering, so we are called second! // This gives us the opportunity to scrub the contributions added by others, to screen out // non-team additions. - menuMgr.addMenuListener(this); /* menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { @@ -293,14 +301,7 @@ public class SystemTeamViewPart fMemento= null; } - /** - * Called when the context menu is about to open. - * From IMenuListener interface - * Calls {@link #fillContextMenu(IMenuManager)} - */ - public void menuAboutToShow(IMenuManager menu) - { - fillContextMenu(menu); + private void addMenuListener(IMenuManager menu) { if (!menuListenerAdded) { if (menu instanceof MenuManager) @@ -315,7 +316,6 @@ public class SystemTeamViewPart } } } - //System.out.println("Inside menuAboutToShow: menu null? "+( ((MenuManager)menu).getMenu()==null)); } // ------------------------------------------- @@ -465,7 +465,6 @@ public class SystemTeamViewPart if (!(selection instanceof StructuredSelection)) return; StructuredSelection ssel = (StructuredSelection)selection; - java.util.List test = ssel.toList(); if (!ssel.isEmpty()) { // select and reveal the item treeViewer.setSelection(ssel, true); @@ -479,23 +478,18 @@ public class SystemTeamViewPart { SystemMenuManager ourMenu = new SystemMenuManager(menu); - privateProfileStillExists = (SystemStartHere.getSystemProfileManager().getDefaultPrivateSystemProfile() != null); - // Populate with our stuff... IStructuredSelection selection = getStructuredSelection(); Object firstSelection = selection.getFirstElement(); + createStandardGroups(menu); if (firstSelection instanceof IProject) { // Scrub unrelated menu items - scrubOtherContributions(menu); - - createStandardGroups(menu); if (selection.size() == 1) fillProjectContextMenu(ourMenu, selection); } else { - createStandardGroups(menu); ISystemViewElementAdapter adapter = SystemAdapterHelpers.getAdapter(firstSelection, treeViewer); if (adapter != null) { @@ -513,7 +507,7 @@ public class SystemTeamViewPart } } } - // wail through all actions, updating shell and selection + // whale through all actions, updating shell and selection IContributionItem[] items = menu.getItems(); for (int idx=0; idx < items.length; idx++) { @@ -657,6 +651,7 @@ public class SystemTeamViewPart */ private SystemTeamReloadAction getReloadRSEAction(IStructuredSelection selection) { + boolean privateProfileStillExists = (SystemStartHere.getSystemProfileManager().getDefaultPrivateSystemProfile() != null); if (reloadRSEAction == null) reloadRSEAction = new SystemTeamReloadAction(getShell()); reloadRSEAction.setSelection(selection); @@ -718,27 +713,21 @@ public class SystemTeamViewPart /** * Scrub the popup menu to remove everything but team-related stuff... */ - private void scrubOtherContributions(IMenuManager menuMgr) - { - IContributionItem items[] = menuMgr.getItems(); - - if (items != null) - { - //System.out.println("# existing menu items: "+items.length); - for (int idx=0; idx 0)) // defect 42112 fireEvent(new org.eclipse.rse.model.SystemResourceChangeEvent(connections, ISystemResourceChangeEvents.EVENT_DELETE_MANY, this)); diff --git a/rse/plugins/org.eclipse.rse.ui/systems/org/eclipse/rse/core/SystemAdapterHelpers.java b/rse/plugins/org.eclipse.rse.ui/systems/org/eclipse/rse/core/SystemAdapterHelpers.java index a4b4751d5b8..04566d010b6 100644 --- a/rse/plugins/org.eclipse.rse.ui/systems/org/eclipse/rse/core/SystemAdapterHelpers.java +++ b/rse/plugins/org.eclipse.rse.ui/systems/org/eclipse/rse/core/SystemAdapterHelpers.java @@ -34,19 +34,20 @@ public class SystemAdapterHelpers /** - * Returns the implementation of ISystemViewElement for the given - * object. Returns null if the adapter is not defined or the - * object is not adaptable. - */ - public static ISystemViewElementAdapter getAdapter(Object o) - { - ISystemViewElementAdapter adapter = null; - if (!(o instanceof IAdaptable)) - adapter = (ISystemViewElementAdapter)Platform.getAdapterManager().getAdapter(o,ISystemViewElementAdapter.class); - else - adapter = (ISystemViewElementAdapter)((IAdaptable)o).getAdapter(ISystemViewElementAdapter.class); - return adapter; - } + * Returns the implementation of ISystemViewElement for the given + * object. Returns null if the adapter is not defined or the + * object is not adaptable. + */ + public static ISystemViewElementAdapter getAdapter(Object o) { + ISystemViewElementAdapter adapter = null; + if (o instanceof IAdaptable) { + adapter = (ISystemViewElementAdapter) ((IAdaptable) o).getAdapter(ISystemViewElementAdapter.class); + } if (o != null) { + adapter = (ISystemViewElementAdapter) Platform.getAdapterManager().getAdapter(o, ISystemViewElementAdapter.class); + } + return adapter; + } + /** * Overload to use when calling from a viewer. This not only finds and returns * the adapter, but also sets its viewer to the given viewer. Many actions rely