001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.cli2.commandline;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025import java.util.Properties;
026import java.util.Set;
027
028import org.apache.commons.cli2.Argument;
029import org.apache.commons.cli2.Option;
030import org.apache.commons.cli2.WriteableCommandLine;
031import org.apache.commons.cli2.option.PropertyOption;
032import org.apache.commons.cli2.resource.ResourceConstants;
033import org.apache.commons.cli2.resource.ResourceHelper;
034
035/**
036 * A WriteableCommandLine implementation allowing Options to write their
037 * processed information to a CommandLine.
038 */
039public class WriteableCommandLineImpl
040    extends CommandLineImpl implements WriteableCommandLine {
041    private final Map optionToProperties = new HashMap();
042//    private final Properties properties = new Properties();
043    private final List options = new ArrayList();
044    private final Map nameToOption = new HashMap();
045    private final Map values = new HashMap();
046    private final Map switches = new HashMap();
047    private final Map defaultValues = new HashMap();
048    private final Map defaultSwitches = new HashMap();
049    private final List normalised;
050    private final Set prefixes;
051    private Option currentOption;
052    private String checkForOption;
053
054    /**
055     * Creates a new WriteableCommandLineImpl rooted on the specified Option, to
056     * hold the parsed arguments.
057     *
058     * @param rootOption the CommandLine's root Option
059     * @param arguments the arguments this CommandLine represents
060     */
061    public WriteableCommandLineImpl(final Option rootOption,
062                                    final List arguments) {
063        this.prefixes = rootOption.getPrefixes();
064        this.normalised = arguments;
065        setCurrentOption(rootOption);
066    }
067
068    public Option getCurrentOption() {
069        return currentOption;
070    }
071
072    public void setCurrentOption(Option currentOption) {
073        this.currentOption = currentOption;
074    }
075
076    public void addOption(Option option) {
077        options.add(option);
078        nameToOption.put(option.getPreferredName(), option);
079
080        for (Iterator i = option.getTriggers().iterator(); i.hasNext();) {
081            nameToOption.put(i.next(), option);
082        }
083
084        // ensure that all parent options are also added
085        Option parent = option.getParent();
086        while (parent != null && !options.contains(parent)) {
087            options.add(parent);
088            parent = parent.getParent();
089        }
090    }
091
092    public void addValue(final Option option,
093                         final Object value) {
094        if (option instanceof Argument) {
095            addOption(option);
096        }
097
098        List valueList = (List) values.get(option);
099
100        if (valueList == null) {
101            valueList = new ArrayList();
102            values.put(option, valueList);
103        }
104
105        valueList.add(value);
106    }
107
108    public void addSwitch(final Option option,
109                          final boolean value) {
110        addOption(option);
111
112        if (switches.containsKey(option)) {
113            throw new IllegalStateException(ResourceHelper.getResourceHelper().getMessage(ResourceConstants.SWITCH_ALREADY_SET));
114        } else {
115            switches.put(option, value ? Boolean.TRUE : Boolean.FALSE);
116        }
117    }
118
119    public boolean hasOption(final Option option) {
120        final boolean present = options.contains(option);
121
122        return present;
123    }
124
125    public Option getOption(final String trigger) {
126        return (Option) nameToOption.get(trigger);
127    }
128
129    public List getValues(final Option option,
130                          List defaultValues) {
131        // initialize the return list
132        List valueList = (List) values.get(option);
133
134        // grab the correct default values
135        if (defaultValues == null || defaultValues.isEmpty()) {
136            defaultValues = (List) this.defaultValues.get(option);
137        }
138
139        // augment the list with the default values
140        if (defaultValues != null && !defaultValues.isEmpty()) {
141            if (valueList == null || valueList.isEmpty()) {
142                valueList = defaultValues;
143            } else {
144                // if there are more default values as specified, add them to
145                // the list.
146                if (defaultValues.size() > valueList.size()) {
147                    // copy the list first
148                    valueList = new ArrayList(valueList);
149                    for (int i=valueList.size(); i<defaultValues.size(); i++) {
150                        valueList.add(defaultValues.get(i));
151                    }
152                }
153            }
154        }
155
156        return valueList == null ? Collections.EMPTY_LIST : valueList;
157    }
158
159    public List getUndefaultedValues(Option option) {
160      // First grab the command line values
161      List valueList = (List) values.get(option);
162
163      // Finally use an empty list
164      if (valueList == null) {
165        valueList = Collections.EMPTY_LIST;
166      }
167
168      return valueList;
169    }
170
171    public Boolean getSwitch(final Option option,
172                             final Boolean defaultValue) {
173        // First grab the command line values
174        Boolean bool = (Boolean) switches.get(option);
175
176        // Secondly try the defaults supplied to the method
177        if (bool == null) {
178            bool = defaultValue;
179        }
180
181        // Thirdly try the option's default values
182        if (bool == null) {
183            bool = (Boolean) this.defaultSwitches.get(option);
184        }
185
186        return bool;
187    }
188
189    public String getProperty(final String property) {
190        return getProperty(new PropertyOption(), property);
191    }
192
193    public void addProperty(final Option option,
194                            final String property,
195                            final String value) {
196        Properties properties = (Properties) optionToProperties.get(option);
197        if (properties == null) {
198            properties = new Properties();
199            optionToProperties.put(option, properties);
200        }
201        properties.setProperty(property, value);
202    }
203
204    public void addProperty(final String property, final String value) {
205        addProperty(new PropertyOption(), property, value);
206    }
207
208    public String getProperty(final Option option,
209                              final String property,
210                              final String defaultValue) {
211        Properties properties = (Properties) optionToProperties.get(option);
212        if (properties == null) {
213            return defaultValue;
214        }
215        return properties.getProperty(property, defaultValue);
216    }
217
218    public Set getProperties(final Option option) {
219        Properties properties = (Properties) optionToProperties.get(option);
220        if (properties == null) {
221            return Collections.EMPTY_SET;
222        }
223        return Collections.unmodifiableSet(properties.keySet());
224    }
225
226    public Set getProperties() {
227        return getProperties(new PropertyOption());
228    }
229
230    /**
231     * Tests whether the passed in trigger looks like an option. This
232     * implementation first checks whether the passed in string starts with a
233     * prefix that indicates an option. If this is the case, it is also checked
234     * whether an option of this name is known for the current option. (This can
235     * lead to reentrant invocations of this method, so care has to be taken
236     * about this.)
237     *
238     * @param trigger the command line element to test
239     * @return a flag whether this element seems to be an option
240     */
241    public boolean looksLikeOption(final String trigger)
242    {
243        if (checkForOption != null)
244        {
245            // this is a reentrant call
246            return !checkForOption.equals(trigger);
247        }
248
249        checkForOption = trigger;
250        try
251        {
252            for (final Iterator i = prefixes.iterator(); i.hasNext();)
253            {
254                final String prefix = (String) i.next();
255
256                if (trigger.startsWith(prefix))
257                {
258                    if (getCurrentOption().canProcess(this, trigger)
259                            || getCurrentOption().findOption(trigger) != null)
260                    {
261                        return true;
262                    }
263                }
264            }
265
266            return false;
267        }
268        finally
269        {
270            checkForOption = null;
271        }
272    }
273
274    public String toString() {
275        final StringBuffer buffer = new StringBuffer();
276
277        // need to add group header
278        for (final Iterator i = normalised.iterator(); i.hasNext();) {
279            final String arg = (String) i.next();
280
281            if (arg.indexOf(' ') >= 0) {
282                buffer.append("\"").append(arg).append("\"");
283            } else {
284                buffer.append(arg);
285            }
286
287            if (i.hasNext()) {
288                buffer.append(' ');
289            }
290        }
291
292        return buffer.toString();
293    }
294
295    public List getOptions() {
296        return Collections.unmodifiableList(options);
297    }
298
299    public Set getOptionTriggers() {
300        return Collections.unmodifiableSet(nameToOption.keySet());
301    }
302
303    public void setDefaultValues(final Option option,
304                                 final List defaults) {
305        if (defaults == null) {
306            defaultValues.remove(option);
307        } else {
308            defaultValues.put(option, defaults);
309        }
310    }
311
312    public void setDefaultSwitch(final Option option,
313                                 final Boolean defaultSwitch) {
314        if (defaultSwitch == null) {
315            defaultSwitches.remove(option);
316        } else {
317            defaultSwitches.put(option, defaultSwitch);
318        }
319    }
320
321    public List getNormalised() {
322        return Collections.unmodifiableList(normalised);
323    }
324}