001 /*
002 * Copyright 2003-2005 The Apache Software Foundation
003 * Copyright 2005 Stephen McConnell
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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 net.dpml.cli.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 net.dpml.cli.Argument;
026 import net.dpml.cli.Option;
027 import net.dpml.cli.OptionException;
028 import net.dpml.cli.WriteableCommandLine;
029 import net.dpml.cli.resource.ResourceConstants;
030 import net.dpml.cli.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 * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
038 * @version @PROJECT-VERSION@
039 */
040 public class SourceDestArgument extends ArgumentImpl
041 {
042 private final Argument m_source;
043 private final Argument m_dest;
044
045 /**
046 * Creates a SourceDestArgument using defaults where possible.
047 *
048 * @param source the variable size Argument
049 * @param dest the fixed size Argument
050 */
051 public SourceDestArgument(
052 final Argument source, final Argument dest )
053 {
054 this(
055 source,
056 dest,
057 DEFAULT_INITIAL_SEPARATOR,
058 DEFAULT_SUBSEQUENT_SEPARATOR,
059 DEFAULT_CONSUME_REMAINING,
060 null );
061 }
062
063 /**
064 * Creates a SourceDestArgument using the specified parameters.
065 *
066 * @param source the variable size Argument
067 * @param dest the fixed size Argument
068 * @param initialSeparator the inistial separator to use
069 * @param subsequentSeparator the subsequent separator to use
070 * @param consumeRemaining the token triggering consume remaining behaviour
071 * @param defaultValues the default values for the SourceDestArgument
072 */
073 public SourceDestArgument(
074 final Argument source, final Argument dest, final char initialSeparator,
075 final char subsequentSeparator, final String consumeRemaining,
076 final List defaultValues )
077 {
078 super(
079 "SourceDestArgument", null, sum( source.getMinimum(), dest.getMinimum() ),
080 sum( source.getMaximum(), dest.getMaximum() ), initialSeparator,
081 subsequentSeparator, null, consumeRemaining, defaultValues, 0 );
082
083 m_source = source;
084 m_dest = dest;
085
086 if( dest.getMinimum() != dest.getMaximum() )
087 {
088 throw new IllegalArgumentException(
089 ResourceHelper.getResourceHelper().getMessage(
090 ResourceConstants.SOURCE_DEST_MUST_ENFORCE_VALUES ) );
091 }
092 }
093
094 private static int sum( final int a, final int b )
095 {
096 return Math.max( a, Math.max( b, a + b ) );
097 }
098
099 /**
100 * Appends usage information to the specified StringBuffer
101 *
102 * @param buffer the buffer to append to
103 * @param helpSettings a set of display settings @see DisplaySetting
104 * @param comp a comparator used to sort the Options
105 */
106 public void appendUsage(
107 final StringBuffer buffer, final Set helpSettings, final Comparator comp )
108 {
109 final int length = buffer.length();
110
111 m_source.appendUsage( buffer, helpSettings, comp );
112
113 if( buffer.length() != length )
114 {
115 buffer.append( ' ' );
116 }
117
118 m_dest.appendUsage( buffer, helpSettings, comp );
119 }
120
121 /**
122 * Builds up a list of HelpLineImpl instances to be presented by HelpFormatter.
123 *
124 * @see net.dpml.cli.HelpLine
125 * @see net.dpml.cli.util.HelpFormatter
126 * @param depth the initial indent depth
127 * @param helpSettings the HelpSettings that should be applied
128 * @param comp a comparator used to sort options when applicable.
129 * @return a List of HelpLineImpl objects
130 */
131 public List helpLines(
132 int depth, Set helpSettings, Comparator comp )
133 {
134 final List helpLines = new ArrayList();
135 helpLines.addAll( m_source.helpLines( depth, helpSettings, comp ) );
136 helpLines.addAll( m_dest.helpLines( depth, helpSettings, comp ) );
137 return helpLines;
138 }
139
140 /**
141 * Checks that the supplied CommandLine is valid with respect to the
142 * suppled option.
143 *
144 * @param commandLine the CommandLine to check.
145 * @param option the option to evaluate
146 * @throws OptionException if the CommandLine is not valid.
147 */
148 public void validate( WriteableCommandLine commandLine, Option option )
149 throws OptionException
150 {
151 final List values = commandLine.getValues( option );
152
153 final int limit = values.size() - m_dest.getMinimum();
154 int count = 0;
155
156 final Iterator i = values.iterator();
157
158 while( count++ < limit )
159 {
160 commandLine.addValue( m_source, i.next() );
161 }
162
163 while( i.hasNext() )
164 {
165 commandLine.addValue( m_dest, i.next() );
166 }
167
168 m_source.validate( commandLine, m_source );
169 m_dest.validate( commandLine, m_dest );
170 }
171
172 /**
173 * Indicates whether this Option will be able to process the particular
174 * argument.
175 *
176 * @param commandLine the CommandLine object to store defaults in
177 * @param arg the argument to be tested
178 * @return true if the argument can be processed by this Option
179 */
180 public boolean canProcess(
181 final WriteableCommandLine commandLine, final String arg )
182 {
183 return m_source.canProcess( commandLine, arg ) || m_dest.canProcess( commandLine, arg );
184 }
185 }