Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 571   Methods: 33
NCLOC: 277   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
Commandline.java 72.7% 81.2% 75.8% 78.2%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 
 55   
 package org.apache.tools.ant.types;
 56   
 
 57   
 import java.io.File;
 58   
 import java.util.StringTokenizer;
 59   
 import java.util.Vector;
 60   
 import org.apache.tools.ant.BuildException;
 61   
 import org.apache.tools.ant.ProjectComponent;
 62   
 import org.apache.tools.ant.util.StringUtils;
 63   
 
 64   
 
 65   
 /**
 66   
  * Commandline objects help handling command lines specifying processes to
 67   
  * execute.
 68   
  *
 69   
  * The class can be used to define a command line as nested elements or as a
 70   
  * helper to define a command line by an application.
 71   
  * <p>
 72   
  * <code>
 73   
  * &lt;someelement&gt;<br>
 74   
  * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
 75   
  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
 76   
  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
 77   
  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
 78   
  * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
 79   
  * &lt;/someelement&gt;<br>
 80   
  * </code>
 81   
  * The element <code>someelement</code> must provide a method
 82   
  * <code>createAcommandline</code> which returns an instance of this class.
 83   
  *
 84   
  * @author thomas.haas@softwired-inc.com
 85   
  * @author Stefan Bodewig
 86   
  */
 87   
 public class Commandline implements Cloneable {
 88   
 
 89   
     private Vector arguments = new Vector();
 90   
     private String executable = null;
 91   
 
 92   
     protected static final String DISCLAIMER =
 93   
         StringUtils.LINE_SEP
 94   
         + "The \' characters around the executable and arguments are"
 95   
         + StringUtils.LINE_SEP
 96   
         + "not part of the command."
 97   
         + StringUtils.LINE_SEP;
 98   
 
 99  0
     public Commandline(String to_process) {
 100  0
         super();
 101  0
         String[] tmp = translateCommandline(to_process);
 102  0
         if (tmp != null && tmp.length > 0) {
 103  0
             setExecutable(tmp[0]);
 104  0
             for (int i = 1; i < tmp.length; i++) {
 105  0
                 createArgument().setValue(tmp[i]);
 106   
             }
 107   
         }
 108   
     }
 109   
 
 110  139
     public Commandline() {
 111  139
         super();
 112   
     }
 113   
 
 114   
     /**
 115   
      * Used for nested xml command line definitions.
 116   
      */
 117   
     public static class Argument extends ProjectComponent {
 118   
 
 119   
         private String[] parts;
 120   
 
 121   
         /**
 122   
          * Sets a single commandline argument.
 123   
          *
 124   
          * @param value a single commandline argument.
 125   
          */
 126  250
         public void setValue(String value) {
 127  250
             parts = new String[] {value};
 128   
         }
 129   
 
 130   
         /**
 131   
          * Line to split into several commandline arguments.
 132   
          *
 133   
          * @param line line to split into several commandline arguments
 134   
          */
 135  3
         public void setLine(String line) {
 136  3
             if (line == null) {
 137  0
                 return;
 138   
             }
 139  3
             parts = translateCommandline(line);
 140   
         }
 141   
 
 142   
         /**
 143   
          * Sets a single commandline argument and treats it like a
 144   
          * PATH - ensures the right separator for the local platform
 145   
          * is used.
 146   
          *
 147   
          * @param value a single commandline argument.
 148   
          */
 149  34
         public void setPath(Path value) {
 150  34
             parts = new String[] {value.toString()};
 151   
         }
 152   
 
 153   
         /**
 154   
          * Sets a single commandline argument from a reference to a
 155   
          * path - ensures the right separator for the local platform
 156   
          * is used.
 157   
          *
 158   
          * @param value a single commandline argument.
 159   
          */
 160  0
         public void setPathref(Reference value) {
 161  0
             Path p = new Path(getProject());
 162  0
             p.setRefid(value);
 163  0
             parts = new String[] {p.toString()};
 164   
         }
 165   
 
 166   
         /**
 167   
          * Sets a single commandline argument to the absolute filename
 168   
          * of the given file.
 169   
          *
 170   
          * @param value a single commandline argument.
 171   
          */
 172  26
         public void setFile(File value) {
 173  26
             parts = new String[] {value.getAbsolutePath()};
 174   
         }
 175   
 
 176   
         /**
 177   
          * Returns the parts this Argument consists of.
 178   
          */
 179  707
         public String[] getParts() {
 180  707
             return parts;
 181   
         }
 182   
     }
 183   
 
 184   
     /**
 185   
      * Class to keep track of the position of an Argument.
 186   
      */
 187   
     // <p>This class is there to support the srcfile and targetfile
 188   
     // elements of &lt;execon&gt; and &lt;transform&gt; - don't know
 189   
     // whether there might be additional use cases.</p> --SB
 190   
     public class Marker {
 191   
 
 192   
         private int position;
 193   
         private int realPos = -1;
 194   
 
 195  0
         Marker(int position) {
 196  0
             this.position = position;
 197   
         }
 198   
 
 199   
         /**
 200   
          * Return the number of arguments that preceeded this marker.
 201   
          *
 202   
          * <p>The name of the executable - if set - is counted as the
 203   
          * very first argument.</p>
 204   
          */
 205  0
         public int getPosition() {
 206  0
             if (realPos == -1) {
 207  0
                 realPos = (executable == null ? 0 : 1);
 208  0
                 for (int i = 0; i < position; i++) {
 209  0
                     Argument arg = (Argument) arguments.elementAt(i);
 210  0
                     realPos += arg.getParts().length;
 211   
                 }
 212   
             }
 213  0
             return realPos;
 214   
         }
 215   
     }
 216   
 
 217   
     /**
 218   
      * Creates an argument object.
 219   
      *
 220   
      * <p>Each commandline object has at most one instance of the
 221   
      * argument class.  This method calls
 222   
      * <code>this.createArgument(false)</code>.</p>
 223   
      *
 224   
      * @see #createArgument(boolean)
 225   
      * @return the argument object.
 226   
      */
 227  308
     public Argument createArgument() {
 228  308
         return this.createArgument(false);
 229   
     }
 230   
 
 231   
     /**
 232   
      * Creates an argument object and adds it to our list of args.
 233   
      *
 234   
      * <p>Each commandline object has at most one instance of the
 235   
      * argument class.</p>
 236   
      *
 237   
      * @param insertAtStart if true, the argument is inserted at the
 238   
      * beginning of the list of args, otherwise it is appended.
 239   
      */
 240  308
     public Argument createArgument(boolean insertAtStart) {
 241  308
         Argument argument = new Argument();
 242  308
         if (insertAtStart) {
 243  0
             arguments.insertElementAt(argument, 0);
 244   
         } else {
 245  308
             arguments.addElement(argument);
 246   
         }
 247  308
         return argument;
 248   
     }
 249   
 
 250   
     /**
 251   
      * Sets the executable to run.
 252   
      */
 253  119
     public void setExecutable(String executable) {
 254  119
         if (executable == null || executable.length() == 0) {
 255  0
             return;
 256   
         }
 257  119
         this.executable = executable.replace('/', File.separatorChar)
 258   
             .replace('\\', File.separatorChar);
 259   
     }
 260   
 
 261   
 
 262  35
     public String getExecutable() {
 263  35
         return executable;
 264   
     }
 265   
 
 266   
 
 267  70
     public void addArguments(String[] line) {
 268  70
         for (int i = 0; i < line.length; i++) {
 269  10
             createArgument().setValue(line[i]);
 270   
         }
 271   
     }
 272   
 
 273   
     /**
 274   
      * Returns the executable and all defined arguments.
 275   
      */
 276  142
     public String[] getCommandline() {
 277  142
         final String[] args = getArguments();
 278  142
         if (executable == null) {
 279  6
             return args;
 280   
         }
 281  136
         final String[] result = new String[args.length + 1];
 282  136
         result[0] = executable;
 283  136
         System.arraycopy(args, 0, result, 1, args.length);
 284  136
         return result;
 285   
     }
 286   
 
 287   
 
 288   
     /**
 289   
      * Returns all arguments defined by <code>addLine</code>,
 290   
      * <code>addValue</code> or the argument object.
 291   
      */
 292  242
     public String[] getArguments() {
 293  242
         Vector result = new Vector(arguments.size() * 2);
 294  242
         for (int i = 0; i < arguments.size(); i++) {
 295  698
             Argument arg = (Argument) arguments.elementAt(i);
 296  698
             String[] s = arg.getParts();
 297  698
             if (s != null) {
 298  698
                 for (int j = 0; j < s.length; j++) {
 299  698
                     result.addElement(s[j]);
 300   
                 }
 301   
             }
 302   
         }
 303   
 
 304  242
         String [] res = new String[result.size()];
 305  242
         result.copyInto(res);
 306  242
         return res;
 307   
     }
 308   
 
 309   
 
 310  0
     public String toString() {
 311  0
         return toString(getCommandline());
 312   
     }
 313   
 
 314   
     /**
 315   
      * Put quotes around the given String if necessary.
 316   
      *
 317   
      * <p>If the argument doesn't include spaces or quotes, return it
 318   
      * as is. If it contains double quotes, use single quotes - else
 319   
      * surround the argument by double quotes.</p>
 320   
      *
 321   
      * @exception BuildException if the argument contains both, single
 322   
      *                           and double quotes.
 323   
      */
 324  9
     public static String quoteArgument(String argument) {
 325  9
         if (argument.indexOf("\"") > -1) {
 326  1
             if (argument.indexOf("\'") > -1) {
 327  0
                 throw new BuildException("Can\'t handle single and double quotes in same argument");
 328   
             } else {
 329  1
                 return '\'' + argument + '\'';
 330   
             }
 331  8
         } else if (argument.indexOf("\'") > -1 || argument.indexOf(" ") > -1) {
 332  2
             return '\"' + argument + '\"';
 333   
         } else {
 334  6
             return argument;
 335   
         }
 336   
     }
 337   
 
 338   
     /**
 339   
      * Quotes the parts of the given array in way that makes them
 340   
      * usable as command line arguments.
 341   
      */
 342  6
     public static String toString(String [] line) {
 343   
         // empty path return empty string
 344  6
         if (line == null || line.length == 0) {
 345  2
             return "";
 346   
         }
 347   
 
 348   
         // path containing one or more elements
 349  4
         final StringBuffer result = new StringBuffer();
 350  4
         for (int i = 0; i < line.length; i++) {
 351  9
             if (i > 0) {
 352  5
                 result.append(' ');
 353   
             }
 354  9
             result.append(quoteArgument(line[i]));
 355   
         }
 356  4
         return result.toString();
 357   
     }
 358   
 
 359  21
     public static String[] translateCommandline(String to_process) {
 360  21
         if (to_process == null || to_process.length() == 0) {
 361  2
             return new String[0];
 362   
         }
 363   
 
 364   
         // parse with a simple finite state machine
 365   
 
 366  19
         final int normal = 0;
 367  19
         final int inQuote = 1;
 368  19
         final int inDoubleQuote = 2;
 369  19
         int state = normal;
 370  19
         StringTokenizer tok = new StringTokenizer(to_process, "\"\' ", true);
 371  19
         Vector v = new Vector();
 372  19
         StringBuffer current = new StringBuffer();
 373  19
         boolean lastTokenHasBeenQuoted = false;
 374   
 
 375  19
         while (tok.hasMoreTokens()) {
 376  103
             String nextTok = tok.nextToken();
 377  103
             switch (state) {
 378   
             case inQuote:
 379  16
                 if ("\'".equals(nextTok)) {
 380  6
                     lastTokenHasBeenQuoted = true;
 381  6
                     state = normal;
 382   
                 } else {
 383  10
                     current.append(nextTok);
 384   
                 }
 385  16
                 break;
 386   
             case inDoubleQuote:
 387  16
                 if ("\"".equals(nextTok)) {
 388  6
                     lastTokenHasBeenQuoted = true;
 389  6
                     state = normal;
 390   
                 } else {
 391  10
                     current.append(nextTok);
 392   
                 }
 393  16
                 break;
 394   
             default:
 395  71
                 if ("\'".equals(nextTok)) {
 396  7
                     state = inQuote;
 397  64
                 } else if ("\"".equals(nextTok)) {
 398  7
                     state = inDoubleQuote;
 399  57
                 } else if (" ".equals(nextTok)) {
 400  26
                     if (lastTokenHasBeenQuoted || current.length() != 0) {
 401  26
                         v.addElement(current.toString());
 402  26
                         current = new StringBuffer();
 403   
                     }
 404   
                 } else {
 405  31
                     current.append(nextTok);
 406   
                 }
 407  71
                 lastTokenHasBeenQuoted = false;
 408  71
                 break;
 409   
             }
 410   
         }
 411   
 
 412  19
         if (lastTokenHasBeenQuoted || current.length() != 0) {
 413  19
             v.addElement(current.toString());
 414   
         }
 415   
 
 416  19
         if (state == inQuote || state == inDoubleQuote) {
 417  2
             throw new BuildException("unbalanced quotes in " + to_process);
 418   
         }
 419   
 
 420  17
         String[] args = new String[v.size()];
 421  17
         v.copyInto(args);
 422  17
         return args;
 423   
     }
 424   
 
 425  74
     public int size() {
 426  74
         return getCommandline().length;
 427   
     }
 428   
 
 429  53
     public Object clone() {
 430  53
         Commandline c = new Commandline();
 431  53
         c.setExecutable(executable);
 432  53
         c.addArguments(getArguments());
 433  53
         return c;
 434   
     }
 435   
 
 436   
     /**
 437   
      * Clear out the whole command line.  */
 438  0
     public void clear() {
 439  0
         executable = null;
 440  0
         arguments.removeAllElements();
 441   
     }
 442   
 
 443   
     /**
 444   
      * Clear out the arguments but leave the executable in place for
 445   
      * another operation.
 446   
      */
 447  0
     public void clearArgs() {
 448  0
         arguments.removeAllElements();
 449   
     }
 450   
 
 451   
     /**
 452   
      * Return a marker.
 453   
      *
 454   
      * <p>This marker can be used to locate a position on the
 455   
      * commandline - to insert something for example - when all
 456   
      * parameters have been set.</p>
 457   
      */
 458  0
     public Marker createMarker() {
 459  0
         return new Marker(arguments.size());
 460   
     }
 461   
 
 462   
     /**
 463   
      * Returns a String that describes the command and arguments
 464   
      * suitable for verbose output before a call to
 465   
      * <code>Runtime.exec(String[])<code>
 466   
      *
 467   
      * @since Ant 1.5
 468   
      */
 469  5
     public String describeCommand() {
 470  5
         return describeCommand(this);
 471   
     }
 472   
 
 473   
     /**
 474   
      * Returns a String that describes the arguments suitable for
 475   
      * verbose output before a call to
 476   
      * <code>Runtime.exec(String[])<code>
 477   
      *
 478   
      * @since Ant 1.5
 479   
      */
 480  17
     public String describeArguments() {
 481  17
         return describeArguments(this);
 482   
     }
 483   
 
 484   
     /**
 485   
      * Returns a String that describes the command and arguments
 486   
      * suitable for verbose output before a call to
 487   
      * <code>Runtime.exec(String[])<code>
 488   
      *
 489   
      * @since Ant 1.5
 490   
      */
 491  16
     public static String describeCommand(Commandline line) {
 492  16
         return describeCommand(line.getCommandline());
 493   
     }
 494   
 
 495   
     /**
 496   
      * Returns a String that describes the arguments suitable for
 497   
      * verbose output before a call to
 498   
      * <code>Runtime.exec(String[])<code>
 499   
      *
 500   
      * @since Ant 1.5
 501   
      */
 502  17
     public static String describeArguments(Commandline line) {
 503  17
         return describeArguments(line.getArguments());
 504   
     }
 505   
 
 506   
     /**
 507   
      * Returns a String that describes the command and arguments
 508   
      * suitable for verbose output before a call to
 509   
      * <code>Runtime.exec(String[])<code>.
 510   
      *
 511   
      * <p>This method assumes that the first entry in the array is the
 512   
      * executable to run.</p>
 513   
      * 
 514   
      * @since Ant 1.5
 515   
      */
 516  69
     public static String describeCommand(String[] args) {
 517  69
         if (args == null || args.length == 0) {
 518  0
             return "";
 519   
         }
 520   
         
 521  69
         StringBuffer buf = new StringBuffer("Executing \'");
 522  69
         buf.append(args[0]);
 523  69
         buf.append("\'");
 524  69
         if (args.length > 0) {
 525  69
             buf.append(" with ");
 526  69
             buf.append(describeArguments(args, 1));
 527   
         } else {
 528  0
             buf.append(DISCLAIMER);
 529   
         }
 530  69
         return buf.toString();
 531   
     }
 532   
 
 533   
     /**
 534   
      * Returns a String that describes the arguments suitable for
 535   
      * verbose output before a call to
 536   
      * <code>Runtime.exec(String[])<code>
 537   
      *
 538   
      * @since Ant 1.5
 539   
      */
 540  17
     public static String describeArguments(String[] args) {
 541  17
         return describeArguments(args, 0);
 542   
     }
 543   
 
 544   
     /**
 545   
      * Returns a String that describes the arguments suitable for
 546   
      * verbose output before a call to
 547   
      * <code>Runtime.exec(String[])<code>
 548   
      *
 549   
      * @param offset ignore entries before this index
 550   
      *
 551   
      * @since Ant 1.5
 552   
      */
 553  86
     protected static String describeArguments(String[] args, int offset) {
 554  86
         if (args == null || args.length <= offset) {
 555  5
             return "";
 556   
         }
 557   
 
 558  81
         StringBuffer buf = new StringBuffer("argument");
 559  81
         if (args.length > offset) {
 560  81
             buf.append("s");
 561   
         }
 562  81
         buf.append(":").append(StringUtils.LINE_SEP);
 563  81
         for (int i = offset; i < args.length; i++) {
 564  435
             buf.append("\'").append(args[i]).append("\'")
 565   
                 .append(StringUtils.LINE_SEP);
 566   
         }
 567  81
         buf.append(DISCLAIMER);
 568  81
         return buf.toString();
 569   
     }
 570   
 }
 571