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.option;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.Comparator;
022import java.util.HashSet;
023import java.util.Iterator;
024import java.util.List;
025import java.util.ListIterator;
026import java.util.Set;
027
028import org.apache.commons.cli2.Argument;
029import org.apache.commons.cli2.DisplaySetting;
030import org.apache.commons.cli2.Group;
031import org.apache.commons.cli2.OptionException;
032import org.apache.commons.cli2.WriteableCommandLine;
033import org.apache.commons.cli2.resource.ResourceConstants;
034
035/**
036 * A Parent implementation representing normal options.
037 */
038public 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}