package nl.armatiek.xslweb.saxon.functions.exec;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteResultHandler;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.Executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.BooleanValue;
import net.sf.saxon.value.EmptySequence;
import net.sf.saxon.value.Int64Value;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;
import nl.armatiek.xslweb.configuration.Definitions;
import nl.armatiek.xslweb.saxon.functions.ExtensionFunctionCall;

/**
 * 
 * 
 * @author Maarten Kroon
 */
public class ExecExternal extends ExtensionFunctionDefinition {
  
  private static final Logger logger = LoggerFactory.getLogger(ExecExternal.class);

  private static final StructuredQName qName = new StructuredQName("", Definitions.NAMESPACEURI_XSLWEB_FX_EXEC, "exec-external");

  @Override
  public StructuredQName getFunctionQName() {
    return qName;
  }

  @Override
  public int getMinimumNumberOfArguments() {
    return 2;
  }

  @Override
  public int getMaximumNumberOfArguments() {
    return 5;
  }

  @Override
  public SequenceType[] getArgumentTypes() {
    // commandline, (args), exitValue, watchdog, async
    return new SequenceType[] { 
        SequenceType.SINGLE_STRING, 
        SequenceType.STRING_SEQUENCE,
        SequenceType.SINGLE_INTEGER,
        SequenceType.SINGLE_INTEGER,
        SequenceType.SINGLE_BOOLEAN};
  }

  @Override
  public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
    return SequenceType.OPTIONAL_INTEGER;
  }
  
  @Override
  public ExtensionFunctionCall makeCallExpression() {    
    return new ExecExternalCall();
  }

  private static class ExecExternalCall extends ExtensionFunctionCall {
    
    @Override
    public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {                                  
      final CommandLine cmdLine = new CommandLine(((StringValue) arguments[0].head()).getStringValue());      
      SequenceIterator args = arguments[1].iterate();      
      Item arg;
      while ((arg = args.next()) != null) {
        cmdLine.addArgument(((StringValue) arg).getStringValue());
      }            
      Executor executor = new DefaultExecutor();
      Item exitValue;
      if ((exitValue = arguments[2].head()) != null) {
        executor.setExitValue((int) ((Int64Value) exitValue).longValue());
      }
      Item timeout;
      if ((timeout = arguments[3].head()) != null) {
        ExecuteWatchdog watchdog = new ExecuteWatchdog(((Int64Value) timeout).longValue());
        executor.setWatchdog(watchdog);                        
      }
      try {
        Item async;
        if ((async = arguments[4].head()) != null && ((BooleanValue) async).getBooleanValue()) {                    
          executor.execute(cmdLine, new ExecuteResultHandler() {
            @Override
            public void onProcessComplete(int exitValue) {
              logger.debug("External process \"" + cmdLine.toString() + "\" completed with exit value \"" + exitValue + "\"");
            }

            @Override
            public void onProcessFailed(ExecuteException e) {
              logger.error("External process \"" + cmdLine.toString() + "\" failed with exit value \"" + e.getExitValue() + "\"", e);
            }           
          });                
          return EmptySequence.getInstance();          
        } else {
          int result = executor.execute(cmdLine);        
          return Int64Value.makeIntegerValue(result);
        }
      } catch (Exception e) {
        throw new XPathException("Error executing external process", e);
      }
    }
  }
  
}
@@ -384,16 +384,57 @@
 ctory;%0A%0A
+import net.sf.saxon.expr.StaticProperty;%0A
 import n
@@ -698,24 +698,68 @@
 hException;%0A
+import net.sf.saxon.type.BuiltInAtomicType;%0A
 import net.s
@@ -1616,9 +1616,9 @@
 urn 
-5
+4
 ;%0A  
@@ -1708,19 +1708,8 @@
 gs),
- exitValue,
  wat
@@ -1789,17 +1789,16 @@
 _STRING,
- 
 %0A       
@@ -1815,60 +1815,86 @@
 ype.
-STRING_SEQUENCE,%0A        SequenceType.SINGLE_INTEGER
+makeSequenceType(BuiltInAtomicType.STRING, StaticProperty.ALLOWS_ZERO_OR_MORE)
 ,%0A  
@@ -1912,22 +1912,24 @@
 nceType.
-SINGLE
+OPTIONAL
 _INTEGER
@@ -2545,24 +2545,60 @@
 e());      %0A
+      if (arguments.length %3E 1) %7B%0A  
       Sequen
@@ -2647,24 +2647,26 @@
      %0A      
+  
 Item arg;%0A  
@@ -2669,16 +2669,18 @@
 ;%0A      
+ 
+ 
 while ((
@@ -2701,32 +2701,34 @@
 t()) != null) %7B%0A
+  
         cmdLine.
@@ -2788,21 +2788,19 @@
     
-%7D
+  %7D%0A
       
-      
+%7D
 %0A   
@@ -2856,25 +2856,23 @@
    Item 
-exitValue
+timeout
 ;%0A      
@@ -2879,166 +2879,32 @@
 if (
-(exitValue = arguments%5B2%5D.head()) != null) %7B%0A        executor.setExitValue((int) ((Int64Value) exitValue).longValue());%0A      %7D%0A      Item timeout;%0A      if (
+arguments.length %3E 2 && 
 (tim
@@ -2924,9 +2924,9 @@
 nts%5B
-3
+2
 %5D.he
@@ -3150,16 +3150,40 @@
     if (
+arguments.length %3E 3 && 
 (async =
@@ -3193,17 +3193,17 @@
 guments%5B
-4
+3
 %5D.head()

Change of functionality of XPath extension function exec-external()