001/*
002 * Copyright (c) 2016 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2016-03-08 00:32:45 +0000 (Tue, 08 Mar 2016) $' 
007 * '$Revision: 34450 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029package org.kepler;
030
031import java.util.Collection;
032import java.util.HashMap;
033import java.util.Map;
034
035/** A class to hold command line argument information used when parsing
036 *  the Kepler command like arguments.
037 * 
038 *  @author Daniel Crawl
039 *  @version $Id: CommandLineArgument.java 34450 2016-03-08 00:32:45Z crawl $
040 */
041public class CommandLineArgument {
042
043    /** Create a new CommandLineArgument for a flag with a description. */
044    private CommandLineArgument(String flag, String description) {
045        this(flag, 0, description);
046    }
047
048    /** Create a new CommandLineArgument for a flag with following values. */
049    private CommandLineArgument(String flag, int numArgs, String description, Object... defaultValues) {
050        _flag = flag;
051        _description = description;
052        _numArguments = numArgs;
053            
054        if(numArgs != defaultValues.length) {
055            throw new IllegalArgumentException("Number of arguments must match number of default values.");
056        }
057        
058        if(_numArguments > 0) {
059            _values = new String[_numArguments];
060            for(int i = 0; i < _numArguments; i++) {
061                _values[i] = defaultValues[i].toString();
062            }
063        }
064    }
065    
066    /** Add a new CommandLineArgument for a flag with a description. */
067    public static CommandLineArgument add(String flag, String description) {
068        return add(flag, 0, description);
069    }
070    
071    /** Add a new CommandLineArgument for a flag with following values. */
072    public static CommandLineArgument add(String flag, int numArgs, String description, Object... defaultStr) {
073        CommandLineArgument arg = new CommandLineArgument(flag, numArgs, description, defaultStr);
074        _argsMap.put(flag, arg);
075        return arg;
076    }
077    
078    /** Get the CommandLineArgument for a specific flag. */
079    public static CommandLineArgument get(String flag) {
080        return _argsMap.get(flag);
081    }
082    
083    /** Get all the CommandLineArguments registered. */
084    public static Collection<CommandLineArgument> getAll() {
085        return _argsMap.values();
086    }
087    
088    /** Get the flag. */
089    public String getFlag() {
090        return _flag;
091    }
092    
093    /** Get the action class. */
094    public String getActionClass() {
095        return _actionClass;
096    }
097    
098    /** Get the action method. */
099    public String getActionMethod() {
100        return _actionMethod;
101    }
102            
103    /** Get a string for the command line usage. */
104    public String getUsage() {
105        StringBuilder buf = new StringBuilder();
106        buf.append(_flag)
107            // TODO use format() for spaces.
108            .append("                      ")
109            .append(_description);
110        
111        if(_values != null && _values.length > 0) {
112            buf.append(" (default = ");
113            for(int i = 0; i < _values.length - 1; i++) {
114                buf.append(_values[i]);
115                buf.append(" ");
116            }
117            buf.append(_values[_values.length - 1]);
118            buf.append(")");
119        }
120        return buf.toString();
121    }
122        
123    /** Get the value of the first argument. */
124    public String getValue() {
125        return getValue(0);
126    }
127    
128    /** Get the value of the argument at a specific index. */
129    public String getValue(int index) {
130        if(index >= _values.length) {
131            throw new IllegalArgumentException("Index must be less than number of arguments.");
132        }
133        return _values[index];
134    }
135    
136    /** Get the value of the first argument for a flag. */
137    public static String getValue(String flag) {
138        CommandLineArgument arg = _argsMap.get(flag);
139        if(arg == null) {
140            throw new IllegalArgumentException("No command line argument with flag " + flag);
141        }
142        return arg.getValue();
143    }
144    
145    /** Returns true if this CommandLineArgument is an action to run Kepler.
146     *  If true, then run() will be called instead of launching the Kepler
147     *  UI or running a workflow from the command line.
148     */
149    public boolean isAction() {
150        return _actionClass != null;
151    }
152
153    /** Parse one or more of the remaining command line arguments.
154     *  @param The command line arguments.
155     *  @param The index to the current command line argument yet to be parsed.
156     *  @return If one or more of the current command line arguments can be
157     *  parsed, the index to the next unparsed argument. Otherwise (no
158     *  arguments can be parsed), returns -1.
159     */
160    public int parseRemainingArgs(String[] args, int i) throws IllegalArgumentException {
161        if(args[i].equals(_flag)) {
162            // increment the index past the flag argument
163            i++;
164            // try to parse the arguments after the flag, if any
165            for(int j = 0; j < _numArguments; j++) {   
166                // see if there are not enough arguments.
167                if(i >= args.length) {
168                    // TODO
169                    throw new IllegalArgumentException(); 
170                }
171                // save the argument
172                _values[j] = args[i];
173                // increment to parse the next argument
174                i++;
175            }
176            _parsed = true;
177            return i;
178        }
179        _parsed = false;
180        return -1;
181    }
182    
183    /** Set the class and method to invoke if this argument is seen on the command line. */
184    public void setAction(String actionClass, String actionMethod) {
185        _actionClass = actionClass;
186        _actionMethod = actionMethod;
187    }
188    
189    /** Get a string representation of the command line argument. */
190    @Override
191    public String toString() {
192        StringBuilder buf = new StringBuilder(_flag + " parsed=" + _parsed);
193        if(_numArguments > 0 && _parsed) {
194            buf.append(" args=");
195            for(int i = 0; i < _numArguments - 1; i++) {
196                buf.append(_values[i]).append(",");
197            }
198            buf.append(_values[_numArguments - 1]);
199        }
200        
201        return buf.toString();
202    }
203    
204    /** Returns true if the last call to parseRemainingArgs() succeeded. */
205    public boolean wasParsed() {
206        return _parsed;
207    }
208    
209    /** Returns true if the last call to parseRemainingArgs() succeeded. */
210    public static boolean wasParsed(String flag) {
211        CommandLineArgument arg = _argsMap.get(flag);
212        if(arg == null) {
213            throw new IllegalArgumentException("No command line argument with flag " + flag);
214        }
215        return arg.wasParsed();
216    }
217
218    /** The flag for the argmument. */
219    private String _flag;
220    
221    /** The description of the argument. */
222    private String _description;
223    
224    /** The number of arguments after flag on the command line. */
225    private int _numArguments;
226    
227    /** If true, this argument was seen on the command line. */
228    private boolean _parsed = false;
229    
230    /** The values seen on the command line after the flag. */
231    private String[] _values;
232    
233    /** The class to run if this argument was seen on the command line. */
234    private String _actionClass;
235    
236    /** The method to invoke if this argument was seen on the command line. */
237    private String _actionMethod;
238    
239    /** A map of flag to CommandLineArgument containing all the CommandLineArguments registered */
240    private final static Map<String,CommandLineArgument> _argsMap = new HashMap<String,CommandLineArgument>();
241}