/*
*
* * Copyright (C) 2015 CS SI
* *
* * This program is free software; you can redistribute it and/or modify it
* * under the terms of the GNU General Public License as published by the Free
* * Software Foundation; either version 3 of the License, or (at your option)
* * any later version.
* * This program 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 for
* * more details.
* *
* * You should have received a copy of the GNU General Public License along
* * with this program; if not, see http://www.gnu.org/licenses/
*
*/
package org.esa.snap.ui.tooladapter.actions;
import org.esa.snap.core.gpf.GPF;
import org.esa.snap.core.gpf.OperatorSpi;
import org.esa.snap.core.gpf.OperatorSpiRegistry;
import org.esa.snap.core.gpf.descriptor.OperatorDescriptor;
import org.esa.snap.core.gpf.descriptor.ToolAdapterOperatorDescriptor;
import org.esa.snap.core.gpf.operators.tooladapter.ToolAdapterIO;
import org.esa.snap.core.gpf.operators.tooladapter.ToolAdapterListener;
import org.esa.snap.core.gpf.operators.tooladapter.ToolAdapterOpSpi;
import org.esa.snap.core.gpf.operators.tooladapter.ToolAdapterRegistry;
import org.esa.snap.rcp.util.Dialogs;
import org.esa.snap.utils.AdapterWatcher;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.modules.OnStart;
import org.openide.modules.OnStop;
import org.openide.modules.Places;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
/**
* Helper class for creating menu entries for tool adapter operators.
* The inner runnable class should be invoked when the IDE starts, and will
* register the available adapters as menu actions.
* TODO: Delegate registration of SPIs to snap-sta. Register actions only for what was already registered
* @author Cosmin Cara
*/
public class ToolAdapterActionRegistrar {
private static final String DEFAULT_MENU_PATH = "Menu/Tools/External Tools";
private static final Map<String, ToolAdapterOperatorDescriptor> actionMap = new HashMap<>();
private static final ToolAdapterListener listener = new ToolAdapterListener() {
@Override
public void adapterAdded(ToolAdapterOperatorDescriptor operatorDescriptor) {
registerOperatorMenu(operatorDescriptor);
}
@Override
public void adapterRemoved(ToolAdapterOperatorDescriptor operatorDescriptor) {
removeOperatorMenu(operatorDescriptor);
}
};
/**
* Returns the map of menu items (actions) and operator descriptors.
*
* @return
*/
public static Map<String, ToolAdapterOperatorDescriptor> getActionMap() {
return actionMap;
}
public static String getDefaultMenuLocation() {
return DEFAULT_MENU_PATH;
}
/**
* Creates a menu entry in the default menu location (Tools > External Tools) for the given adapter operator.
*
* @param operator The operator descriptor
*/
public static void registerOperatorMenu(ToolAdapterOperatorDescriptor operator) {
String menuGroup = operator.getMenuLocation();
if (menuGroup == null) {
operator.setMenuLocation(DEFAULT_MENU_PATH);
}
registerOperatorMenu(operator, true);
}
/**
* Creates a menu entry in the given menu location for the given adapter operator.
*
* @param operator The operator descriptor
* @param hasChanged Flag that indicates if the descriptor has changed (true) or is new (false)
*/
public static void registerOperatorMenu(ToolAdapterOperatorDescriptor operator, boolean hasChanged) {
String menuLocation = operator.getMenuLocation();
try {
getDefaultLocation();
} catch (IOException e) {
e.printStackTrace();
}
if (menuLocation == null) {
menuLocation = getDefaultMenuLocation();
operator.setMenuLocation(menuLocation);
}
FileObject menuFolder = FileUtil.getConfigFile(menuLocation);
try {
if (menuFolder == null) {
menuFolder = FileUtil.getConfigFile("Menu");
String[] menuTokens = menuLocation.split("/");
for (int i = 1; i < menuTokens.length; i++) {
FileObject subMenu = menuFolder.getFileObject(menuTokens[i]);
if (subMenu == null) {
menuFolder = menuFolder.createFolder(menuTokens[i]);
} else {
menuFolder = subMenu;
}
}
menuFolder.setAttribute("position", 9999);
}
String menuKey = operator.getAlias();
FileObject newItem = menuFolder.getFileObject(menuKey, "instance");
if (newItem == null) {
newItem = menuFolder.createData(menuKey, "instance");
}
ExecuteToolAdapterAction action = new ExecuteToolAdapterAction(menuKey);
newItem.setAttribute("instanceCreate", action);
newItem.setAttribute("instanceClass", action.getClass().getName());
if (actionMap.containsKey(menuKey)) {
actionMap.remove(menuKey);
}
actionMap.put(menuKey, operator);
} catch (IOException e) {
Dialogs.showError("Error:" + e.getMessage());
}
}
public static void removeOperatorMenu(ToolAdapterOperatorDescriptor operator) {
//if (!operator.isFromPackage()) {
FileObject menuFolder = FileUtil.getConfigFile(operator.getMenuLocation());
try {
if (menuFolder != null) {
String operatorAlias = operator.getAlias();
FileObject newItem = menuFolder.getFileObject(operatorAlias, "instance");
if (newItem != null) {
newItem.delete();
}
if (actionMap.containsKey(operatorAlias)) {
actionMap.remove(operatorAlias);
}
}
FileObject defaultLocation = getDefaultLocation();
FileObject[] children = defaultLocation.getChildren();
if (children == null || children.length == 0) {
defaultLocation.delete();
}
} catch (IOException e) {
Dialogs.showError("Error:" + e.getMessage());
}
//}
}
private static FileObject getDefaultLocation() throws IOException {
FileObject defaultMenu = FileUtil.getConfigFile(DEFAULT_MENU_PATH);
if (defaultMenu == null) {
defaultMenu = FileUtil.getConfigFile("Menu").getFileObject("Tools");
defaultMenu = defaultMenu.createFolder(DEFAULT_MENU_PATH.replace("Menu/Tools/", ""));
FileObject[] objects = defaultMenu.getParent().getChildren();
int position = 9999;
Object value = objects[objects.length - 1].getAttribute("position");
if (value != null) {
position = ((Integer) value) + 1;
}
defaultMenu.setAttribute("position", position);
}
return defaultMenu;
}
/**
* Startup class that performs menu initialization to be invoked by NetBeans.
*/
@OnStart
public static class StartOp implements Runnable {
@Override
public void run() {
OperatorSpiRegistry spiRegistry = GPF.getDefaultInstance().getOperatorSpiRegistry();
Path jarPaths = Paths.get(Places.getUserDirectory().getAbsolutePath(), "modules");
Map<String, File> jarAdapters = Files.exists(jarPaths) ? getJarAdapters(jarPaths.toFile()) : null;
if (spiRegistry != null) {
Collection<OperatorSpi> operatorSpis = spiRegistry.getOperatorSpis();
if (operatorSpis != null) {
if (operatorSpis.size() == 0) {
operatorSpis.addAll(ToolAdapterIO.searchAndRegisterAdapters());
}
/*final List<OperatorSpi> orphaned = operatorSpis.stream()
.filter(spi -> spi instanceof ToolAdapterOpSpi &&
((ToolAdapterOperatorDescriptor) spi.getOperatorDescriptor()).isFromPackage() &&
jarAdapters != null && !jarAdapters.containsKey(spi.getOperatorDescriptor().getAlias()))
.collect(Collectors.toList());
orphaned.forEach(spi -> {
ToolAdapterOperatorDescriptor operatorDescriptor = (ToolAdapterOperatorDescriptor) spi.getOperatorDescriptor();
operatorSpis.remove(spi);
//ToolAdapterActionRegistrar.removeOperatorMenu(operatorDescriptor);
ToolAdapterIO.removeOperator(operatorDescriptor);
});*/
operatorSpis.stream().filter(spi -> spi instanceof ToolAdapterOpSpi).forEach(spi -> {
OperatorDescriptor descriptor = spi.getOperatorDescriptor();
if (descriptor instanceof ToolAdapterOperatorDescriptor) {
registerOperatorMenu((ToolAdapterOperatorDescriptor) descriptor, false);
}
});
}
ToolAdapterRegistry.INSTANCE.addListener(listener);
AdapterWatcher.INSTANCE.startMonitor();
}
}
private Map<String, File> getJarAdapters(File fromPath) {
Map<String, File> output = new HashMap<>();
if (fromPath != null && fromPath.exists()) {
String descriptionKeyName = "OpenIDE-Module-Short-Description";
Attributes.Name typeKey = new Attributes.Name("OpenIDE-Module-Type");
File[] files = fromPath.listFiles((dir, name) -> name.endsWith("jar"));
if (files != null) {
try {
for (File file : files) {
JarFile jarFile = new JarFile(file);
Manifest manifest = jarFile.getManifest();
if (manifest != null) {
Attributes manifestEntries = manifest.getMainAttributes();
if (manifestEntries.containsKey(typeKey) &&
"STA".equals(manifestEntries.getValue(typeKey.toString()))) {
output.put(manifestEntries.getValue(descriptionKeyName), file);
}
}
}
} catch (Exception ignored) {
}
}
}
return output;
}
}
@OnStop
public static class StopOp implements Runnable {
@Override
public void run() {
AdapterWatcher.INSTANCE.stopMonitor();
}
}
}