/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.visualvm.core.explorer;
import com.sun.tools.visualvm.core.datasource.DataSource;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import org.openide.awt.Mnemonics;
import org.openide.cookies.InstanceCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
/**
* Class responsible for building explorer context menu.
*
* @author Jiri Sedlacek
*/
final class ExplorerContextMenuFactory {
private static final Logger LOGGER = Logger.getLogger(ExplorerContextMenuFactory.class.getName());
private static final String SELECTION_ACTIONS_FILE = "VisualVM/ExplorerPopupSelection"; // NOI18N
private static final String NOSELECTION_ACTIONS_FILE = "VisualVM/ExplorerPopupNoSelection"; // NOI18N
private static ExplorerContextMenuFactory sharedInstance;
/**
* Returns singleton instance of ExplorerContextMenuFactory.
*
* @return singleton instance of ExplorerContextMenuFactory.
*/
static synchronized ExplorerContextMenuFactory instance() {
if (sharedInstance == null) sharedInstance = new ExplorerContextMenuFactory();
return sharedInstance;
}
JPopupMenu createPopupMenu() {
// Get actions for the node
List<Action>[] actionsArray = getActions();
List<Action> defaultActions = actionsArray[0];
List<Action> actions = actionsArray[1];
// Return if there are no actions to display
if (defaultActions.size() == 0 && actions.size() == 0) return null;
// Create a popup menu
JPopupMenu popupMenu = new JPopupMenu();
// Insert default actions
boolean realDefaultAction = true;
if (!defaultActions.isEmpty()) {
for (Action defaultAction : defaultActions) {
JMenuItem defaultItem = new DataSourceItem(defaultAction);
if (realDefaultAction) {
defaultItem.setFont(defaultItem.getFont().deriveFont(Font.BOLD));
realDefaultAction = false;
}
popupMenu.add(defaultItem);
}
}
// Insert separator between default action and other actions
if (!defaultActions.isEmpty() && !actions.isEmpty()) popupMenu.addSeparator();
// Insert other actions
if (!actions.isEmpty()) {
for (Action action : actions) {
if (action == null) popupMenu.addSeparator();
else popupMenu.add(new DataSourceItem(action));
}
}
return popupMenu;
}
Action getDefaultActionFor(Set<DataSource> dataSources) {
if (dataSources.isEmpty()) return null;
List<Action> defaultActions = getActions()[0];
return defaultActions.isEmpty() ? null : defaultActions.get(0);
}
private List<Action>[] getActions() {
if (ExplorerSupport.sharedInstance().getSelectedDataSources().isEmpty())
return getNoSelectionActions();
else
return getSelectionActions();
}
private List<Action>[] getSelectionActions() {
// Find entrypoint into layer
FileObject actionsFO = FileUtil.getConfigFile(SELECTION_ACTIONS_FILE);
return getActions(actionsFO, true);
}
private List<Action>[] getNoSelectionActions() {
// Find entrypoint into layer
FileObject actionsFO = FileUtil.getConfigFile(NOSELECTION_ACTIONS_FILE);
return getActions(actionsFO, false);
}
private List<Action>[] getActions(FileObject actionsFO, boolean allowDefaultActions) {
// Init caches for default and regular context menu actions
List<Action> defaultActions = new ArrayList();
List<Action> actions = new ArrayList();
if (actionsFO != null) {
DataFolder actionsDF = DataFolder.findFolder(actionsFO);
DataObject[] menuItems = actionsDF.getChildren();
for (DataObject menuItemDO : menuItems) {
FileObject fobj = menuItemDO.getPrimaryFile();
if (fobj.isFolder()) {
LOGGER.log(Level.WARNING, "Nested menus not supported for Applications context menu: " + fobj, fobj); // NOI18N
} else {
InstanceCookie menuItemCookie = (InstanceCookie)menuItemDO.getCookie(InstanceCookie.class);
try {
Object menuItem = menuItemCookie.instanceCreate();
boolean isDefaultAction = false;
Object isDefaultActionObj = fobj.getAttribute("default"); // NOI18N
if (isDefaultActionObj != null) try {
isDefaultAction = (Boolean)isDefaultActionObj;
if (!allowDefaultActions && isDefaultAction)
LOGGER.log(Level.WARNING, "Default actions not supported for " + actionsFO.getPath() + ": " + menuItem, menuItem); // NOI18N
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Cannot determine whether context menu action is default: " + isDefaultActionObj, isDefaultActionObj); // NOI18N
}
List<Action> actionsList = isDefaultAction ? defaultActions : actions;
if (menuItem instanceof Action) {
Action action = (Action)menuItem;
if (action.isEnabled()) actionsList.add(action);
} else if (menuItem instanceof JSeparator) {
if (isDefaultAction) {
LOGGER.log(Level.WARNING, "Separator cannot be added to default actions " + menuItem, menuItem); // NOI18N
} else {
actionsList.add(null);
}
} else {
LOGGER.log(Level.WARNING, "Unsupported context menu item: " + menuItem, menuItem); // NOI18N
}
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Unable to resolve context menu action: " + menuItemDO, menuItemDO); // NOI18N
}
}
}
}
// Return actions
return new List[] { cleanupActions(defaultActions), cleanupActions(actions) };
}
private List<Action> cleanupActions(List<Action> actions) {
boolean leadingNull = true;
Action lastAction = null;
List<Action> cleanActions = new ArrayList();
for (Action action : actions) {
if (action == null) {
if (!leadingNull && lastAction != null)
cleanActions.add(null);
} else {
cleanActions.add(action);
leadingNull = false;
}
lastAction = action;
}
if (!cleanActions.isEmpty()) {
int lastItemIndex = cleanActions.size() - 1;
Action lastCleanAction = cleanActions.get(lastItemIndex);
if (lastCleanAction == null) cleanActions.remove(lastItemIndex);
}
return cleanActions;
}
private static class DataSourceItem extends JMenuItem {
public DataSourceItem(Action action) {
super(action);
setIcon(null);
setToolTipText(null);
String name = (String)action.getValue(Action.NAME);
if (name != null) Mnemonics.setLocalizedText(this, name); // NOI18N
}
}
}