/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2010-2011, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotoolkit.processing; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Array; import java.util.AbstractMap.SimpleEntry; import java.util.Map.Entry; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.Arrays; import java.util.Iterator; import javax.measure.Unit; import org.apache.sis.internal.util.X364; import org.geotoolkit.process.*; import org.geotoolkit.processing.util.converter.StringToAffineTransformConverter; import org.geotoolkit.processing.util.converter.StringToFeatureCollectionConverter; import org.geotoolkit.processing.util.converter.StringToMapConverter; import org.geotoolkit.processing.util.converter.StringToSortByConverter; import org.geotoolkit.util.StringUtilities; import org.geotoolkit.lang.Setup; import org.apache.sis.util.UnconvertibleObjectException; import org.geotoolkit.processing.util.converter.StringToGeometryConverter; import org.apache.sis.util.collection.Containers; import org.apache.sis.util.Classes; import org.apache.sis.util.ObjectConverters; import org.apache.sis.util.ObjectConverter; import static org.apache.sis.internal.util.X364.*; import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterValue; import org.opengis.parameter.ParameterValueGroup; import org.opengis.util.InternationalString; import org.opengis.parameter.ParameterDescriptor; import org.opengis.metadata.Identifier; import org.opengis.util.NoSuchIdentifierException; /** * Runnable class which dynamically run processes available in the registry. * * @author Johann Sorel (Geomatys) * @module */ public final class ProcessConsole { private static final boolean X364_SUPPORTED = X364.isAnsiSupported(); private static final List LIST_CONVERTERS = Containers.unmodifiableList( StringToFeatureCollectionConverter.getInstance(), StringToGeometryConverter.getInstance(), StringToAffineTransformConverter.getInstance(), StringToSortByConverter.getInstance(), StringToMapConverter.getInstance()); private static boolean failed = false; private static final ProcessListener CONSOLE_ADAPTER = new ProcessListener() { @Override public void started(final ProcessEvent event) { printEvent(event, FOREGROUND_DEFAULT.sequence()); } @Override public void progressing(final ProcessEvent event) { printEvent(event, FOREGROUND_DEFAULT.sequence()); } @Override public void completed(final ProcessEvent event) { printEvent(event, BOLD.sequence()+FOREGROUND_GREEN.sequence()); } @Override public void failed(final ProcessEvent event) { failed = true; printEvent(event, FOREGROUND_RED.sequence()); } @Override public void paused(final ProcessEvent event) { printEvent(event, FOREGROUND_DEFAULT.sequence()); } @Override public void resumed(final ProcessEvent event) { printEvent(event, FOREGROUND_DEFAULT.sequence()); } private void printEvent(final ProcessEvent event, final String color) { final StringBuilder sb = new StringBuilder(); sb.append(color); sb.append(BOLD.sequence()); sb.append(event.getProgress()); sb.append("%\t"); sb.append(RESET.sequence()); sb.append(color); final InternationalString message = event.getTask(); if(message != null){ sb.append(message.toString()); } final Throwable ex = event.getException(); if(ex != null && message == null){ sb.append(FOREGROUND_RED.sequence()); sb.append(ex.getMessage()); sb.append(FOREGROUND_DEFAULT.sequence()); } if(ex != null){ final StringWriter buffer = new StringWriter(); final PrintWriter writer = new PrintWriter(buffer); ex.printStackTrace(writer); writer.flush(); buffer.flush(); final String str = buffer.toString(); sb.append("\n"); sb.append(FOREGROUND_RED.sequence()); sb.append(str); sb.append(FOREGROUND_DEFAULT.sequence()); } sb.append(RESET.sequence()); sb.append("\n"); print(sb.toString()); } }; public static void main(String ... args) { Setup.initialize(null); if(args.length < 1){ globalHelp(); } boolean displayHelp = false; boolean silent = false; String firstArg = args[0]; if("-list".equalsIgnoreCase(firstArg) || "-l".equalsIgnoreCase(firstArg)){ printList(); return; }else if("-help".equalsIgnoreCase(firstArg) || "-h".equalsIgnoreCase(firstArg)){ displayHelp = true; args = Arrays.copyOfRange(args, 1, args.length); }else if("-silent".equalsIgnoreCase(firstArg) || "-s".equalsIgnoreCase(firstArg)){ silent = true; args = Arrays.copyOfRange(args, 1, args.length); } if(args.length < 1){ globalHelp(); } //first argument must be the tool name //can be written 'processName' or 'authority'.'processName' firstArg = args[0]; final String authorityCode; final String processCode; final int index = firstArg.indexOf('.'); if(index != -1){ authorityCode = firstArg.substring(0, index); processCode = firstArg.substring(index+1); }else{ authorityCode = null; processCode = firstArg; } final ProcessDescriptor desc; try{ desc = ProcessFinder.getProcessDescriptor(authorityCode, processCode); }catch(NoSuchIdentifierException ex){ print(FOREGROUND_RED,"Could not find tool for name : ",firstArg,FOREGROUND_DEFAULT,"\n"); return; } if(displayHelp) { printHelp(desc); return; } //parse parameters args = Arrays.copyOfRange(args, 1, args.length); //remove the tool name parameter if (args.length > 0 && ("-help".equalsIgnoreCase(args[0]) || "-h".equalsIgnoreCase(args[0]))) { printHelp(desc); return; } final ParameterValueGroup params; try { params = parseParameters(args, desc.getInputDescriptor()); } catch (Exception ex) { print(FOREGROUND_RED,ex.getLocalizedMessage(),FOREGROUND_DEFAULT,"\n"); System.exit(1); return; } //execute process final org.geotoolkit.process.Process process = desc.createProcess(params); process.addListener(CONSOLE_ADAPTER); final ParameterValueGroup result; try { result = process.call(); } catch (ProcessException ex) { print(FOREGROUND_RED,ex.getLocalizedMessage(),FOREGROUND_DEFAULT,"\n"); System.exit(1); return; } //show result only if in non-silent mode and result have values if(!silent && !desc.getOutputDescriptor().descriptors().isEmpty()){ System.out.println(result); } Setup.shutdown(); if(failed){ System.exit(1); } } /** * Print a list of all tools available. */ private static void printList() { final Iterator<ProcessingRegistry> ite = ProcessFinder.getProcessFactories(); while (ite.hasNext()) { final ProcessingRegistry registry = ite.next(); for(final Identifier id : registry.getIdentification().getCitation().getIdentifiers()){ print(BOLD,id.getCode()," ",RESET); } print(StringUtilities.toStringTree(Arrays.asList(registry.getNames()))); print("\n"); } } /** * Print a detailed description of the tool description. */ private static void printHelp(final ProcessDescriptor desc){ final InternationalString abs = desc.getProcedureDescription(); if(abs != null){ print("\n",BOLD,"DESCRIPTION",RESET,"\n",abs,"\n"); } printDescriptor(desc.getInputDescriptor(),true); printDescriptor(desc.getOutputDescriptor(),false); } /** * Print list of available parameters in the descriptor. */ private static void printDescriptor(final ParameterDescriptorGroup params, final boolean input){ if(params == null){ return; } print("\n"); if(input){ print(BOLD,FOREGROUND_GREEN,">>> INPUT",RESET,"\n"); }else{ print(BOLD,FOREGROUND_YELLOW,"<<< OUTPUT",RESET,"\n"); } for(final GeneralParameterDescriptor pdesc : params.descriptors()){ final Identifier id = pdesc.getName(); final String code = id.getCode(); final int minOcc = pdesc.getMinimumOccurs(); final int maxOcc = pdesc.getMaximumOccurs(); final InternationalString remark = pdesc.getRemarks(); print(BOLD,(input)?"-":"",code,RESET,"\t"); print("(",minOcc,",",maxOcc,")\t"); if(pdesc instanceof ParameterDescriptor){ final ParameterDescriptor d = (ParameterDescriptor) pdesc; final Class clazz = d.getValueClass(); final Set validValues = d.getValidValues(); final Unit unit = d.getUnit(); final Comparable minVal = d.getMinimumValue(); final Comparable maxVal = d.getMaximumValue(); print(Classes.getShortName(clazz)); if(unit != null){ print(" ",unit); } print("\t"); if(validValues != null){ print("{"); for(final Object obj : validValues){ print(obj," "); } print("}\t"); } if(minVal != null || maxVal != null){ String from = ""; String to = ""; if(minVal != null){ from = minVal.toString(); } if(maxVal != null){ to = maxVal.toString(); } print("[ ",from," ... ",to," ]\t"); } } if(remark!=null){ print("\n\t",FAINT,remark,RESET); } print("\n"); } } /** * Parse, convert and set parameter values from the command line arguments. */ private static ParameterValueGroup parseParameters(final String[] args, final ParameterDescriptorGroup desc) throws UnconvertibleObjectException, IllegalArgumentException{ final ParameterValueGroup group = desc.createValue(); //regroup value for each parameter final List<Entry<String,List<String>>> groups = new ArrayList<Entry<String,List<String>>>(); Entry<String,List<String>> current = null; for(String str : args){ if (str.startsWith("-")) { //start a new parameter if(current != null){ groups.add(current); } current = new SimpleEntry<String, List<String>>(str.substring(1), new ArrayList<String>()); continue; } //append to current parameter if(current == null){ throw new IllegalArgumentException("value : "+str+" is not linked to any parameter."); } current.getValue().add(str); } if(current != null){ groups.add(current); } //set parameter values for(final Entry<String,List<String>> entry : groups){ final ParameterValue parameter = group.parameter(entry.getKey()); final Class clazz = parameter.getDescriptor().getValueClass(); final List<String> values = entry.getValue(); final Object converted = toValue(values, clazz); parameter.setValue(converted); } return group; } /** * Convert a List of string values in the appropriate Class. * Possibly an Array or a single value. */ private static <T> Object toValue(final List<String> values, final Class<T> binding) throws UnconvertibleObjectException{ final int size = values.size(); Class baseBinding = binding; if(binding.isArray()){ baseBinding = binding.getComponentType(); } ObjectConverter<? super String, ? extends T> converter = null; try{ converter = ObjectConverters.find(String.class, baseBinding); }catch(UnconvertibleObjectException ex){ for(ObjectConverter conv : (List<ObjectConverter>)LIST_CONVERTERS){ if(conv.getTargetClass().equals(baseBinding)){ converter = conv; } } if(converter == null){ throw ex; } } if(size == 0 && baseBinding == Boolean.class){ //if there is no values after this parameter, it means we set it to true return true; }else if(size == 0){ return null; }else if(size == 1){ //return a single value converted return converter.apply(values.get(0)); }else{ //convert to array of binding class final T[] array = (T[])Array.newInstance(baseBinding,size); for(int i=0;i<size;i++){ array[i] = converter.apply(values.get(i)); } return array; } } private static void globalHelp(){ print(BOLD,"This tool works using three configurations blocks.\n",RESET); print(BOLD,FOREGROUND_MAGENTA,"[global parameters]",RESET); print(BOLD,FOREGROUND_DEFAULT," [tool name] ",RESET); print(BOLD,FOREGROUND_GREEN,"[tool parameters]",RESET,"\n"); print(FOREGROUND_MAGENTA,"-list -l ",RESET," : Display list of available tools.\n"); print(FOREGROUND_MAGENTA,"-help -h ",RESET," : Display help for a tool, must be followed by the tool name.\n"); print(FOREGROUND_MAGENTA,"-silent -s ",RESET," : Silently execute tool (will not show the result).\n"); print(FOREGROUND_DEFAULT,"Tool name ",RESET," : can be authority.name or name alone if unique.\n"); print(FOREGROUND_GREEN,"Tool params ",RESET,": can be found using -help for a given tool.\n"); System.exit(0); } /** * Print in the console the given objects. * X364 object are automatically removed if the console does not handle them. */ private static void print(final Object ... texts){ final String text; if(texts.length == 1){ text = String.valueOf(texts[0]); }else{ final StringBuilder sb = new StringBuilder(); for(Object obj : texts){ if(obj instanceof X364){ if(X364_SUPPORTED){ sb.append( ((X364)obj).sequence() ); } }else{ sb.append(obj); } } text = sb.toString(); } System.out.print(text); } }