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.Comparator;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.Set;
024    
025    import org.apache.commons.cli2.Argument;
026    import org.apache.commons.cli2.Option;
027    import org.apache.commons.cli2.OptionException;
028    import org.apache.commons.cli2.WriteableCommandLine;
029    import org.apache.commons.cli2.resource.ResourceConstants;
030    import org.apache.commons.cli2.resource.ResourceHelper;
031    
032    /**
033     * An Argument implementation that allows a variable size Argument to precede a
034     * fixed size argument.  The canonical example of it's use is in the unix
035     * <code>cp</code> command where a number of source can be specified with
036     * exactly one destination specfied at the end.
037     */
038    public class SourceDestArgument
039        extends ArgumentImpl {
040        private final Argument source;
041        private final Argument dest;
042    
043        /**
044         * Creates a SourceDestArgument using defaults where possible.
045         *
046         * @param source the variable size Argument
047         * @param dest the fixed size Argument
048         */
049        public SourceDestArgument(final Argument source,
050                                  final Argument dest) {
051            this(source, dest, DEFAULT_INITIAL_SEPARATOR, DEFAULT_SUBSEQUENT_SEPARATOR,
052                 DEFAULT_CONSUME_REMAINING, null);
053        }
054    
055        /**
056         * Creates a SourceDestArgument using the specified parameters.
057         *
058         * @param source the variable size Argument
059         * @param dest the fixed size Argument
060         * @param initialSeparator the inistial separator to use
061         * @param subsequentSeparator the subsequent separator to use
062         * @param consumeRemaining the token triggering consume remaining behaviour
063         * @param defaultValues the default values for the SourceDestArgument
064         */
065        public SourceDestArgument(final Argument source,
066                                  final Argument dest,
067                                  final char initialSeparator,
068                                  final char subsequentSeparator,
069                                  final String consumeRemaining,
070                                  final List defaultValues) {
071            super("SourceDestArgument", null, sum(source.getMinimum(), dest.getMinimum()),
072                  sum(source.getMaximum(), dest.getMaximum()), initialSeparator, subsequentSeparator,
073                  null, consumeRemaining, defaultValues, 0);
074    
075            this.source = source;
076            this.dest = dest;
077    
078            if (dest.getMinimum() != dest.getMaximum()) {
079                throw new IllegalArgumentException(ResourceHelper.getResourceHelper().getMessage(ResourceConstants.SOURCE_DEST_MUST_ENFORCE_VALUES));
080            }
081        }
082    
083        private static int sum(final int a,
084                               final int b) {
085            return Math.max(a, Math.max(b, a + b));
086        }
087    
088        public void appendUsage(final StringBuffer buffer,
089                                final Set helpSettings,
090                                final Comparator comp) {
091            final int length = buffer.length();
092    
093            source.appendUsage(buffer, helpSettings, comp);
094    
095            if (buffer.length() != length) {
096                buffer.append(' ');
097            }
098    
099            dest.appendUsage(buffer, helpSettings, comp);
100        }
101    
102        public List helpLines(int depth,
103                              Set helpSettings,
104                              Comparator comp) {
105            final List helpLines = new ArrayList();
106            helpLines.addAll(source.helpLines(depth, helpSettings, comp));
107            helpLines.addAll(dest.helpLines(depth, helpSettings, comp));
108    
109            return helpLines;
110        }
111    
112        public void validate(WriteableCommandLine commandLine,
113                             Option option)
114            throws OptionException {
115            final List values = commandLine.getValues(option);
116    
117            final int limit = values.size() - dest.getMinimum();
118            int count = 0;
119    
120            final Iterator i = values.iterator();
121    
122            while (count++ < limit) {
123                commandLine.addValue(source, i.next());
124            }
125    
126            while (i.hasNext()) {
127                commandLine.addValue(dest, i.next());
128            }
129    
130            source.validate(commandLine, source);
131            dest.validate(commandLine, dest);
132        }
133    
134        public boolean canProcess(final WriteableCommandLine commandLine,
135                                  final String arg) {
136            return source.canProcess(commandLine, arg) || dest.canProcess(commandLine, arg);
137        }
138    }