View Javadoc
1   package org.apache.maven.shared.utils.cli.shell;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  
23  import java.io.File;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.List;
27  import org.apache.maven.shared.utils.StringUtils;
28  
29  /**
30   * <p>
31   * Class that abstracts the Shell functionality,
32   * with subclases for shells that behave particularly, like
33   * <ul>
34   * <li><code>command.com</code></li>
35   * <li><code>cmd.exe</code></li>
36   * </ul>
37   * </p>
38   *
39   * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
40   * 
41   */
42  public class Shell
43      implements Cloneable
44  {
45      private static final char[] DEFAULT_QUOTING_TRIGGER_CHARS = { ' ' };
46  
47      private String shellCommand;
48  
49      private final List<String> shellArgs = new ArrayList<String>();
50  
51      private boolean quotedArgumentsEnabled = true;
52  
53      private String executable;
54  
55      private String workingDir;
56  
57      private boolean quotedExecutableEnabled = true;
58  
59      private boolean singleQuotedArgumentEscaped = false;
60  
61      private boolean singleQuotedExecutableEscaped = false;
62  
63      private char argQuoteDelimiter = '\"';
64  
65      private char exeQuoteDelimiter = '\"';
66  
67      /**
68       * Set the command to execute the shell (eg. COMMAND.COM, /bin/bash,...)
69       *
70       * @param shellCommand The command
71       */
72      void setShellCommand( String shellCommand )
73      {
74          this.shellCommand = shellCommand;
75      }
76  
77      /**
78       * Get the command to execute the shell
79       *
80       * @return  The command
81       */
82      String getShellCommand()
83      {
84          return shellCommand;
85      }
86  
87      /**
88       * Set the shell arguments when calling a command line (not the executable arguments)
89       * (eg. /X /C for CMD.EXE)
90       *
91       * @param shellArgs the arguments to the shell
92       */
93      void setShellArgs( String[] shellArgs )
94      {
95          this.shellArgs.clear();
96          this.shellArgs.addAll( Arrays.asList( shellArgs ) );
97      }
98  
99      /**
100      * Get the shell arguments
101      *
102      * @return  The arguments
103      */
104     String[] getShellArgs()
105     {
106         if ( ( shellArgs == null ) || shellArgs.isEmpty() )
107         {
108             return null;
109         }
110         else
111         {
112             return shellArgs.toArray( new String[shellArgs.size()] );
113         }
114     }
115 
116     /**
117      * Get the command line for the provided executable and arguments in this shell
118      *
119      * @param executableParameter executable that the shell has to call
120      * @param argumentsParameter  arguments for the executable, not the shell
121      * @return List with one String object with executable and arguments quoted as needed
122      */
123     List<String> getCommandLine( String executableParameter, String... argumentsParameter )
124     {
125         return getRawCommandLine( executableParameter, argumentsParameter );
126     }
127 
128     /**
129      * @param executableParameter Executable.
130      * @param argumentsParameter The arguments for the executable.
131      * @return The list on command line. 
132      */
133     List<String> getRawCommandLine( String executableParameter, String... argumentsParameter )
134     {
135         List<String> commandLine = new ArrayList<String>();
136         StringBuilder sb = new StringBuilder();
137 
138         if ( executableParameter != null )
139         {
140             String preamble = getExecutionPreamble();
141             if ( preamble != null )
142             {
143                 sb.append( preamble );
144             }
145 
146             if ( isQuotedExecutableEnabled() )
147             {
148                 char[] escapeChars =
149                     getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() );
150 
151                 sb.append( StringUtils.quoteAndEscape( getExecutable(), getExecutableQuoteDelimiter(), escapeChars,
152                                                        getQuotingTriggerChars(), '\\', false ) );
153             }
154             else
155             {
156                 sb.append( getExecutable() );
157             }
158         }
159         for ( String argument : argumentsParameter )
160         {
161             if ( sb.length() > 0 )
162             {
163                 sb.append( ' ' );
164             }
165 
166             if ( isQuotedArgumentsEnabled() )
167             {
168                 char[] escapeChars = getEscapeChars( isSingleQuotedArgumentEscaped(), isDoubleQuotedArgumentEscaped() );
169 
170                 sb.append( StringUtils.quoteAndEscape( argument, getArgumentQuoteDelimiter(), escapeChars,
171                                                        getQuotingTriggerChars(), '\\', false ) );
172             }
173             else
174             {
175                 sb.append( argument );
176             }
177         }
178 
179         commandLine.add( sb.toString() );
180 
181         return commandLine;
182     }
183 
184     char[] getQuotingTriggerChars()
185     {
186         return DEFAULT_QUOTING_TRIGGER_CHARS;
187     }
188 
189     String getExecutionPreamble()
190     {
191         return null;
192     }
193 
194     char[] getEscapeChars( boolean includeSingleQuote, boolean includeDoubleQuote )
195     {
196         StringBuilder buf = new StringBuilder( 2 );
197         if ( includeSingleQuote )
198         {
199             buf.append( '\'' );
200         }
201 
202         if ( includeDoubleQuote )
203         {
204             buf.append( '\"' );
205         }
206 
207         char[] result = new char[buf.length()];
208         buf.getChars( 0, buf.length(), result, 0 );
209 
210         return result;
211     }
212 
213     /**
214      * @return false in all cases. 
215      */
216     protected boolean isDoubleQuotedArgumentEscaped()
217     {
218         return false;
219     }
220 
221     /**
222      * @return {@link #singleQuotedArgumentEscaped}
223      */
224     protected boolean isSingleQuotedArgumentEscaped()
225     {
226         return singleQuotedArgumentEscaped;
227     }
228 
229     boolean isDoubleQuotedExecutableEscaped()
230     {
231         return false;
232     }
233 
234     boolean isSingleQuotedExecutableEscaped()
235     {
236         return singleQuotedExecutableEscaped;
237     }
238 
239     /**
240      * @param argQuoteDelimiterParameter {@link #argQuoteDelimiter}
241      */
242     void setArgumentQuoteDelimiter( char argQuoteDelimiterParameter )
243     {
244         this.argQuoteDelimiter = argQuoteDelimiterParameter;
245     }
246 
247     char getArgumentQuoteDelimiter()
248     {
249         return argQuoteDelimiter;
250     }
251 
252     /**
253      * @param exeQuoteDelimiterParameter {@link #exeQuoteDelimiter}
254      */
255     void setExecutableQuoteDelimiter( char exeQuoteDelimiterParameter )
256     {
257         this.exeQuoteDelimiter = exeQuoteDelimiterParameter;
258     }
259 
260     char getExecutableQuoteDelimiter()
261     {
262         return exeQuoteDelimiter;
263     }
264 
265     /**
266      * Get the full command line to execute, including shell command, shell arguments,
267      * executable and executable arguments
268      *
269      * @param arguments arguments for the executable, not the shell
270      * @return List of String objects, whose array version is suitable to be used as argument
271      *         of Runtime.getRuntime().exec()
272      */
273     public List<String> getShellCommandLine( String... arguments )
274     {
275 
276         List<String> commandLine = new ArrayList<String>();
277 
278         if ( getShellCommand() != null )
279         {
280             commandLine.add( getShellCommand() );
281         }
282 
283         if ( getShellArgs() != null )
284         {
285             commandLine.addAll( getShellArgsList() );
286         }
287 
288         commandLine.addAll( getCommandLine( getExecutable(), arguments ) );
289 
290         return commandLine;
291 
292     }
293 
294     List<String> getShellArgsList()
295     {
296         return shellArgs;
297     }
298 
299     /**
300      * @param quotedArgumentsEnabled {@link #quotedArgumentsEnabled}
301      */
302     public void setQuotedArgumentsEnabled( boolean quotedArgumentsEnabled )
303     {
304         this.quotedArgumentsEnabled = quotedArgumentsEnabled;
305     }
306 
307     boolean isQuotedArgumentsEnabled()
308     {
309         return quotedArgumentsEnabled;
310     }
311 
312     void setQuotedExecutableEnabled( boolean quotedExecutableEnabled )
313     {
314         this.quotedExecutableEnabled = quotedExecutableEnabled;
315     }
316 
317     boolean isQuotedExecutableEnabled()
318     {
319         return quotedExecutableEnabled;
320     }
321 
322     /**
323      * Sets the executable to run.
324      * @param executable The executable.
325      */
326     public void setExecutable( String executable )
327     {
328         if ( ( executable == null ) || ( executable.length() == 0 ) )
329         {
330             return;
331         }
332         this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
333     }
334 
335     /**
336      * @return The executable.
337      */
338     public String getExecutable()
339     {
340         return executable;
341     }
342 
343     /**
344      * Sets execution directory.
345      * @param path The path which should be used as working directory.
346      */
347     public void setWorkingDirectory( String path )
348     {
349         if ( path != null )
350         {
351             this.workingDir = path;
352         }
353     }
354 
355     /**
356      * Sets execution directory.
357      * @param workingDirectory The working directory.
358      */
359     public void setWorkingDirectory( File workingDirectory )
360     {
361         if ( workingDirectory != null )
362         {
363             this.workingDir = workingDirectory.getAbsolutePath();
364         }
365     }
366 
367     /**
368      * @return The working directory.
369      */
370     public File getWorkingDirectory()
371     {
372         return workingDir == null ? null : new File( workingDir );
373     }
374 
375     String getWorkingDirectoryAsString()
376     {
377         return workingDir;
378     }
379 
380     /** {@inheritDoc} */
381     public Object clone()
382     {
383         throw new RuntimeException( "Do we ever clone this?" );
384 /*        Shell shell = new Shell();
385         shell.setExecutable( getExecutable() );
386         shell.setWorkingDirectory( getWorkingDirectory() );
387         shell.setShellArgs( getShellArgs() );
388         return shell;*/
389     }
390 
391     void setSingleQuotedArgumentEscaped( boolean singleQuotedArgumentEscaped )
392     {
393         this.singleQuotedArgumentEscaped = singleQuotedArgumentEscaped;
394     }
395 
396     void setSingleQuotedExecutableEscaped( boolean singleQuotedExecutableEscaped )
397     {
398         this.singleQuotedExecutableEscaped = singleQuotedExecutableEscaped;
399     }
400 
401 }