001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.server.util.cli;
029    
030    import java.util.ArrayList;
031    import java.util.HashSet;
032    import java.util.List;
033    
034    import org.opends.server.util.SetupUtils;
035    import org.opends.server.util.args.Argument;
036    import org.opends.server.util.args.BooleanArgument;
037    import org.opends.server.util.args.FileBasedArgument;
038    
039    /**
040     * Class used to be able to generate the non interactive mode.
041     *
042     */
043    public class CommandBuilder
044    {
045      // The command name.
046      private String commandName;
047    
048      // The subcommand name.
049      private String subcommandName;
050    
051      private ArrayList<Argument> args = new ArrayList<Argument>();
052      private HashSet<Argument> obfuscatedArgs = new HashSet<Argument>();
053    
054      // The value used to display arguments that must be obfuscated (such as
055      // passwords).  This does not require localization (since the output of
056      // command builder by its nature is not localized).
057      private final static String OBFUSCATED_VALUE = "******";
058    
059      /**
060       * The constructor for the CommandBuilder.
061       * @param commandName the command name.
062       */
063      public CommandBuilder(String commandName)
064      {
065        this(commandName, null);
066      }
067    
068      /**
069       * The constructor for the CommandBuilder.
070       * @param commandName the command name.
071       * @param subcommandName the subcommand name.
072       */
073      public CommandBuilder(String commandName, String subcommandName)
074      {
075        this.commandName = commandName;
076        this.subcommandName = subcommandName;
077      }
078    
079      /**
080       * Adds an argument to the list of the command builder.
081       * @param argument the argument to be added.
082       */
083      public void addArgument(Argument argument)
084      {
085        // We use an ArrayList to be able to provide the possibility of updating
086        // the position of the attributes.
087        if (!args.contains(argument))
088        {
089          args.add(argument);
090        }
091      }
092    
093      /**
094       * Adds an argument whose values must be obfuscated (passwords for instance).
095       * @param argument the argument to be added.
096       */
097      public void addObfuscatedArgument(Argument argument)
098      {
099        addArgument(argument);
100        obfuscatedArgs.add(argument);
101      }
102    
103      /**
104       * Removes the provided argument from this CommandBuilder.
105       * @param argument the argument to be removed.
106       * @return <CODE>true</CODE> if the attribute was present and removed and
107       * <CODE>false</CODE> otherwise.
108       */
109      public boolean removeArgument(Argument argument)
110      {
111        obfuscatedArgs.remove(argument);
112        return args.remove(argument);
113      }
114    
115      /**
116       * Appends the arguments of another command builder to this command builder.
117       * @param builder the CommandBuilder to append.
118       */
119      public void append(CommandBuilder builder)
120      {
121        for (Argument arg : builder.args)
122        {
123          if (builder.isObfuscated(arg))
124          {
125            addObfuscatedArgument(arg);
126          }
127          else
128          {
129            addArgument(arg);
130          }
131        }
132      }
133    
134      /**
135       * Returns the String representation of this command builder (i.e. what we
136       * want to show to the user).
137       * @return the String representation of this command builder (i.e. what we
138       * want to show to the user).
139       */
140      public String toString()
141      {
142        return toString(false);
143      }
144    
145      /**
146       * Returns the String representation of this command builder (i.e. what we
147       * want to show to the user).
148       * @param showObfuscated displays in clear the obfuscated values.
149       * @return the String representation of this command builder (i.e. what we
150       * want to show to the user).
151       */
152      private String toString(boolean showObfuscated)
153      {
154        StringBuilder builder = new StringBuilder();
155        builder.append(commandName);
156        if (subcommandName != null)
157        {
158          builder.append(" "+subcommandName);
159        }
160        for (Argument arg : args)
161        {
162          String argName;
163          if (arg.getLongIdentifier() != null)
164          {
165            argName = "--"+arg.getLongIdentifier();
166          }
167          else
168          {
169            argName = "-"+arg.getShortIdentifier();
170          }
171          String separator;
172          if (SetupUtils.isWindows())
173          {
174            separator = " ";
175          }
176          else
177          {
178            separator = " \\\n          ";
179          }
180    
181          if (arg instanceof BooleanArgument)
182          {
183            builder.append(separator+argName);
184          }
185          else if (arg instanceof FileBasedArgument)
186          {
187            for (String value :
188              ((FileBasedArgument)arg).getNameToValueMap().keySet())
189            {
190              builder.append(separator+argName+" ");
191              if (isObfuscated(arg) && !showObfuscated)
192              {
193                value = OBFUSCATED_VALUE;
194              }
195              else
196              {
197                value = escapeValue(value);
198              }
199              builder.append(value);
200            }
201          }
202          else
203          {
204            for (String value : arg.getValues())
205            {
206              builder.append(separator+argName+" ");
207              if (isObfuscated(arg) && !showObfuscated)
208              {
209                value = OBFUSCATED_VALUE;
210              }
211              else
212              {
213                value = escapeValue(value);
214              }
215              builder.append(value);
216            }
217          }
218        }
219        return builder.toString();
220      }
221    
222      /**
223       * Clears the arguments.
224       */
225      public void clearArguments()
226      {
227        args.clear();
228        obfuscatedArgs.clear();
229      }
230    
231      /**
232       * Returns the list of arguments.
233       * @return the list of arguments.
234       */
235      public List<Argument> getArguments()
236      {
237        return args;
238      }
239    
240      /**
241       * Tells whether the provided argument's values must be obfuscated or not.
242       * @param argument the argument to handle.
243       * @return <CODE>true</CODE> if the attribute's values must be obfuscated and
244       * <CODE>false</CODE> otherwise.
245       */
246      private boolean isObfuscated(Argument argument)
247      {
248        return obfuscatedArgs.contains(argument);
249      }
250    
251      // Chars that require special treatment when passing them to command-line.
252      private final char[] charsToEscape = {' ', '\t', '\n', '|', ';', '<', '>',
253          '(', ')', '$', '`', '\\', '"', '\''};
254      /**
255       * This method simply takes a value and tries to transform it (with escape or
256       * '"') characters so that it can be used in a command line.
257       * @param value the String to be treated.
258       * @return the transformed value.
259       */
260      private String escapeValue(String value)
261      {
262        StringBuilder b = new StringBuilder();
263        if (SetupUtils.isUnix())
264        {
265          for (int i=0 ; i<value.length(); i++)
266          {
267            char c = value.charAt(i);
268            boolean charToEscapeFound = false;
269            for (int j=0; j<charsToEscape.length && !charToEscapeFound; j++)
270            {
271              charToEscapeFound = c == charsToEscape[j];
272            }
273            if (charToEscapeFound)
274            {
275              b.append('\\');
276            }
277            b.append(c);
278          }
279        }
280        else
281        {
282          b.append('"').append(value).append('"');
283        }
284    
285        return b.toString();
286      }
287    }