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.List;
023import java.util.ListIterator;
024import java.util.Set;
025
026import org.apache.commons.cli2.Argument;
027import org.apache.commons.cli2.DisplaySetting;
028import org.apache.commons.cli2.Group;
029import org.apache.commons.cli2.Option;
030import org.apache.commons.cli2.OptionException;
031import org.apache.commons.cli2.Parent;
032import org.apache.commons.cli2.WriteableCommandLine;
033
034/**
035 * A base implementation of Parent providing limited ground work for further
036 * Parent implementations.
037 */
038public abstract class ParentImpl
039    extends OptionImpl implements Parent {
040    private static final char NUL = '\0';
041    private final Group children;
042    private final Argument argument;
043    private final String description;
044
045    protected ParentImpl(final Argument argument,
046                         final Group children,
047                         final String description,
048                         final int id,
049                         final boolean required) {
050        super(id, required);
051        this.children = children;
052        this.argument = argument;
053        this.description = description;
054    }
055
056    /*
057     * (non-Javadoc)
058     *
059     * @see org.apache.commons.cli2.Option#process(org.apache.commons.cli2.CommandLine,
060     *      java.util.ListIterator)
061     */
062    public void process(final WriteableCommandLine commandLine,
063                        final ListIterator arguments)
064        throws OptionException {
065        if (argument != null) {
066            handleInitialSeparator(arguments, argument.getInitialSeparator());
067        }
068
069        processParent(commandLine, arguments);
070
071        if (argument != null) {
072            argument.processValues(commandLine, arguments, this);
073        }
074
075        if ((children != null) && children.canProcess(commandLine, arguments)) {
076            children.process(commandLine, arguments);
077        }
078    }
079
080    /*
081     * (non-Javadoc)
082     *
083     * @see org.apache.commons.cli2.Option#canProcess(java.lang.String)
084     */
085    public boolean canProcess(final WriteableCommandLine commandLine,
086                              final String arg) {
087        final Set triggers = getTriggers();
088
089        if (argument != null) {
090            final char separator = argument.getInitialSeparator();
091
092            // if there is a valid separator character
093            if (separator != NUL) {
094                final int initialIndex = arg.indexOf(separator);
095
096                // if there is a separator present
097                if (initialIndex > 0) {
098                    return triggers.contains(arg.substring(0, initialIndex));
099                }
100            }
101        }
102
103        return triggers.contains(arg);
104    }
105
106    /*
107     * (non-Javadoc)
108     *
109     * @see org.apache.commons.cli2.Option#prefixes()
110     */
111    public Set getPrefixes() {
112        return (children == null) ? Collections.EMPTY_SET : children.getPrefixes();
113    }
114
115    /*
116     * (non-Javadoc)
117     *
118     * @see org.apache.commons.cli2.Option#validate(org.apache.commons.cli2.CommandLine)
119     */
120    public void validate(WriteableCommandLine commandLine)
121        throws OptionException {
122        if (commandLine.hasOption(this)) {
123            if (argument != null) {
124                argument.validate(commandLine, this);
125            }
126
127            if (children != null) {
128                children.validate(commandLine);
129            }
130        }
131    }
132
133    /*
134     * (non-Javadoc)
135     *
136     * @see org.apache.commons.cli2.Option#appendUsage(java.lang.StringBuffer,
137     *      java.util.Set, java.util.Comparator)
138     */
139    public void appendUsage(final StringBuffer buffer,
140                            final Set helpSettings,
141                            final Comparator comp) {
142        final boolean displayArgument =
143            (this.argument != null) &&
144            helpSettings.contains(DisplaySetting.DISPLAY_PARENT_ARGUMENT);
145        final boolean displayChildren =
146            (this.children != null) &&
147            helpSettings.contains(DisplaySetting.DISPLAY_PARENT_CHILDREN);
148
149        if (displayArgument) {
150            buffer.append(' ');
151            argument.appendUsage(buffer, helpSettings, comp);
152        }
153
154        if (displayChildren) {
155            buffer.append(' ');
156            children.appendUsage(buffer, helpSettings, comp);
157        }
158    }
159
160    /**
161     * @return a description of this parent option
162     */
163    public String getDescription() {
164        return description;
165    }
166
167    /*
168     * (non-Javadoc)
169     *
170     * @see org.apache.commons.cli2.Option#helpLines(int, java.util.Set,
171     *      java.util.Comparator)
172     */
173    public List helpLines(final int depth,
174                          final Set helpSettings,
175                          final Comparator comp) {
176        final List helpLines = new ArrayList();
177        helpLines.add(new HelpLineImpl(this, depth));
178
179        if (helpSettings.contains(DisplaySetting.DISPLAY_PARENT_ARGUMENT) && (argument != null)) {
180            helpLines.addAll(argument.helpLines(depth + 1, helpSettings, comp));
181        }
182
183        if (helpSettings.contains(DisplaySetting.DISPLAY_PARENT_CHILDREN) && (children != null)) {
184            helpLines.addAll(children.helpLines(depth + 1, helpSettings, comp));
185        }
186
187        return helpLines;
188    }
189
190    /**
191     * @return Returns the argument.
192     */
193    public Argument getArgument() {
194        return argument;
195    }
196
197    /**
198     * @return Returns the children.
199     */
200    public Group getChildren() {
201        return children;
202    }
203
204    /**
205     * Split the token using the specified separator character.
206     * @param arguments the current position in the arguments iterator
207     * @param separator the separator char to split on
208     */
209    private void handleInitialSeparator(final ListIterator arguments,
210                                        final char separator) {
211        // next token
212        final String newArgument = (String) arguments.next();
213
214        // split the token
215        final int initialIndex = newArgument.indexOf(separator);
216
217        if (initialIndex > 0) {
218            arguments.remove();
219            arguments.add(newArgument.substring(0, initialIndex));
220            String value = newArgument.substring(initialIndex + 1);
221            // The value obviously isn't an option, so we need to quote it if looks like an option.
222            // The quotes will be removed later
223            if (value.startsWith("-")) {
224                value = '"' + value + '"';
225            }
226            arguments.add(value);
227            arguments.previous();
228        }
229
230        arguments.previous();
231    }
232
233    /*
234     * @see org.apache.commons.cli2.Option#findOption(java.lang.String)
235     */
236    public Option findOption(final String trigger) {
237        final Option found = super.findOption(trigger);
238
239        if ((found == null) && (children != null)) {
240            return children.findOption(trigger);
241        } else {
242            return found;
243        }
244    }
245
246    public void defaults(final WriteableCommandLine commandLine) {
247        super.defaults(commandLine);
248
249        if (argument != null) {
250            argument.defaultValues(commandLine, this);
251        }
252
253        if (children != null) {
254            children.defaults(commandLine);
255        }
256    }
257}