/*
* uDig - User Friendly Desktop Internet GIS client
* (C) HydroloGIS - www.hydrologis.com
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the HydroloGIS BSD
* License v1.0 (http://udig.refractions.net/files/hsd3-v10.html).
*/
package org.locationtech.udig.omsbox.core;
import java.io.File;
import java.io.PrintStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.locationtech.udig.catalog.CatalogPlugin;
import org.locationtech.udig.catalog.IGeoResource;
import org.locationtech.udig.catalog.IService;
import org.locationtech.udig.catalog.IServiceFactory;
import org.locationtech.udig.catalog.URLUtils;
import org.locationtech.udig.project.IMap;
import org.locationtech.udig.project.internal.Layer;
import org.locationtech.udig.project.internal.LayerFactory;
import org.locationtech.udig.project.internal.commands.AddLayerCommand;
import org.locationtech.udig.project.ui.ApplicationGIS;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.gce.grassraster.JGrassConstants;
import org.locationtech.udig.catalog.jgrass.core.JGrassMapGeoResource;
import org.locationtech.udig.catalog.jgrass.core.JGrassService;
import org.locationtech.udig.catalog.jgrass.utils.JGrassCatalogUtilities;
import org.locationtech.udig.omsbox.OmsBoxPlugin;
import org.locationtech.udig.omsbox.utils.OmsBoxConstants;
import org.locationtech.udig.omsbox.utils.OmsBoxUtils;
import org.locationtech.udig.omsbox.view.widgets.ModuleGui;
import org.locationtech.udig.omsbox.view.widgets.ModuleGuiElement;
/**
* Handler for script generation from gui and script execution.
*
* @author Andrea Antonello (www.hydrologis.com)
*/
@SuppressWarnings("nls")
public class ScriptHandler {
private static final String QUOTE = "'";
private AtomicInteger counter = new AtomicInteger();
/**
* Map of variables bound to their {@link ModuleDescription}.
*/
private HashMap<ModuleDescription, String> variableNamesMap = new HashMap<ModuleDescription, String>();
private ModuleDescription mainModuleDescription;
/**
* Generates the script from the supplied gui.
*
* @param moduleGui the gui object for which to generate the script.
* @return the oms3 script.
* @throws Exception
*/
public String genereateScript( ModuleGui moduleGui ) throws Exception {
variableNamesMap.clear();
mainModuleDescription = moduleGui.getModuleDescription();
// input
List<ModuleGuiElement> modulesInputGuiList = moduleGui.getModulesInputGuiList();
// output
List<ModuleGuiElement> modulesOutputGuiList = moduleGui.getModulesOuputGuiList();
StringBuilder sb = new StringBuilder();
for( ModuleGuiElement mgElem : modulesInputGuiList ) {
String res = mgElem.validateContent();
if (res != null) {
sb.append(res);
}
}
for( ModuleGuiElement mgElem : modulesOutputGuiList ) {
String res = mgElem.validateContent();
if (res != null) {
sb.append(res);
}
}
if (sb.length() > 0) {
Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell();
MessageDialog.openWarning(shell, "WARNING", "The following problems were reported\n" + sb.toString());
return null;
}
String loggerLevelGui = OmsBoxPlugin.getDefault().retrieveSavedLogLevel();
String loggerLevelOms = OmsBoxConstants.LOGLEVELS_MAP.get(loggerLevelGui);
StringBuilder scriptSb = new StringBuilder();
scriptSb.append("def simulation = new oms3.SimBuilder(logging:'" + loggerLevelOms + "').sim(name:");
scriptSb.append(QUOTE);
scriptSb.append(mainModuleDescription.getName());
scriptSb.append(QUOTE);
scriptSb.append(") {\n\n");
scriptSb.append("model {\n\n");
/*
* first get all the components
*/
StringBuilder componentsSb = new StringBuilder();
componentsSb.append("components {\n");
componentsSb.append(module2ComponenDescription(mainModuleDescription));
for( ModuleGuiElement inElement : modulesInputGuiList ) {
if (!inElement.hasData()) {
continue;
}
FieldData fieldData = inElement.getFieldData();
if (fieldData.otherModule != null) {
String componentDescription = module2ComponenDescription(fieldData.otherModule);
componentsSb.append(componentDescription);
}
}
for( ModuleGuiElement outElement : modulesOutputGuiList ) {
if (!outElement.hasData()) {
continue;
}
FieldData fieldData = outElement.getFieldData();
if (fieldData.otherModule != null) {
String componentDescription = module2ComponenDescription(fieldData.otherModule);
componentsSb.append(componentDescription);
}
}
componentsSb.append("}\n\n");
scriptSb.append(componentsSb.toString());
/*
* then gather all the input component's fields
*/
StringBuilder parametersSb = new StringBuilder();
parametersSb.append("parameter {\n");
for( ModuleGuiElement inElement : modulesInputGuiList ) {
if (!inElement.hasData()) {
continue;
}
FieldData fieldData = inElement.getFieldData();
field2ParameterDescription(fieldData, mainModuleDescription, parametersSb);
}
for( ModuleGuiElement outElement : modulesOutputGuiList ) {
if (!outElement.hasData()) {
continue;
}
FieldData fieldData = outElement.getFieldData();
field2ParameterDescription(fieldData, mainModuleDescription, parametersSb);
}
parametersSb.append("}\n\n");
scriptSb.append(parametersSb.toString());
/*
* and finally create all the connections
*/
StringBuilder connectionsSb = new StringBuilder();
connectionsSb.append("connect {\n");
for( ModuleGuiElement inElement : modulesInputGuiList ) {
if (!inElement.hasData()) {
continue;
}
FieldData fieldData = inElement.getFieldData();
connectInputModules(mainModuleDescription, fieldData, connectionsSb);
}
for( ModuleGuiElement outElement : modulesOutputGuiList ) {
if (!outElement.hasData()) {
continue;
}
FieldData fieldData = outElement.getFieldData();
connectOutputModules(mainModuleDescription, fieldData, connectionsSb);
}
connectionsSb.append("}\n\n");
scriptSb.append(connectionsSb.toString());
scriptSb.append("}\n");
scriptSb.append("}\n");
scriptSb.append("result = simulation.run();\n\n");
dumpSimpleOutputs(scriptSb, mainModuleDescription);
return scriptSb.toString();
}
/**
* Runs a script.
*
* @param scriptId the unique id that will be used to identify the process of the run script.
* @param script the script.
*/
public void runModule( final String scriptId, String script ) {
JConsoleOutputConsole outputConsole = new JConsoleOutputConsole(scriptId);
outputConsole.clearConsole();
PrintStream internalStream = outputConsole.internal;
// PrintStream outputStream = outputConsole.out;
PrintStream errorStream = outputConsole.err;
// open console
IConsoleManager manager = org.eclipse.ui.console.ConsolePlugin.getDefault().getConsoleManager();
manager.addConsoles(new IConsole[]{outputConsole});
manager.showConsoleView(outputConsole);
try {
OmsScriptExecutor executor = new OmsScriptExecutor();
executor.addProcessListener(new IProcessListener(){
public void onProcessStopped() {
OmsBoxPlugin.getDefault().cleanProcess(scriptId);
loadOutputMaps();
}
});
String loggerLevelGui = OmsBoxPlugin.getDefault().retrieveSavedLogLevel();
String ramLevel = String.valueOf(OmsBoxPlugin.getDefault().retrieveSavedHeap());
Process process = executor.exec(script, internalStream, errorStream, loggerLevelGui, ramLevel);
OmsBoxPlugin.getDefault().addProcess(process, scriptId);
} catch (Exception e) {
e.printStackTrace();
}
}
private void loadOutputMaps() {
List<FieldData> outputsList = mainModuleDescription.getOutputsList();
List<FieldData> InputsList = mainModuleDescription.getInputsList();
for( FieldData fieldData : outputsList ) {
if (fieldData.fieldType.equals(GridCoverage2D.class.getCanonicalName())
|| fieldData.fieldType.equals(SimpleFeatureCollection.class.getCanonicalName())) {
if (fieldData.otherModule != null) {
List<FieldData> inputList2 = fieldData.otherModule.getInputsList();
for( FieldData fieldData2 : inputList2 ) {
String filePath = fieldData2.fieldValue;
File file = new File(filePath);
try {
if (file.exists())
loadFileInMap(file);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
for( FieldData fieldData : InputsList ) {
if (fieldData.guiHints == null)
continue;
if (fieldData.fieldType.equals(String.class.getCanonicalName())
&& fieldData.guiHints.contains(OmsBoxConstants.FILEOUT_UI_HINT) && fieldData.fieldValue != null
&& fieldData.fieldValue.length() > 0) {
String filePath = fieldData.fieldValue;
File file = new File(filePath);
try {
if (file.exists())
loadFileInMap(file);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void loadFileInMap( File file ) throws Exception {
URL fileUrl = file.toURI().toURL();
CatalogPlugin cp = CatalogPlugin.getDefault();
IServiceFactory sf = cp.getServiceFactory();
List<IService> services = sf.createService(fileUrl);
List<IGeoResource> resources = new ArrayList<IGeoResource>();
for( IService service : services ) {
List< ? extends IGeoResource> geoResource = service.resources(new NullProgressMonitor());
if (geoResource != null) {
if (service instanceof JGrassService) {
for( IGeoResource iGeoResource : geoResource ) {
URL identifier = iGeoResource.getIdentifier();
File newFile = URLUtils.urlToFile(identifier);
if (file.getName().equals(newFile.getName())) {
File parentFile = file.getParentFile();
File newParentFile = newFile.getParentFile();
if (parentFile != null && newParentFile != null
&& parentFile.getName().equals(newParentFile.getName())) {
parentFile = parentFile.getParentFile();
newParentFile = newParentFile.getParentFile();
if (parentFile != null && newParentFile != null) {
if (parentFile.getName().equals(newParentFile.getName())) {
resources.add(iGeoResource);
}
} else {
resources.add(iGeoResource);
}
}
}
}
} else {
for( IGeoResource iGeoResource : geoResource ) {
resources.add(iGeoResource);
}
}
}
}
if (resources.size() == 0) {
return;
}
IMap map = ApplicationGIS.getActiveMap();
LayerFactory layerFactory = map.getLayerFactory();
for( IGeoResource resource : resources ) {
if (resource instanceof JGrassMapGeoResource) {
JGrassMapGeoResource grassMGR = (JGrassMapGeoResource) resource;
File locationFile = grassMGR.getLocationFile();
File mapsetFile = grassMGR.getMapsetFile();
File mapFile = grassMGR.getMapFile();
IGeoResource addedMapToCatalog = JGrassCatalogUtilities.addMapToCatalog(locationFile.getAbsolutePath(),
mapsetFile.getName(), mapFile.getName(), JGrassConstants.GRASSBINARYRASTERMAP);
int index = map.getMapLayers().size();
ApplicationGIS.addLayersToMap(map, Arrays.asList(addedMapToCatalog), index);
} else {
Layer layer = layerFactory.createLayer(resource);
AddLayerCommand cmd = new AddLayerCommand(layer);
map.sendCommandASync(cmd);
}
}
}
/**
* Adds to the script a tail part to dump the outputs that are not connected to any module.
*
* <p>These outputs are for example single double values or
* arrays and matrixes of numbers.
*
* @param scriptSb the script {@link StringBuilder} to which to add the dump commands.
* @param mainModuleDescription the main {@link ModuleDescription module}.
*/
private void dumpSimpleOutputs( StringBuilder scriptSb, ModuleDescription mainModuleDescription ) {
// make print whatever is simple output
String mainVarName = variableNamesMap.get(mainModuleDescription);
List<FieldData> outputsList = mainModuleDescription.getOutputsList();
for( FieldData fieldData : outputsList ) {
if (fieldData.isSimpleType()) {
scriptSb.append("println \"");
scriptSb.append(fieldData.fieldDescription);
scriptSb.append(" = \" + result.");
scriptSb.append(mainVarName);
scriptSb.append(".");
scriptSb.append(fieldData.fieldName);
scriptSb.append("\n");
}
}
// in case make print double[] and double[][] outputs
scriptSb.append("println \" \"\n\n");
for( FieldData fieldData : outputsList ) {
if (fieldData.isSimpleArrayType()) {
if (fieldData.fieldType.equals(double[][].class.getCanonicalName())
|| fieldData.fieldType.equals(float[][].class.getCanonicalName())
|| fieldData.fieldType.equals(int[][].class.getCanonicalName())) {
String typeStr = null;
if (fieldData.fieldType.equals(double[][].class.getCanonicalName())) {
typeStr = "double[][]";
} else if (fieldData.fieldType.equals(float[][].class.getCanonicalName())) {
typeStr = "float[][]";
} else if (fieldData.fieldType.equals(int[][].class.getCanonicalName())) {
typeStr = "int[][]";
}
scriptSb.append("println \"");
scriptSb.append(fieldData.fieldDescription);
scriptSb.append("\"\n");
scriptSb.append("println \"-----------------------------------\"\n");
scriptSb.append(typeStr);
scriptSb.append(" matrix = result.");
scriptSb.append(mainVarName);
scriptSb.append(".");
scriptSb.append(fieldData.fieldName);
scriptSb.append("\n");
scriptSb.append("for( int i = 0; i < matrix.length; i++ ) {\n");
scriptSb.append("for( int j = 0; j < matrix[0].length; j++ ) {\n");
scriptSb.append("print matrix[i][j] + \" \";\n");
scriptSb.append("}\n");
scriptSb.append("println \" \";\n");
scriptSb.append("}\n");
scriptSb.append("\n");
} else if (fieldData.fieldType.equals(double[].class.getCanonicalName())
|| fieldData.fieldType.equals(float[].class.getCanonicalName())
|| fieldData.fieldType.equals(int[].class.getCanonicalName())) {
String typeStr = null;
if (fieldData.fieldType.equals(double[].class.getCanonicalName())) {
typeStr = "double[]";
} else if (fieldData.fieldType.equals(float[].class.getCanonicalName())) {
typeStr = "float[]";
} else if (fieldData.fieldType.equals(int[].class.getCanonicalName())) {
typeStr = "int[]";
}
scriptSb.append("println \"");
scriptSb.append(fieldData.fieldDescription);
scriptSb.append("\"\n");
scriptSb.append("println \"-----------------------------------\"\n");
scriptSb.append(typeStr);
scriptSb.append(" array = result.");
scriptSb.append(mainVarName);
scriptSb.append(".");
scriptSb.append(fieldData.fieldName);
scriptSb.append("\n");
scriptSb.append("for( int i = 0; i < array.length; i++ ) {\n");
scriptSb.append("println array[i] + \" \";\n");
scriptSb.append("}\n");
scriptSb.append("\n");
}
scriptSb.append("println \" \"\n\n");
}
}
}
/**
* Converts a module to its oms3 script description as needed in the modules part.
*
* @param moduleDescription the main {@link ModuleDescription module}.
* @return the string describing the module in oms3 syntax.
*/
private String module2ComponenDescription( ModuleDescription moduleDescription ) {
StringBuilder sb = new StringBuilder();
String varName = moduleDescription.getScriptName() + counter.getAndIncrement();
variableNamesMap.put(moduleDescription, varName);
sb.append("\t");
sb.append(QUOTE);
sb.append(varName);
sb.append(QUOTE);
sb.append("\t\t");
sb.append(QUOTE);
sb.append(moduleDescription.getScriptName());
sb.append(QUOTE);
sb.append("\n");
return sb.toString();
}
/**
* Converts module fields to their oms3 script description as needed in the parameters part.
*
* @param field
* @param mainModuleDescription
* @param sb
*/
private void field2ParameterDescription( FieldData field, ModuleDescription mainModuleDescription, StringBuilder sb ) {
if (field.otherModule == null && field.fieldValue != null && field.fieldValue.length() > 0) {
String TMPQUOTE = QUOTE;
if (OmsBoxUtils.isFieldExceptional(field)) {
TMPQUOTE = "";
}
if (field.guiHints != null && field.guiHints.contains(OmsBoxConstants.MULTILINE_UI_HINT)) {
TMPQUOTE = "\"\"\"";
}
sb.append("\t");
sb.append(QUOTE);
sb.append(variableNamesMap.get(mainModuleDescription));
sb.append(".");
sb.append(field.fieldName);
sb.append(QUOTE);
sb.append("\t\t");
String fieldValue = field.fieldValue;
sb.append(TMPQUOTE);
sb.append(fieldValue);
sb.append(TMPQUOTE);
sb.append("\n");
} else if (field.otherModule != null) {
ModuleDescription otherModule = field.otherModule;
List<FieldData> inputsList = otherModule.getInputsList();
for( FieldData fieldData : inputsList ) {
if (fieldData.isSimpleType()) {
field2ParameterDescription(fieldData, otherModule, sb);
} else if (OmsBoxUtils.isFieldExceptional(fieldData)) {
field2ParameterDescription(fieldData, otherModule, sb);
}
}
List<FieldData> outputList = otherModule.getOutputsList();
for( FieldData fieldData : outputList ) {
if (fieldData.isSimpleType())
field2ParameterDescription(fieldData, otherModule, sb);
}
}
}
/**
* Connects input modules in OMS3 script syntax.
*
* @param mainModule
* @param inData
* @param sb
*/
private void connectInputModules( ModuleDescription mainModule, FieldData inData, StringBuilder sb ) {
if (inData.otherModule == null) {
return;
}
ModuleDescription otherModule = inData.otherModule;
sb.append("\t");
sb.append(QUOTE);
sb.append(variableNamesMap.get(otherModule));
sb.append(".");
sb.append(inData.otherFieldName);
sb.append(QUOTE);
sb.append("\t\t");
sb.append(QUOTE);
sb.append(variableNamesMap.get(mainModule));
sb.append(".");
sb.append(inData.fieldName);
sb.append(QUOTE);
sb.append("\n");
}
/**
* Connects output modules in OMS3 script syntax.
*
* @param mainModule
* @param outData
* @param sb
*/
private void connectOutputModules( ModuleDescription mainModule, FieldData outData, StringBuilder sb ) {
if (outData.otherModule == null) {
return;
}
ModuleDescription otherModule = outData.otherModule;
sb.append("\t");
sb.append(QUOTE);
sb.append(variableNamesMap.get(mainModule));
sb.append(".");
sb.append(outData.fieldName);
sb.append(QUOTE);
sb.append("\t\t");
sb.append(QUOTE);
sb.append(variableNamesMap.get(otherModule));
sb.append(".");
sb.append(outData.otherFieldName);
sb.append(QUOTE);
sb.append("\n");
}
}