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     */
017    package org.apache.commons.cli2.commandline;
018    
019    import java.util.ArrayList;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Properties;
026    import java.util.Set;
027    
028    import org.apache.commons.cli2.Argument;
029    import org.apache.commons.cli2.Option;
030    import org.apache.commons.cli2.WriteableCommandLine;
031    import org.apache.commons.cli2.option.PropertyOption;
032    import org.apache.commons.cli2.resource.ResourceConstants;
033    import org.apache.commons.cli2.resource.ResourceHelper;
034    
035    /**
036     * A WriteableCommandLine implementation allowing Options to write their
037     * processed information to a CommandLine.
038     */
039    public 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    }