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    import 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     */
040    public 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    }