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;
034import org.apache.commons.cli2.resource.ResourceHelper;
035
036/**
037 * A Parent implementation representing normal switch options.
038 * For example: <code>+d|-d</code> or <code>--enable-x|--disable-x</code>.
039 */
040public class Switch
041    extends ParentImpl {
042    /** i18n */
043    public static final ResourceHelper resources = ResourceHelper.getResourceHelper();
044
045    /**
046     * The default prefix for enabled switches
047     */
048    public static final String DEFAULT_ENABLED_PREFIX = "+";
049
050    /**
051     * The default prefix for disabled switches
052     */
053    public static final String DEFAULT_DISABLED_PREFIX = "-";
054    private final String enabledPrefix;
055    private final String disabledPrefix;
056    private final Set triggers;
057    private final String preferredName;
058    private final Set aliases;
059    private final Set prefixes;
060    private final Boolean defaultSwitch;
061
062    /**
063     * Creates a new Switch with the specified parameters
064     * @param enabledPrefix the prefix used for enabled switches
065     * @param disabledPrefix the prefix used for disabled switches
066     * @param preferredName the preferred name of the switch
067     * @param aliases the aliases by which the Switch is known
068     * @param description a description of the Switch
069     * @param required whether the Option is strictly required
070     * @param argument the Argument belonging to this Parent, or null
071     * @param children the Group children belonging to this Parent, ot null
072     * @param id the unique identifier for this Option
073     * @throws IllegalArgumentException if the preferredName or an alias isn't
074     *     prefixed with enabledPrefix or disabledPrefix
075     */
076    public Switch(final String enabledPrefix,
077                  final String disabledPrefix,
078                  final String preferredName,
079                  final Set aliases,
080                  final String description,
081                  final boolean required,
082                  final Argument argument,
083                  final Group children,
084                  final int id,
085                  final Boolean switchDefault) {
086        super(argument, children, description, id, required);
087
088        if (enabledPrefix == null) {
089            throw new IllegalArgumentException(resources.getMessage(ResourceConstants.SWITCH_NO_ENABLED_PREFIX));
090        }
091
092        if (disabledPrefix == null) {
093            throw new IllegalArgumentException(resources.getMessage(ResourceConstants.SWITCH_NO_DISABLED_PREFIX));
094        }
095
096        if (enabledPrefix.startsWith(disabledPrefix)) {
097            throw new IllegalArgumentException(resources.getMessage(ResourceConstants.SWITCH_ENABLED_STARTS_WITH_DISABLED));
098        }
099
100        if (disabledPrefix.startsWith(enabledPrefix)) {
101            throw new IllegalArgumentException(resources.getMessage(ResourceConstants.SWITCH_DISABLED_STARTWS_WITH_ENABLED));
102        }
103
104        this.enabledPrefix = enabledPrefix;
105        this.disabledPrefix = disabledPrefix;
106        this.preferredName = preferredName;
107
108        if ((preferredName == null) || (preferredName.length() < 1)) {
109            throw new IllegalArgumentException(resources.getMessage(ResourceConstants.SWITCH_PREFERRED_NAME_TOO_SHORT));
110        }
111
112        final Set newTriggers = new HashSet();
113        newTriggers.add(enabledPrefix + preferredName);
114        newTriggers.add(disabledPrefix + preferredName);
115        this.triggers = Collections.unmodifiableSet(newTriggers);
116
117        if (aliases == null) {
118            this.aliases = Collections.EMPTY_SET;
119        } else {
120            this.aliases = Collections.unmodifiableSet(new HashSet(aliases));
121
122            for (final Iterator i = aliases.iterator(); i.hasNext();) {
123                final String alias = (String) i.next();
124                newTriggers.add(enabledPrefix + alias);
125                newTriggers.add(disabledPrefix + alias);
126            }
127        }
128
129        final Set newPrefixes = new HashSet(super.getPrefixes());
130        newPrefixes.add(enabledPrefix);
131        newPrefixes.add(disabledPrefix);
132        this.prefixes = Collections.unmodifiableSet(newPrefixes);
133
134        this.defaultSwitch = switchDefault;
135
136        checkPrefixes(newPrefixes);
137    }
138
139    public void processParent(final WriteableCommandLine commandLine,
140                              final ListIterator arguments)
141        throws OptionException {
142        final String arg = (String) arguments.next();
143
144        if (canProcess(commandLine, arg)) {
145            if (arg.startsWith(enabledPrefix)) {
146                commandLine.addSwitch(this, true);
147                arguments.set(enabledPrefix + preferredName);
148            }
149
150            if (arg.startsWith(disabledPrefix)) {
151                commandLine.addSwitch(this, false);
152                arguments.set(disabledPrefix + preferredName);
153            }
154        } else {
155            throw new OptionException(this, ResourceConstants.UNEXPECTED_TOKEN, arg);
156        }
157    }
158
159    public Set getTriggers() {
160        return triggers;
161    }
162
163    public Set getPrefixes() {
164        return prefixes;
165    }
166
167    public void validate(WriteableCommandLine commandLine)
168        throws OptionException {
169        if (isRequired() && !commandLine.hasOption(this)) {
170            throw new OptionException(this, ResourceConstants.OPTION_MISSING_REQUIRED,
171                                      getPreferredName());
172        }
173
174        super.validate(commandLine);
175    }
176
177    public void appendUsage(final StringBuffer buffer,
178                            final Set helpSettings,
179                            final Comparator comp) {
180        // do we display optionality
181        final boolean optional =
182            !isRequired() && helpSettings.contains(DisplaySetting.DISPLAY_OPTIONAL);
183        final boolean displayAliases = helpSettings.contains(DisplaySetting.DISPLAY_ALIASES);
184        final boolean disabled = helpSettings.contains(DisplaySetting.DISPLAY_SWITCH_DISABLED);
185        final boolean enabled =
186            !disabled || helpSettings.contains(DisplaySetting.DISPLAY_SWITCH_ENABLED);
187        final boolean both = disabled && enabled;
188
189        if (optional) {
190            buffer.append('[');
191        }
192
193        if (enabled) {
194            buffer.append(enabledPrefix).append(preferredName);
195        }
196
197        if (both) {
198            buffer.append('|');
199        }
200
201        if (disabled) {
202            buffer.append(disabledPrefix).append(preferredName);
203        }
204
205        if (displayAliases && !aliases.isEmpty()) {
206            buffer.append(" (");
207
208            final List list = new ArrayList(aliases);
209            Collections.sort(list);
210
211            for (final Iterator i = list.iterator(); i.hasNext();) {
212                final String alias = (String) i.next();
213
214                if (enabled) {
215                    buffer.append(enabledPrefix).append(alias);
216                }
217
218                if (both) {
219                    buffer.append('|');
220                }
221
222                if (disabled) {
223                    buffer.append(disabledPrefix).append(alias);
224                }
225
226                if (i.hasNext()) {
227                    buffer.append(',');
228                }
229            }
230
231            buffer.append(')');
232        }
233
234        super.appendUsage(buffer, helpSettings, comp);
235
236        if (optional) {
237            buffer.append(']');
238        }
239    }
240
241    public String getPreferredName() {
242        return enabledPrefix + preferredName;
243    }
244
245    public void defaults(final WriteableCommandLine commandLine) {
246        commandLine.setDefaultSwitch(this, defaultSwitch);
247    }
248}