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.option;
018    
019    import java.util.ArrayList;
020    import java.util.Collections;
021    import java.util.Comparator;
022    import java.util.HashSet;
023    import java.util.Iterator;
024    import java.util.List;
025    import java.util.ListIterator;
026    import java.util.Set;
027    
028    import org.apache.commons.cli2.Argument;
029    import org.apache.commons.cli2.DisplaySetting;
030    import org.apache.commons.cli2.Group;
031    import org.apache.commons.cli2.OptionException;
032    import org.apache.commons.cli2.WriteableCommandLine;
033    import org.apache.commons.cli2.resource.ResourceConstants;
034    
035    /**
036     * A Parent implementation representing normal options.
037     */
038    public class DefaultOption
039        extends ParentImpl {
040        /**
041         * The default token used to prefix a short option
042         */
043        public static final String DEFAULT_SHORT_PREFIX = "-";
044    
045        /**
046         * The default token used to prefix a long option
047         */
048        public static final String DEFAULT_LONG_PREFIX = "--";
049    
050        /**
051         * The default value for the burstEnabled constructor parameter
052         */
053        public static final boolean DEFAULT_BURST_ENABLED = true;
054        private final String preferredName;
055        private final Set aliases;
056        private final Set burstAliases;
057        private final Set triggers;
058        private final Set prefixes;
059        private final String shortPrefix;
060        private final boolean burstEnabled;
061        private final int burstLength;
062    
063        /**
064         * Creates a new DefaultOption
065         *
066         * @param shortPrefix the prefix used for short options
067         * @param longPrefix the prefix used for long options
068         * @param burstEnabled should option bursting be enabled
069         * @param preferredName the preferred name for this Option, this should begin with either shortPrefix or longPrefix
070         * @param description a description of this Option
071         * @param aliases the alternative names for this Option
072         * @param burstAliases the aliases that can be burst
073         * @param required whether the Option is strictly required
074         * @param argument the Argument belonging to this Parent, or null
075         * @param children the Group children belonging to this Parent, ot null
076         * @param id the unique identifier for this Option
077         * @throws IllegalArgumentException if the preferredName or an alias isn't
078         *     prefixed with shortPrefix or longPrefix
079         */
080        public DefaultOption(final String shortPrefix,
081                             final String longPrefix,
082                             final boolean burstEnabled,
083                             final String preferredName,
084                             final String description,
085                             final Set aliases,
086                             final Set burstAliases,
087                             final boolean required,
088                             final Argument argument,
089                             final Group children,
090                             final int id) {
091            super(argument, children, description, id, required);
092    
093            this.shortPrefix = shortPrefix;
094            this.burstEnabled = burstEnabled;
095    
096            this.burstLength = shortPrefix.length() + 1;
097    
098            this.preferredName = preferredName;
099            this.aliases =
100                (aliases == null) ? Collections.EMPTY_SET
101                                  : Collections.unmodifiableSet(new HashSet(aliases));
102    
103            this.burstAliases =
104                (burstAliases == null) ? Collections.EMPTY_SET
105                                       : Collections.unmodifiableSet(new HashSet(burstAliases));
106    
107            final Set newTriggers = new HashSet();
108            newTriggers.add(preferredName);
109            newTriggers.addAll(this.aliases);
110            newTriggers.addAll(this.burstAliases);
111            this.triggers = Collections.unmodifiableSet(newTriggers);
112    
113            final Set newPrefixes = new HashSet(super.getPrefixes());
114            newPrefixes.add(shortPrefix);
115            newPrefixes.add(longPrefix);
116            this.prefixes = Collections.unmodifiableSet(newPrefixes);
117    
118            checkPrefixes(newPrefixes);
119        }
120    
121        public boolean canProcess(final WriteableCommandLine commandLine,
122                                  final String argument) {
123            return (argument != null) &&
124                   (super.canProcess(commandLine, argument) ||
125                   ((argument.length() >= burstLength) &&
126                   burstAliases.contains(argument.substring(0, burstLength))));
127        }
128    
129        public void processParent(WriteableCommandLine commandLine,
130                                  ListIterator arguments)
131            throws OptionException {
132            final String argument = (String) arguments.next();
133    
134            if (triggers.contains(argument)) {
135                commandLine.addOption(this);
136                arguments.set(preferredName);
137            } else if (burstEnabled && (argument.length() >= burstLength)) {
138                final String burst = argument.substring(0, burstLength);
139    
140                if (burstAliases.contains(burst)) {
141                    commandLine.addOption(this);
142    
143                    //HMM test bursting all vs bursting one by one.
144                    arguments.set(preferredName);
145    
146                    if (getArgument() == null) {
147                        arguments.add(shortPrefix + argument.substring(burstLength));
148                    } else {
149                        arguments.add(argument.substring(burstLength));
150                    }
151    
152                    arguments.previous();
153                } else {
154                    throw new OptionException(this, ResourceConstants.CANNOT_BURST, argument);
155                }
156            } else {
157                throw new OptionException(this, ResourceConstants.UNEXPECTED_TOKEN, argument);
158            }
159        }
160    
161        public Set getTriggers() {
162            return triggers;
163        }
164    
165        public Set getPrefixes() {
166            return prefixes;
167        }
168    
169        public void validate(WriteableCommandLine commandLine)
170            throws OptionException {
171            if (isRequired() && !commandLine.hasOption(this)) {
172                throw new OptionException(this, ResourceConstants.OPTION_MISSING_REQUIRED,
173                                          getPreferredName());
174            }
175    
176            super.validate(commandLine);
177        }
178    
179        public void appendUsage(final StringBuffer buffer,
180                                final Set helpSettings,
181                                final Comparator comp) {
182            // do we display optionality
183            final boolean optional =
184                !isRequired() && helpSettings.contains(DisplaySetting.DISPLAY_OPTIONAL);
185            final boolean displayAliases = helpSettings.contains(DisplaySetting.DISPLAY_ALIASES);
186    
187            if (optional) {
188                buffer.append('[');
189            }
190    
191            buffer.append(preferredName);
192    
193            if (displayAliases && !aliases.isEmpty()) {
194                buffer.append(" (");
195    
196                final List list = new ArrayList(aliases);
197                Collections.sort(list);
198    
199                for (final Iterator i = list.iterator(); i.hasNext();) {
200                    final String alias = (String) i.next();
201                    buffer.append(alias);
202    
203                    if (i.hasNext()) {
204                        buffer.append(',');
205                    }
206                }
207    
208                buffer.append(')');
209            }
210    
211            super.appendUsage(buffer, helpSettings, comp);
212    
213            if (optional) {
214                buffer.append(']');
215            }
216        }
217    
218        public String getPreferredName() {
219            return preferredName;
220        }
221    }