/******************************************************************************* * Mission Control Technologies, Copyright (c) 2009-2012, United States Government * as represented by the Administrator of the National Aeronautics and Space * Administration. All rights reserved. * * The MCT platform is licensed 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. * * MCT includes source code licensed under additional open source licenses. See * the MCT Open Source Licenses file included with this distribution or the About * MCT Licenses dialog available at runtime from the MCT Help menu for additional * information. *******************************************************************************/ package gov.nasa.arc.mct.gui.actions; import gov.nasa.arc.mct.components.AbstractComponent; import gov.nasa.arc.mct.gui.ActionContext; import gov.nasa.arc.mct.gui.ContextAwareAction; import gov.nasa.arc.mct.gui.MCTMutableTreeNode; import gov.nasa.arc.mct.gui.OptionBox; import gov.nasa.arc.mct.gui.View; import gov.nasa.arc.mct.gui.housing.MCTDirectoryArea; import gov.nasa.arc.mct.gui.housing.MCTHousing; import gov.nasa.arc.mct.gui.impl.ActionContextImpl; import gov.nasa.arc.mct.gui.impl.WindowManagerImpl; import gov.nasa.arc.mct.platform.spi.Platform; import gov.nasa.arc.mct.platform.spi.PlatformAccess; import gov.nasa.arc.mct.platform.spi.WindowManager; import gov.nasa.arc.mct.policy.PolicyContext; import gov.nasa.arc.mct.policy.PolicyInfo; import gov.nasa.arc.mct.services.component.PolicyManager; import gov.nasa.arc.mct.services.component.ViewInfo; import gov.nasa.arc.mct.services.component.ViewType; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTree; import javax.swing.SwingConstants; import javax.swing.tree.TreePath; /** * Describes recursive deletion of components and their descendants. * Descendants which can be deleted will be deleted, and descendants * which cannot be deleted (due to policy, for example) will instead * be ignored (this has the same effect as a Remove Manifestation.) * * In the specific case where a descendant cannot be deleted (per * policy) but is the last manifestation of that object in existence, * the action is not completed and the user is instead notified. */ public class DeleteAllAction extends ContextAwareAction { private static final ResourceBundle bundle = ResourceBundle.getBundle("gov/nasa/arc/mct/gui/actions/Bundle"); private static final long serialVersionUID = 3047419887471823851L; private static String WARNING = bundle.getString("DeleteAllWarningTitle"); private static String TEXT = "Delete All"; private TreePath[] selectedTreePaths; private ActionContextImpl actionContext; public DeleteAllAction() { super(TEXT); } @Override public boolean canHandle(ActionContext context) { actionContext = (ActionContextImpl) context; MCTHousing activeHousing = actionContext.getTargetHousing(); if (activeHousing == null) return false; Collection<View> selection = activeHousing.getSelectionProvider().getSelectedManifestations(); if (selection.isEmpty()) { return false; } ViewInfo vi = selection.iterator().next().getInfo(); if (!(vi != null && vi.getViewType() == ViewType.NODE)){ return false; } if (!(activeHousing.getDirectoryArea() instanceof MCTDirectoryArea)) { return false; } MCTDirectoryArea directory = MCTDirectoryArea.class.cast(activeHousing.getDirectoryArea()); MCTMutableTreeNode firstSelectedNode = directory.getSelectedDirectoryNode(); if (firstSelectedNode == null) return false; JTree tree = firstSelectedNode.getParentTree(); selectedTreePaths = tree.getSelectionPaths(); return selectedTreePaths != null && selectedTreePaths.length > 0; } @Override public boolean isEnabled() { for (TreePath path : selectedTreePaths) { if (!isRemovable(path)) return false; } return true; } @Override public void actionPerformed(ActionEvent e) { Map<String, AbstractComponent> toDelete = new HashMap<String, AbstractComponent>(); Map<String, AbstractComponent> toRemove = new HashMap<String, AbstractComponent>(); for (TreePath path : selectedTreePaths) { MCTMutableTreeNode selectedNode = (MCTMutableTreeNode) path.getLastPathComponent(); AbstractComponent selectedComponent = ((View) selectedNode.getUserObject()).getManifestedComponent(); categorizeDescendants(selectedComponent, toDelete, toRemove); } Set<AbstractComponent> cannotRemove = findNonRemovableComponents(toDelete, toRemove); handleWarnings(toDelete.values(), toRemove.values(), cannotRemove); } private void categorizeDescendants(AbstractComponent component, Map<String, AbstractComponent> toDelete, Map<String, AbstractComponent> toRemove) { // Assemble sets by component id String componentId = component.getComponentId(); // Don't travel down cycles if (toDelete.containsKey(componentId) || toRemove.containsKey(componentId)) { return; } // Place into maps according to whether or not the component can be deleted if (component.canBeDeleted()) { toDelete.put(componentId, component); // Since it can be deleted, consider descendants for (AbstractComponent child : component.getComponents()) { categorizeDescendants(child, toDelete, toRemove); } } else { toRemove.put(componentId, component); } } private Set<AbstractComponent> findNonRemovableComponents( Map<String, AbstractComponent> toDelete, Map<String, AbstractComponent> toRemove) { // Assemble a list of components which cannot be deleted Set<AbstractComponent> nonRemovable = new HashSet<AbstractComponent>(); for (AbstractComponent remove : toRemove.values()) { // See if any referencing components are NOT in the list boolean safeToRemove = false; for (AbstractComponent ref : remove.getReferencingComponents()) { String refId = ref.getComponentId(); if (!toDelete.containsKey(refId)) { safeToRemove = true; break; } } if (!safeToRemove) { nonRemovable.add(remove); } } return nonRemovable; } private void handleWarnings(Collection<AbstractComponent> toDelete, Collection<AbstractComponent> toRemove, Collection<AbstractComponent> cannotRemove) { // Get references to platform and window manager; these will be used a few times Platform platform = PlatformAccess.getPlatform(); WindowManager windowManager = platform.getWindowManager(); // Can only complete action if there are no components which cannot be removed if (cannotRemove.isEmpty()) { String confirm = bundle.getString("DeleteAllCoreText"); String abort = bundle.getString("DeleteAllAbortText"); String[] options = { confirm, abort }; // Issue a warning dialog to the user Map<String, Object> hints = new HashMap<String, Object>(); hints.put(WindowManagerImpl.PARENT_COMPONENT, actionContext.getWindowManifestation()); hints.put(WindowManagerImpl.OPTION_TYPE, OptionBox.YES_NO_OPTION); hints.put(WindowManagerImpl.MESSAGE_TYPE, OptionBox.WARNING_MESSAGE); hints.put(WindowManagerImpl.MESSAGE_OBJECT, buildWarningPanel(toDelete, toRemove)); String choice = windowManager.showInputDialog( WARNING, //title "", // message - will be overridden by custom object options, // options null, // default option hints); // hints // Complete the action, if the user has confirmed it if (confirm.equals(choice)) { for (AbstractComponent delete : toDelete) { windowManager.closeWindows(delete.getComponentId()); } platform.getPersistenceProvider().delete(toDelete); } } else { // Some components cannot be removed safely - let the user know this String ok = bundle.getString("DeleteAllErrorConfirm"); Map<String, Object> hints = new HashMap<String, Object>(); hints.put(WindowManagerImpl.PARENT_COMPONENT, actionContext.getWindowManifestation()); hints.put(WindowManagerImpl.MESSAGE_TYPE, OptionBox.ERROR_MESSAGE); windowManager.showInputDialog( "ERROR: "+ WARNING, //title bundle.getString("DeleteAllErrorHasDescendantsText"), // message new String[] { ok }, // options ok, // default option hints); // hints (none) } } private JPanel buildWarningPanel( Collection<AbstractComponent> toDelete, Collection<AbstractComponent> toRemove) { String removeTitle = bundle.getString("DeleteAllRemoveTitle"); String deleteTitle = bundle.getString("DeleteAllDeleteTitle"); String removeMessage = bundle.getString("DeleteAllRemoveWarning"); String deleteMessage = bundle.getString("DeleteAllWarningText"); if (toRemove.isEmpty()) { return buildWarningPanel(deleteTitle, deleteMessage, toDelete); } else { JPanel panel = new JPanel(new BorderLayout()); panel.add(buildWarningPanel(deleteTitle, deleteMessage, toDelete), BorderLayout.WEST); panel.add(buildWarningPanel(removeTitle, removeMessage, toRemove), BorderLayout.EAST); return panel; } } private JPanel buildWarningPanel(String title, String message, Collection<AbstractComponent> componentsToBeDeleted) { List<String> deleteComps = new ArrayList<String>(componentsToBeDeleted.size()); for (AbstractComponent comp : componentsToBeDeleted) { deleteComps.add(comp.getDisplayName()); } JPanel warning = new JPanel(new FlowLayout()); warning.setPreferredSize(new Dimension(400,400)); @SuppressWarnings({ "rawtypes", "unchecked" }) JList deleteList = new JList(deleteComps.toArray()); JScrollPane scrollPane2 = new JScrollPane(deleteList); scrollPane2.setPreferredSize(new Dimension(300,200)); JLabel deletingObjectsLabel = new JLabel(title); deletingObjectsLabel.setPreferredSize(new Dimension(300,20)); deletingObjectsLabel.setVerticalAlignment(SwingConstants.BOTTOM); deletingObjectsLabel.setHorizontalAlignment(SwingConstants.LEFT); JTextArea warningMessage = new JTextArea(message); warningMessage.setWrapStyleWord(true); warningMessage.setLineWrap(true); warningMessage.setOpaque(false); warningMessage.setPreferredSize(new Dimension(300,200)); warningMessage.setEditable(false); warning.add(warningMessage); warning.add(deletingObjectsLabel); warning.add(scrollPane2); return warning; } private boolean isRemovable(TreePath path) { MCTMutableTreeNode lastPathComponent = (MCTMutableTreeNode) path.getLastPathComponent(); AbstractComponent selectedComponent = View.class.cast(lastPathComponent.getUserObject()).getManifestedComponent(); MCTMutableTreeNode parentNode = (MCTMutableTreeNode) lastPathComponent.getParent(); if (parentNode == null) return false; AbstractComponent parentComponent = ((View) parentNode.getUserObject()).getManifestedComponent(); if (!selectedComponent.canBeDeleted()) { return false; } PolicyContext context = new PolicyContext(); context.setProperty(PolicyContext.PropertyName.TARGET_COMPONENT.getName(), parentComponent); context.setProperty(PolicyContext.PropertyName.ACTION.getName(), 'w'); String compositionKey = PolicyInfo.CategoryType.COMPOSITION_POLICY_CATEGORY.getKey(); PolicyManager policyManager = PlatformAccess.getPlatform().getPolicyManager(); return policyManager.execute(compositionKey, context).getStatus(); } }