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.Comparator;
021import java.util.Iterator;
022import java.util.List;
023import java.util.Set;
024
025import org.apache.commons.cli2.Argument;
026import org.apache.commons.cli2.Option;
027import org.apache.commons.cli2.OptionException;
028import org.apache.commons.cli2.WriteableCommandLine;
029import org.apache.commons.cli2.resource.ResourceConstants;
030import 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 */
038public 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}