Add a very rudimentary command line program that will check the integrity (a very basic integrity check) of a world.

This commit is contained in:
sk89q 2011-01-02 16:59:50 -08:00
parent 92fe984bfe
commit 96e848e880
42 changed files with 4276 additions and 0 deletions

View File

@ -33,6 +33,7 @@
<attribute name="Implementation-Version" value="${version}"/> <attribute name="Implementation-Version" value="${version}"/>
<attribute name="WorldEdit-Version" value="${version}"/> <attribute name="WorldEdit-Version" value="${version}"/>
<attribute name="Class-Path" value="truezip.jar"/> <attribute name="Class-Path" value="truezip.jar"/>
<attribute name="Main-Class" value="com.sk89q.worldedit.cli.Main"/>
</manifest> </manifest>
<jar jarfile="${dist.dir}/WorldEdit.jar" basedir="${build.dir}" manifest="manifest.mf"/> <jar jarfile="${dist.dir}/WorldEdit.jar" basedir="${build.dir}" manifest="manifest.mf"/>
</target> </target>

View File

@ -0,0 +1,94 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.cli;
import static java.util.Arrays.*;
import java.util.List;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
/**
*
* @author sk89q
*/
public class Main {
/**
* @param args
*/
public static void main(String[] _args) throws Throwable {
OptionParser parser = new OptionParser();
OptionSpec<String> world =
parser.accepts("world").withRequiredArg().ofType(String.class)
.describedAs("world directory").defaultsTo("world");
parser.acceptsAll(asList("?", "h"), "show help");
OptionSet options = parser.parse(_args);
List<String> args = options.nonOptionArguments();
if (args.size() != 1 || options.has("?")) {
System.err.println("worldedit <action>");
System.err.println();
parser.printHelpOn(System.err);
return;
}
System.err.println("WorldEdit v" + getVersion());
System.err.println("Copyright (c) 2010-2011 sk89q <http://www.sk89q.com>");
System.err.println();
String act = args.get(0);
String worldPath = options.valueOf(world);
if (act.equalsIgnoreCase("check")) {
new WorldChecker(worldPath);
} else {
System.err.println("Only valid action is 'check'.");
}
}
/**
* Get the version.
*
* @return
*/
public static String getVersion() {
Package p = com.sk89q.worldedit.cli.Main.class.getPackage();
if (p == null) {
p = Package.getPackage("com.sk89q.worldedit");
}
String version;
if (p == null) {
return "(unknown)";
} else {
version = p.getImplementationVersion();
if (version == null) {
return "(unknown)";
}
}
return version;
}
}

View File

@ -0,0 +1,92 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.cli;
import java.io.*;
import java.nio.*;
import java.util.regex.Pattern;
import org.jnbt.NBTInputStream;
import org.jnbt.Tag;
public class WorldChecker {
private File worldPath;
public WorldChecker(String path) {
worldPath = new File(path);
checkLevelDat();
checkFiles();
System.out.println("Done.");
}
public void checkLevelDat() {
try {
checkNBT(new File(worldPath, "level.dat"));
} catch (IOException e) {
System.out.println("BAD: level.dat: " + e.getMessage());
}
}
public void checkFiles() {
final Pattern chunkFilePattern = Pattern.compile("^c\\..*\\.dat$");
FileFilter folderFilter = new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory();
}
};
FileFilter chunkFilter = new FileFilter() {
@Override
public boolean accept(File f) {
return f.isFile()
&& chunkFilePattern.matcher(f.getName()).matches();
}
};
for (File l1 : worldPath.listFiles(folderFilter)) {
for (File l2 : l1.listFiles(folderFilter)) {
for (File chunkFile : l2.listFiles(chunkFilter)) {
checkChunkFile(chunkFile,
l1.getName(), l2.getName(), chunkFile.getName());
}
}
}
}
public void checkChunkFile(File f, String a, String b, String c) {
String id = a + "/" + b + "/" + c;
try {
checkNBT(f);
} catch (IOException e) {
System.out.println("BAD: " + id);
}
}
public void checkNBT(File file) throws IOException {
FileInputStream stream = new FileInputStream(file);
NBTInputStream nbt = new NBTInputStream(stream);
Tag tag = nbt.readTag();
}
}

View File

@ -0,0 +1,124 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static java.util.Collections.*;
import static joptsimple.internal.Strings.*;
/**
* @param <V> represents the type of the arguments this option accepts
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: AbstractOptionSpec.java,v 1.19 2009/11/28 21:31:53 pholser Exp $
*/
abstract class AbstractOptionSpec<V> implements OptionSpec<V> {
private final List<String> options = new ArrayList<String>();
private final String description;
protected AbstractOptionSpec( String option ) {
this( singletonList( option ), EMPTY );
}
protected AbstractOptionSpec( Collection<String> options, String description ) {
arrangeOptions( options );
this.description = description;
}
public final Collection<String> options() {
return unmodifiableCollection( options );
}
public final List<V> values( OptionSet detectedOptions ) {
return detectedOptions.valuesOf( this );
}
public final V value( OptionSet detectedOptions ) {
return detectedOptions.valueOf( this );
}
abstract List<V> defaultValues();
String description() {
return description;
}
protected abstract V convert( String argument );
abstract void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
String detectedArgument );
abstract boolean acceptsArguments();
abstract boolean requiresArgument();
abstract void accept( OptionSpecVisitor visitor );
private void arrangeOptions( Collection<String> unarranged ) {
if ( unarranged.size() == 1 ) {
options.addAll( unarranged );
return;
}
List<String> shortOptions = new ArrayList<String>();
List<String> longOptions = new ArrayList<String>();
for ( String each : unarranged ) {
if ( each.length() == 1 )
shortOptions.add( each );
else
longOptions.add( each );
}
sort( shortOptions );
sort( longOptions );
options.addAll( shortOptions );
options.addAll( longOptions );
}
@Override
public boolean equals( Object that ) {
if ( !( that instanceof AbstractOptionSpec<?> ) )
return false;
AbstractOptionSpec<?> other = (AbstractOptionSpec<?>) that;
return options.equals( other.options );
}
@Override
public int hashCode() {
return options.hashCode();
}
@Override
public String toString() {
return options.toString();
}
}

View File

@ -0,0 +1,57 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import static java.util.Collections.*;
import static joptsimple.ParserRules.*;
/**
* <p>Represents the <kbd>"-W"</kbd> form of long option specification.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: AlternativeLongOptionSpec.java,v 1.13 2009/09/28 01:12:48 pholser Exp $
*/
class AlternativeLongOptionSpec extends ArgumentAcceptingOptionSpec<String> {
AlternativeLongOptionSpec() {
super( singletonList( RESERVED_FOR_EXTENSIONS ), true, "Alternative form of long options" );
describedAs( "opt=value" );
}
@Override
protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
if ( !arguments.hasMore() )
throw new OptionMissingRequiredArgumentException( options() );
arguments.treatNextAsLongOption();
}
@Override
void accept( OptionSpecVisitor visitor ) {
visitor.visit( this );
}
}

View File

@ -0,0 +1,301 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import static java.util.Collections.*;
import joptsimple.internal.ReflectionException;
import static joptsimple.internal.Objects.*;
import static joptsimple.internal.Reflection.*;
import static joptsimple.internal.Strings.*;
/**
* <p>Specification of an option that accepts an argument.</p>
*
* <p>Instances are returned from {@link OptionSpecBuilder} methods to allow the formation
* of parser directives as sentences in a "fluent interface" language. For example:</p>
*
* <pre>
* <code>
* OptionParser parser = new OptionParser();
* parser.accepts( "c" ).withRequiredArg().<strong>ofType( Integer.class )</strong>;
* </code>
* </pre>
*
* <p>If no methods are invoked on an instance of this class, then that instance's option
* will treat its argument as a {@link String}.</p>
*
* @param <V> represents the type of the arguments this option accepts
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ArgumentAcceptingOptionSpec.java,v 1.43 2009/10/25 18:37:06 pholser Exp $
*/
public abstract class ArgumentAcceptingOptionSpec<V> extends AbstractOptionSpec<V> {
private static final char NIL_VALUE_SEPARATOR = '\u0000';
private final boolean argumentRequired;
private ValueConverter<V> converter;
private String argumentDescription = "";
private String valueSeparator = String.valueOf( NIL_VALUE_SEPARATOR );
private final List<V> defaultValues = new ArrayList<V>();
ArgumentAcceptingOptionSpec( String option, boolean argumentRequired ) {
super( option );
this.argumentRequired = argumentRequired;
}
ArgumentAcceptingOptionSpec( Collection<String> options, boolean argumentRequired, String description ) {
super( options, description );
this.argumentRequired = argumentRequired;
}
/**
* <p>Specifies a type to which arguments of this spec's option are to be
* converted.</p>
*
* <p>JOpt Simple accepts types that have either:</p>
*
* <ol>
* <li>a public static method called {@code valueOf} which accepts a single
* argument of type {@link String} and whose return type is the same as the class
* on which the method is declared. The {@code java.lang} primitive wrapper
* classes have such methods.</li>
*
* <li>a public constructor which accepts a single argument of type
* {@link String}.</li>
* </ol>
*
* <p>This class converts arguments using those methods in that order; that is,
* {@code valueOf} would be invoked before a one-{@link String}-arg constructor
* would.</p>
*
* <p>Invoking this method will trump any previous calls to this method or to
* {@link #withValuesConvertedBy(ValueConverter)}.
*
* @param <T> represents the runtime class of the desired option argument type
* @param argumentType desired type of arguments to this spec's option
* @return self, so that the caller can add clauses to the fluent interface sentence
* @throws NullPointerException if the type is {@code null}
* @throws IllegalArgumentException if the type does not have the standard conversion
* methods
*/
public final <T> ArgumentAcceptingOptionSpec<T> ofType( Class<T> argumentType ) {
return withValuesConvertedBy( findConverter( argumentType ) );
}
/**
* <p>Specifies a converter to use to translate arguments of this spec's option into
* Java objects. This is useful when converting to types that do not have the
* requisite factory method or constructor for {@link #ofType(Class)}.</p>
*
* <p>Invoking this method will trump any previous calls to this method or to
* {@link #ofType(Class)}.
*
* @param <T> represents the runtime class of the desired option argument type
* @param aConverter the converter to use
* @return self, so that the caller can add clauses to the fluent interface sentence
* @throws NullPointerException if the converter is {@code null}
*/
@SuppressWarnings( "unchecked" )
public final <T> ArgumentAcceptingOptionSpec<T> withValuesConvertedBy( ValueConverter<T> aConverter ) {
if ( aConverter == null )
throw new NullPointerException( "illegal null converter" );
converter = (ValueConverter<V>) aConverter;
return (ArgumentAcceptingOptionSpec<T>) this;
}
/**
* <p>Specifies a description for the argument of the option that this spec
* represents. This description is used when generating help information about
* the parser.</p>
*
* @param description describes the nature of the argument of this spec's
* option
* @return self, so that the caller can add clauses to the fluent interface sentence
*/
public final ArgumentAcceptingOptionSpec<V> describedAs( String description ) {
argumentDescription = description;
return this;
}
/**
* <p>Specifies a value separator for the argument of the option that this spec
* represents. This allows a single option argument to represent multiple values
* for the option. For example:</p>
*
* <pre>
* <code>
* parser.accepts( "z" ).withRequiredArg()
* .<strong>withValuesSeparatedBy( ',' )</strong>;
* OptionSet options = parser.parse( new String[] { "-z", "foo,bar,baz", "-z",
* "fizz", "-z", "buzz" } );
* </code>
* </pre>
*
* <p>Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar,
* baz, fizz, buzz]}.</p>
*
* <p>You cannot use Unicode U+0000 as the separator.</p>
*
* @param separator a character separator
* @return self, so that the caller can add clauses to the fluent interface sentence
* @throws IllegalArgumentException if the separator is Unicode U+0000
*/
public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( char separator ) {
if ( separator == NIL_VALUE_SEPARATOR )
throw new IllegalArgumentException( "cannot use U+0000 as separator" );
valueSeparator = String.valueOf( separator );
return this;
}
/**
* <p>Specifies a set of default values for the argument of the option that this spec
* represents.</p>
*
* @param value the first in the set of default argument values for this spec's option
* @param values the (optional) remainder of the set of default argument values for this spec's option
* @return self, so that the caller can add clauses to the fluent interface sentence
* @throws NullPointerException if {@code value}, {@code values}, or any elements of {@code values} are
* {@code null}
*/
public ArgumentAcceptingOptionSpec<V> defaultsTo( V value, V... values ) {
addDefaultValue( value );
for ( V each : values )
addDefaultValue( each );
return this;
}
private void addDefaultValue( V value ) {
ensureNotNull( value );
defaultValues.add( value );
}
@Override
final void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
String detectedArgument ) {
if ( isNullOrEmpty( detectedArgument ) )
detectOptionArgument( parser, arguments, detectedOptions );
else
addArguments( detectedOptions, detectedArgument );
}
protected void addArguments( OptionSet detectedOptions, String detectedArgument ) {
StringTokenizer lexer = new StringTokenizer( detectedArgument, valueSeparator );
if ( !lexer.hasMoreTokens() )
detectedOptions.addWithArgument( this, detectedArgument );
else {
while ( lexer.hasMoreTokens() )
detectedOptions.addWithArgument( this, lexer.nextToken() );
}
}
protected abstract void detectOptionArgument( OptionParser parser, ArgumentList arguments,
OptionSet detectedOptions );
@SuppressWarnings( "unchecked" )
@Override
protected final V convert( String argument ) {
if ( converter == null )
return (V) argument;
try {
return converter.convert( argument );
}
catch ( ReflectionException ex ) {
throw new OptionArgumentConversionException( options(), argument, converter.valueType(), ex );
}
catch ( ValueConversionException ex ) {
throw new OptionArgumentConversionException( options(), argument, converter.valueType(), ex );
}
}
protected boolean canConvertArgument( String argument ) {
StringTokenizer lexer = new StringTokenizer( argument, valueSeparator );
try {
while ( lexer.hasMoreTokens() )
convert( lexer.nextToken() );
return true;
}
catch ( OptionException ignored ) {
return false;
}
}
protected boolean isArgumentOfNumberType() {
return converter != null && Number.class.isAssignableFrom( converter.valueType() );
}
@Override
boolean acceptsArguments() {
return true;
}
@Override
boolean requiresArgument() {
return argumentRequired;
}
String argumentDescription() {
return argumentDescription;
}
String typeIndicator() {
if ( converter == null )
return null;
String pattern = converter.valuePattern();
return pattern == null ? converter.valueType().getName() : pattern;
}
@Override
List<V> defaultValues() {
return unmodifiableList( defaultValues );
}
@Override
public boolean equals( Object that ) {
if ( !super.equals( that ) )
return false;
ArgumentAcceptingOptionSpec<?> other = (ArgumentAcceptingOptionSpec<?>) that;
return requiresArgument() == other.requiresArgument();
}
@Override
public int hashCode() {
return super.hashCode() ^ ( argumentRequired ? 0 : 1 );
}
}

View File

@ -0,0 +1,60 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import static joptsimple.ParserRules.*;
/**
* <p>Wrapper for an array of command line arguments.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ArgumentList.java,v 1.7 2009/08/13 00:34:35 pholser Exp $
*/
class ArgumentList {
private final String[] arguments;
private int currentIndex;
ArgumentList( String... arguments ) {
this.arguments = arguments.clone();
}
boolean hasMore() {
return currentIndex < arguments.length;
}
String next() {
return arguments[ currentIndex++ ];
}
String peek() {
return arguments[ currentIndex ];
}
void treatNextAsLongOption() {
if ( HYPHEN_CHAR != arguments[ currentIndex ].charAt( 0 ) )
arguments[ currentIndex ] = DOUBLE_HYPHEN + arguments[ currentIndex ];
}
}

View File

@ -0,0 +1,149 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import joptsimple.internal.ColumnarData;
import static joptsimple.ParserRules.*;
import static joptsimple.internal.Classes.*;
import static joptsimple.internal.Strings.*;
/**
* <p>Produces text for a help screen given a set of options.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: HelpFormatter.java,v 1.19 2009/10/04 00:13:41 pholser Exp $
*/
class HelpFormatter implements OptionSpecVisitor {
private final ColumnarData grid;
HelpFormatter() {
grid = new ColumnarData( "Option", "Description" );
}
String format( Map<String, AbstractOptionSpec<?>> options ) {
if ( options.isEmpty() )
return "No options specified";
grid.clear();
Comparator<AbstractOptionSpec<?>> comparator =
new Comparator<AbstractOptionSpec<?>>() {
public int compare( AbstractOptionSpec<?> first, AbstractOptionSpec<?> second ) {
return first.options().iterator().next().compareTo( second.options().iterator().next() );
}
};
Set<AbstractOptionSpec<?>> sorted = new TreeSet<AbstractOptionSpec<?>>( comparator );
sorted.addAll( options.values() );
for ( AbstractOptionSpec<?> each : sorted )
each.accept( this );
return grid.format();
}
void addHelpLineFor( AbstractOptionSpec<?> spec, String additionalInfo ) {
grid.addRow( createOptionDisplay( spec ) + additionalInfo, createDescriptionDisplay( spec ) );
}
public void visit( NoArgumentOptionSpec spec ) {
addHelpLineFor( spec, "" );
}
public void visit( RequiredArgumentOptionSpec<?> spec ) {
visit( spec, '<', '>' );
}
public void visit( OptionalArgumentOptionSpec<?> spec ) {
visit( spec, '[', ']' );
}
public void visit( AlternativeLongOptionSpec spec ) {
addHelpLineFor( spec, ' ' + surround( spec.argumentDescription(), '<', '>' ) );
}
private void visit( ArgumentAcceptingOptionSpec<?> spec, char begin, char end ) {
String argDescription = spec.argumentDescription();
String typeIndicator = typeIndicator( spec );
StringBuilder collector = new StringBuilder();
if ( typeIndicator.length() > 0 ) {
collector.append( typeIndicator );
if ( argDescription.length() > 0 )
collector.append( ": " ).append( argDescription );
}
else if ( argDescription.length() > 0 )
collector.append( argDescription );
String helpLine = collector.length() == 0
? ""
: ' ' + surround( collector.toString(), begin, end );
addHelpLineFor( spec, helpLine );
}
private String createOptionDisplay( AbstractOptionSpec<?> spec ) {
StringBuilder buffer = new StringBuilder();
for ( Iterator<String> iter = spec.options().iterator(); iter.hasNext(); ) {
String option = iter.next();
buffer.append( option.length() > 1 ? DOUBLE_HYPHEN : HYPHEN );
buffer.append( option );
if ( iter.hasNext() )
buffer.append( ", " );
}
return buffer.toString();
}
private String createDescriptionDisplay( AbstractOptionSpec<?> spec ) {
List<?> defaultValues = spec.defaultValues();
if ( defaultValues.isEmpty() )
return spec.description();
String defaultValuesDisplay = createDefaultValuesDisplay( defaultValues );
return spec.description() + ' ' + surround( "default: " + defaultValuesDisplay, '(', ')' );
}
private String createDefaultValuesDisplay( List<?> defaultValues ) {
return defaultValues.size() == 1 ? defaultValues.get( 0 ).toString() : defaultValues.toString();
}
private static String typeIndicator( ArgumentAcceptingOptionSpec<?> spec ) {
String indicator = spec.typeIndicator();
return indicator == null || String.class.getName().equals( indicator )
? ""
: shortNameOf( indicator );
}
}

View File

@ -0,0 +1,48 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import static java.util.Collections.*;
/**
* <p>Thrown when the option parser discovers a cluster of short options in which
* at least one of the short options can accept arguments.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: IllegalOptionClusterException.java,v 1.11 2009/10/25 18:37:06 pholser Exp $
*/
class IllegalOptionClusterException extends OptionException {
private static final long serialVersionUID = -1L;
IllegalOptionClusterException( String option ) {
super( singletonList( option ) );
}
@Override
public String getMessage() {
return "Option cluster containing " + singleOptionMessage() + " is illegal";
}
}

View File

@ -0,0 +1,48 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import static java.util.Collections.*;
/**
* <p>Thrown when the option parser is asked to recognize an option with illegal
* characters in it.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: IllegalOptionSpecificationException.java,v 1.11 2009/10/25 18:37:06 pholser Exp $
*/
class IllegalOptionSpecificationException extends OptionException {
private static final long serialVersionUID = -1L;
IllegalOptionSpecificationException( String option ) {
super( singletonList( option ) );
}
@Override
public String getMessage() {
return singleOptionMessage() + " is not a legal option character";
}
}

View File

@ -0,0 +1,48 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
/**
* <p>Thrown when asking an {@link OptionSet} for a single argument of an option when
* many have been specified.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: MultipleArgumentsForOptionException.java,v 1.14 2009/10/25 18:37:06 pholser Exp $
*/
class MultipleArgumentsForOptionException extends OptionException {
private static final long serialVersionUID = -1L;
MultipleArgumentsForOptionException( Collection<String> options ) {
super( options );
}
@Override
public String getMessage() {
return "Found multiple arguments for option " + multipleOptionMessage() + ", but you asked for only one";
}
}

View File

@ -0,0 +1,79 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
import java.util.List;
import static java.util.Collections.*;
/**
* <p>A specification for an option that does not accept arguments.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: NoArgumentOptionSpec.java,v 1.16 2009/10/04 00:13:41 pholser Exp $
*/
class NoArgumentOptionSpec extends AbstractOptionSpec<Void> {
NoArgumentOptionSpec( String option ) {
this( singletonList( option ), "" );
}
NoArgumentOptionSpec( Collection<String> options, String description ) {
super( options, description );
}
@Override
void handleOption( OptionParser parser, ArgumentList arguments,
OptionSet detectedOptions, String detectedArgument ) {
detectedOptions.add( this );
}
@Override
boolean acceptsArguments() {
return false;
}
@Override
boolean requiresArgument() {
return false;
}
@Override
void accept( OptionSpecVisitor visitor ) {
visitor.visit( this );
}
@Override
protected Void convert( String argument ) {
return null;
}
@Override
List<Void> defaultValues() {
return emptyList();
}
}

View File

@ -0,0 +1,56 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
/**
* <p>Thrown when a problem occurs converting an argument of an option from {@link String}
* to another type.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionArgumentConversionException.java,v 1.16 2009/10/25 18:37:06 pholser Exp $
*/
class OptionArgumentConversionException extends OptionException {
private static final long serialVersionUID = -1L;
private final String argument;
private final Class<?> valueType;
OptionArgumentConversionException( Collection<String> options, String argument, Class<?> valueType,
Throwable cause ) {
super( options, cause );
this.argument = argument;
this.valueType = valueType;
}
@Override
public String getMessage() {
return "Cannot convert argument '" + argument + "' of option " + multipleOptionMessage() + " to " + valueType;
}
}

View File

@ -0,0 +1,95 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import static java.util.Collections.*;
import static joptsimple.internal.Strings.*;
/**
* <p>Thrown when a problem occurs during option parsing.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionException.java,v 1.21 2009/08/13 00:34:35 pholser Exp $
*/
public abstract class OptionException extends RuntimeException {
private static final long serialVersionUID = -1L;
private final List<String> options = new ArrayList<String>();
protected OptionException( Collection<String> options ) {
this.options.addAll( options );
}
protected OptionException( Collection<String> options, Throwable cause ) {
super( cause );
this.options.addAll( options );
}
/**
* <p>Gives the option being considered when the exception was created.</p>
*
* @return the option being considered when the exception was created
*/
public Collection<String> options() {
return unmodifiableCollection( options );
}
protected final String singleOptionMessage() {
return singleOptionMessage( options.get( 0 ) );
}
protected final String singleOptionMessage( String option ) {
return SINGLE_QUOTE + option + SINGLE_QUOTE;
}
protected final String multipleOptionMessage() {
StringBuilder buffer = new StringBuilder( "[" );
for ( Iterator<String> iter = options.iterator(); iter.hasNext(); ) {
buffer.append( singleOptionMessage( iter.next() ) );
if ( iter.hasNext() )
buffer.append( ", " );
}
buffer.append( ']' );
return buffer.toString();
}
static OptionException illegalOptionCluster( String option ) {
return new IllegalOptionClusterException( option );
}
static OptionException unrecognizedOption( String option ) {
return new UnrecognizedOptionException( option );
}
}

View File

@ -0,0 +1,48 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
/**
* <p>Thrown when the option parser discovers an option that requires an argument,
* but that argument is missing.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionMissingRequiredArgumentException.java,v 1.12 2009/10/25 18:37:06 pholser Exp $
*/
class OptionMissingRequiredArgumentException extends OptionException {
private static final long serialVersionUID = -1L;
OptionMissingRequiredArgumentException( Collection<String> options ) {
super( options );
}
@Override
public String getMessage() {
return "Option " + multipleOptionMessage() + " requires an argument";
}
}

View File

@ -0,0 +1,496 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Collections.*;
import joptsimple.internal.AbbreviationMap;
import joptsimple.util.KeyValuePair;
import static joptsimple.OptionException.*;
import static joptsimple.OptionParserState.*;
import static joptsimple.ParserRules.*;
/**
* <p>Parses command line arguments, using a syntax that attempts to take from the best
* of POSIX {@code getopt()} and GNU {@code getopt_long()}.</p>
*
* <p>This parser supports short options and long options.</p>
*
* <ul>
* <li><dfn>Short options</dfn> begin with a single hyphen ("<kbd>-</kbd>") followed
* by a single letter or digit, or question mark ("<kbd>?</kbd>"), or dot
* ("<kbd>.</kbd>").</li>
*
* <li>Short options can accept single arguments. The argument can be made required or
* optional. The option's argument can occur:
* <ul>
* <li>in the slot after the option, as in <kbd>-d /tmp</kbd></li>
* <li>right up against the option, as in <kbd>-d/tmp</kbd></li>
* <li>right up against the option separated by an equals sign (<kbd>"="</kbd>),
* as in <kbd>-d=/tmp</kbd></li>
* </ul>
* To specify <var>n</var> arguments for an option, specify the option <var>n</var>
* times, once for each argument, as in <kbd>-d /tmp -d /var -d /opt</kbd>; or, when
* using the {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char)
* "separated values"} clause of the "fluent interface" (see below), give multiple
* values separated by a given character as a single argument to the option.</li>
*
* <li>Short options can be clustered, so that <kbd>-abc</kbd> is treated as
* <kbd>-a -b -c</kbd>, if none of those options can accept arguments.</li>
*
* <li>An argument consisting only of two hyphens (<kbd>"--"</kbd>) signals that the
* remaining arguments are to be treated as non-options.</li>
*
* <li>An argument consisting only of a single hyphen is considered a non-option
* argument (though it can be an argument of an option). Many Unix programs treat
* single hyphens as stand-ins for the standard input or standard output streams.</li>
*
* <li><dfn>Long options</dfn> begin with two hyphens (<kbd>"--"</kbd>), followed
* by multiple letters, digits, hyphens, question marks, or dots. A hyphen cannot be
* the first character of a long option specification when configuring the parser.</li>
*
* <li>You can abbreviate long options, so long as the abbreviation is unique.</li>
*
* <li>Long options can accept single arguments. The argument can be made required or
* optional. The option's argument can occur:
* <ul>
* <li>in the slot after the option, as in <kbd>--directory /tmp</kbd></li>
* <li>right up against the option separated by an equals sign (<kbd>"="</kbd>),
* as in <kbd>--directory=/tmp</kbd>
* </ul>
* Specify multiple arguments for a long option in the same manner as for short options
* (see above).</li>
*
* <li>You can use a single hyphen (<kbd>"-"</kbd>) instead of a double hyphen
* (<kbd>"--"</kbd>) for a long option.</li>
*
* <li>The option <kbd>-W</kbd> is reserved. If you tell the parser to {@linkplain
* #recognizeAlternativeLongOptions(boolean) recognize alternative long options}, then
* it will treat, for example, <kbd>-W foo=bar</kbd> as the long option
* <kbd>foo</kbd> with argument <kbd>bar</kbd>, as though you had written
* <kbd>--foo=bar</kbd>.</li>
*
* <li>You can specify <kbd>-W</kbd> as a valid short option, or use it as an
* abbreviation for a long option, but {@linkplain
* #recognizeAlternativeLongOptions(boolean) recognizing alternative long options} will
* always supersede this behavior.</li>
*
* <li>You can specify a given short or long option multiple times on a single command
* line. The parser collects any arguments specified for those options as a list.</li>
*
* <li>If the parser detects an option whose argument is optional, and the next argument
* "looks like" an option, that argument is not treated as the argument to the option,
* but as a potentially valid option. If, on the other hand, the optional argument is
* typed as a derivative of {@link Number}, then that argument is treated as the
* negative number argument of the option, even if the parser recognizes the
* corresponding numeric option. For example:
* <pre><code>
* OptionParser parser = new OptionParser();
* parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
* parser.accepts( "2" );
* OptionSet options = parser.parse( "-a", "-2" );
* </code></pre>
* In this case, the option set contains <kbd>"a"</kbd> with argument <kbd>-2</kbd>,
* not both <kbd>"a"</kbd> and <kbd>"2"</kbd>. Swapping the elements in the
* <var>args</var> array gives the latter.</li>
* </ul>
*
* <p>There are two ways to tell the parser what options to recognize:</p>
*
* <ol>
* <li>A "fluent interface"-style API for specifying options, available since
* version 2. Sentences in this fluent interface language begin with a call to
* {@link #accepts(String) accepts} or {@link #acceptsAll(Collection) acceptsAll}
* methods; calls on the ensuing chain of objects describe whether the options can take
* an argument, whether the argument is required or optional, to what type arguments of
* the options should be converted if any, etc. Since version 3, these calls return
* an instance of {@link OptionSpec}, which can subsequently be used to retrieve the
* arguments of the associated option in a type-safe manner.</li>
*
* <li>Since version 1, a more concise way of specifying short options has been to use
* the special {@linkplain #OptionParser(String) constructor}. Arguments of options
* specified in this manner will be of type {@link String}. Here are the rules for the
* format of the specification strings this constructor accepts:
*
* <ul>
* <li>Any letter or digit is treated as an option character.</li>
*
* <li>If an option character is followed by a single colon (<kbd>":"</kbd>),
* then the option requires an argument.</li>
*
* <li>If an option character is followed by two colons (<kbd>"::"</kbd>), then
* the option accepts an optional argument.</li>
*
* <li>Otherwise, the option character accepts no argument.</li>
*
* <li>If the option specification string begins with a plus sign (<kbd>"+"</kbd>),
* the parser will behave "POSIX-ly correct".</li>
*
* <li>If the option specification string contains the sequence <kbd>"W;"</kbd>
* (capital W followed by a semicolon), the parser will recognize the alternative
* form of long options.</li>
* </ul>
* </li>
* </ol>
*
* <p>Each of the options in a list of options given to {@link #acceptsAll(Collection)
* acceptsAll} is treated as a synonym of the others. For example:
* <pre>
* <code>
* OptionParser parser = new OptionParser();
* parser.acceptsAll( asList( "w", "interactive", "confirmation" ) );
* OptionSet options = parser.parse( "-w" );
* </code>
* </pre>
* In this case, <code>options.{@link OptionSet#has(String) has}</code> would answer
* {@code true} when given arguments <kbd>"w"</kbd>, <kbd>"interactive"</kbd>, and
* <kbd>"confirmation"</kbd>. The {@link OptionSet} would give the same responses to
* these arguments for its other methods as well.</p>
*
* <p>By default, as with GNU {@code getopt()}, the parser allows intermixing of options
* and non-options. If, however, the parser has been created to be "POSIX-ly correct",
* then the first argument that does not look lexically like an option, and is not a
* required argument of a preceding option, signals the end of options. You can still
* bind optional arguments to their options using the abutting (for short options) or
* <kbd>=</kbd> syntax.</p>
*
* <p>Unlike GNU {@code getopt()}, this parser does not honor the environment variable
* {@code POSIXLY_CORRECT}. "POSIX-ly correct" parsers are configured by either:</p>
*
* <ol>
* <li>using the method {@link #posixlyCorrect(boolean)}, or</li>
*
* <li>using the {@linkplain #OptionParser(String) constructor} with an argument whose
* first character is a plus sign (<kbd>"+"</kbd>)</li>
* </ol>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionParser.java,v 1.38 2009/10/25 18:37:06 pholser Exp $
* @see <a href="http://www.gnu.org/software/libc/manual">The GNU C Library </a>
*/
public class OptionParser {
private final AbbreviationMap<AbstractOptionSpec<?>> recognizedOptions;
private OptionParserState state;
private boolean posixlyCorrect;
/**
* <p>Creates an option parser that initially recognizes no options, and does not
* exhibit "POSIX-ly correct" behavior.</p>
*/
public OptionParser() {
recognizedOptions = new AbbreviationMap<AbstractOptionSpec<?>>();
state = moreOptions( false );
}
/**
* <p>Creates an option parser and configures it to recognize the short options
* specified in the given string.</p>
*
* <p>Arguments of options specified this way will be of type {@link String}.</p>
*
* @param optionSpecification an option specification
* @throws NullPointerException if <var>optionSpecification</var> is
* {@code null}
* @throws OptionException if the option specification contains illegal characters
* or otherwise cannot be recognized
*/
public OptionParser( String optionSpecification ) {
this();
new OptionSpecTokenizer( optionSpecification ).configure( this );
}
/**
* <p>Tells the parser to recognize the given option.</p>
*
* <p>This method returns an instance of {@link OptionSpecBuilder} to allow the
* formation of parser directives as sentences in a fluent interface language.
* For example:</p>
*
* <pre><code>
* OptionParser parser = new OptionParser();
* parser.<strong>accepts( "c" )</strong>.withRequiredArg().ofType( Integer.class );
* </code></pre>
*
* <p>If no methods are invoked on the returned {@link OptionSpecBuilder}, then the
* parser treats the option as accepting no argument.</p>
*
* @param option the option to recognize
* @return an object that can be used to flesh out more detail about the option
* @throws OptionException if the option contains illegal characters
* @throws NullPointerException if the option is {@code null}
*/
public OptionSpecBuilder accepts( String option ) {
return acceptsAll( singletonList( option ) );
}
/**
* <p>Tells the parser to recognize the given option.</p>
*
* @see #accepts(String)
* @param option the option to recognize
* @param description a string that describes the purpose of the option. This is
* used when generating help information about the parser.
* @return an object that can be used to flesh out more detail about the option
* @throws OptionException if the option contains illegal characters
* @throws NullPointerException if the option is {@code null}
*/
public OptionSpecBuilder accepts( String option, String description ) {
return acceptsAll( singletonList( option ), description );
}
/**
* <p>Tells the parser to recognize the given options, and treat them as
* synonymous.</p>
*
* @see #accepts(String)
* @param options the options to recognize and treat as synonymous
* @return an object that can be used to flesh out more detail about the options
* @throws OptionException if any of the options contain illegal characters
* @throws NullPointerException if the option list or any of its elements are
* {@code null}
*/
public OptionSpecBuilder acceptsAll( Collection<String> options ) {
return acceptsAll( options, "" );
}
/**
* <p>Tells the parser to recognize the given options, and treat them as
* synonymous.</p>
*
* @see #acceptsAll(Collection)
* @param options the options to recognize and treat as synonymous
* @param description a string that describes the purpose of the option. This is
* used when generating help information about the parser.
* @return an object that can be used to flesh out more detail about the options
* @throws OptionException if any of the options contain illegal characters
* @throws NullPointerException if the option list or any of its elements are
* {@code null}
* @throws IllegalArgumentException if the option list is empty
*/
public OptionSpecBuilder acceptsAll( Collection<String> options,
String description ) {
if ( options.isEmpty() )
throw new IllegalArgumentException( "need at least one option" );
ensureLegalOptions( options );
return new OptionSpecBuilder( this, options, description );
}
/**
* <p>Tells the parser whether or not to behave "POSIX-ly correct"-ly.</p>
*
* @param setting {@code true} if the parser should behave "POSIX-ly correct"-ly
*/
public void posixlyCorrect( boolean setting ) {
posixlyCorrect = setting;
state = moreOptions( setting );
}
boolean posixlyCorrect() {
return posixlyCorrect;
}
/**
* <p>Tells the parser either to recognize or ignore <kbd>"-W"</kbd>-style long
* options.</p>
*
* @param recognize {@code true} if the parser is to recognize the special style
* of long options
*/
public void recognizeAlternativeLongOptions( boolean recognize ) {
if ( recognize )
recognize( new AlternativeLongOptionSpec() );
else
recognizedOptions.remove( String.valueOf( RESERVED_FOR_EXTENSIONS ) );
}
void recognize( AbstractOptionSpec<?> spec ) {
recognizedOptions.putAll( spec.options(), spec );
}
/**
* <p>Writes information about the options this parser recognizes to the given output
* sink.</p>
*
* <p>The output sink is flushed, but not closed.</p>
*
* @param sink the sink to write information to
* @throws IOException if there is a problem writing to the sink
* @throws NullPointerException if <var>sink</var> is {@code null}
* @see #printHelpOn(Writer)
*/
public void printHelpOn( OutputStream sink ) throws IOException {
printHelpOn( new OutputStreamWriter( sink ) );
}
/**
* <p>Writes information about the options this parser recognizes to the given output
* sink.</p>
*
* <p>The output sink is flushed, but not closed.</p>
*
* @param sink the sink to write information to
* @throws IOException if there is a problem writing to the sink
* @throws NullPointerException if <var>sink</var> is {@code null}
* @see #printHelpOn(OutputStream)
*/
public void printHelpOn( Writer sink ) throws IOException {
sink.write( new HelpFormatter().format( recognizedOptions.toJavaUtilMap() ) );
sink.flush();
}
/**
* <p>Parses the given command line arguments according to the option specifications
* given to the parser.</p>
*
* @param arguments arguments to parse
* @return an {@link OptionSet} describing the parsed options, their arguments, and
* any non-option arguments found
* @throws OptionException if problems are detected while parsing
* @throws NullPointerException if the argument list is {@code null}
*/
public OptionSet parse( String... arguments ) {
ArgumentList argumentList = new ArgumentList( arguments );
OptionSet detected = new OptionSet( defaultValues() );
while ( argumentList.hasMore() )
state.handleArgument( this, argumentList, detected );
reset();
return detected;
}
void handleLongOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {
KeyValuePair optionAndArgument = parseLongOptionWithArgument( candidate );
if ( !isRecognized( optionAndArgument.key ) )
throw unrecognizedOption( optionAndArgument.key );
AbstractOptionSpec<?> optionSpec = specFor( optionAndArgument.key );
optionSpec.handleOption( this, arguments, detected, optionAndArgument.value );
}
void handleShortOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {
KeyValuePair optionAndArgument = parseShortOptionWithArgument( candidate );
if ( isRecognized( optionAndArgument.key ) ) {
specFor( optionAndArgument.key ).handleOption( this, arguments, detected, optionAndArgument.value );
}
else
handleShortOptionCluster( candidate, arguments, detected );
}
private void handleShortOptionCluster( String candidate, ArgumentList arguments, OptionSet detected ) {
char[] options = extractShortOptionsFrom( candidate );
validateOptionCharacters( options );
AbstractOptionSpec<?> optionSpec = specFor( options[ 0 ] );
if ( optionSpec.acceptsArguments() && options.length > 1 ) {
String detectedArgument = String.valueOf( options, 1, options.length - 1 );
optionSpec.handleOption( this, arguments, detected, detectedArgument );
}
else {
for ( char each : options )
specFor( each ).handleOption( this, arguments, detected, null );
}
}
void noMoreOptions() {
state = OptionParserState.noMoreOptions();
}
boolean looksLikeAnOption( String argument ) {
return isShortOptionToken( argument ) || isLongOptionToken( argument );
}
private boolean isRecognized( String option ) {
return recognizedOptions.contains( option );
}
private AbstractOptionSpec<?> specFor( char option ) {
return specFor( String.valueOf( option ) );
}
private AbstractOptionSpec<?> specFor( String option ) {
return recognizedOptions.get( option );
}
private void reset() {
state = moreOptions( posixlyCorrect );
}
private static char[] extractShortOptionsFrom( String argument ) {
char[] options = new char[ argument.length() - 1 ];
argument.getChars( 1, argument.length(), options, 0 );
return options;
}
private void validateOptionCharacters( char[] options ) {
for ( int i = 0; i < options.length; ++i ) {
String option = String.valueOf( options[ i ] );
if ( !isRecognized( option ) )
throw unrecognizedOption( option );
if ( specFor( option ).acceptsArguments() ) {
if ( i > 0 )
throw illegalOptionCluster( option );
// remainder of chars are the argument to the option at char 0
return;
}
}
}
private static KeyValuePair parseLongOptionWithArgument( String argument ) {
return KeyValuePair.valueOf( argument.substring( 2 ) );
}
private static KeyValuePair parseShortOptionWithArgument( String argument ) {
return KeyValuePair.valueOf( argument.substring( 1 ) );
}
private Map<String, List<?>> defaultValues() {
Map<String, List<?>> defaults = new HashMap<String, List<?>>();
for ( Map.Entry<String, AbstractOptionSpec<?>> each : recognizedOptions.toJavaUtilMap().entrySet() )
defaults.put( each.getKey(), each.getValue().defaultValues() );
return defaults;
}
}

View File

@ -0,0 +1,69 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import static joptsimple.ParserRules.*;
/**
* <p>Abstraction of parser state; mostly serves to model how a parser behaves depending
* on whether end-of-options has been detected.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionParserState.java,v 1.8 2009/09/28 01:12:48 pholser Exp $
*/
abstract class OptionParserState {
static OptionParserState noMoreOptions() {
return new OptionParserState() {
@Override
protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
detectedOptions.addNonOptionArgument( arguments.next() );
}
};
}
static OptionParserState moreOptions( final boolean posixlyCorrect ) {
return new OptionParserState() {
@Override
protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
String candidate = arguments.next();
if ( isOptionTerminator( candidate ) )
parser.noMoreOptions();
else if ( isLongOptionToken( candidate ) )
parser.handleLongOptionToken( candidate, arguments, detectedOptions );
else if ( isShortOptionToken( candidate ) )
parser.handleShortOptionToken( candidate, arguments, detectedOptions );
else {
if ( posixlyCorrect )
parser.noMoreOptions();
detectedOptions.addNonOptionArgument( candidate );
}
}
};
}
protected abstract void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions );
}

View File

@ -0,0 +1,301 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import static java.util.Collections.*;
import static joptsimple.internal.Objects.*;
/**
* <p>Representation of a group of detected command line options, their arguments, and
* non-option arguments.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionSet.java,v 1.26 2009/10/25 18:37:05 pholser Exp $
*/
public class OptionSet {
private final Map<String, AbstractOptionSpec<?>> detectedOptions;
private final Map<AbstractOptionSpec<?>, List<String>> optionsToArguments;
private final List<String> nonOptionArguments;
private final Map<String, List<?>> defaultValues;
/**
* Package-private because clients don't create these.
*/
OptionSet( Map<String, List<?>> defaults ) {
detectedOptions = new HashMap<String, AbstractOptionSpec<?>>();
optionsToArguments = new IdentityHashMap<AbstractOptionSpec<?>, List<String>>();
nonOptionArguments = new ArrayList<String>();
defaultValues = new HashMap<String, List<?>>( defaults );
}
/**
* <p>Tells whether the given option was detected.</p>
*
* @param option the option to search for
* @return {@code true} if the option was detected
* @see #has(OptionSpec)
*/
public boolean has( String option ) {
return detectedOptions.containsKey( option );
}
/**
* <p>Tells whether the given option was detected.</p>
*
* <p>This method recognizes only instances of options returned from the fluent
* interface methods.</p>
*
* <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[])} default argument value}
* for an option does not cause this method to return {@code true} if the option was not detected on the command
* line.</p>
*
* @param option the option to search for
* @return {@code true} if the option was detected
* @see #has(String)
*/
public boolean has( OptionSpec<?> option ) {
return optionsToArguments.containsKey( option );
}
/**
* <p>Tells whether there are any arguments associated with the given option.</p>
*
* @param option the option to search for
* @return {@code true} if the option was detected and at least one argument was
* detected for the option
* @see #hasArgument(OptionSpec)
*/
public boolean hasArgument( String option ) {
AbstractOptionSpec<?> spec = detectedOptions.get( option );
return spec != null && hasArgument( spec );
}
/**
* <p>Tells whether there are any arguments associated with the given option.</p>
*
* <p>This method recognizes only instances of options returned from the fluent
* interface methods.</p>
*
* <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
* for an option does not cause this method to return {@code true} if the option was not detected on the command
* line, or if the option can take an optional argument but did not have one on the command line.</p>
*
* @param option the option to search for
* @return {@code true} if the option was detected and at least one argument was
* detected for the option
* @throws NullPointerException if {@code option} is {@code null}
* @see #hasArgument(String)
*/
public boolean hasArgument( OptionSpec<?> option ) {
ensureNotNull( option );
List<String> values = optionsToArguments.get( option );
return values != null && !values.isEmpty();
}
/**
* <p>Gives the argument associated with the given option. If the option was given
* an argument type, the argument will take on that type; otherwise, it will be a
* {@link String}.</p>
*
* <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
* for an option will cause this method to return that default value even if the option was not detected on the
* command line, or if the option can take an optional argument but did not have one on the command line.</p>
*
* @param option the option to search for
* @return the argument of the given option; {@code null} if no argument is
* present, or that option was not detected
* @throws NullPointerException if {@code option} is {@code null}
* @throws OptionException if more than one argument was detected for the option
*/
public Object valueOf( String option ) {
ensureNotNull( option );
AbstractOptionSpec<?> spec = detectedOptions.get( option );
if ( spec == null ) {
List<?> defaults = defaultValuesFor( option );
return defaults.isEmpty() ? null : defaults.get( 0 );
}
return valueOf( spec );
}
/**
* <p>Gives the argument associated with the given option.</p>
*
* <p>This method recognizes only instances of options returned from the fluent
* interface methods.</p>
*
* @param <V> represents the type of the arguments the given option accepts
* @param option the option to search for
* @return the argument of the given option; {@code null} if no argument is
* present, or that option was not detected
* @throws OptionException if more than one argument was detected for the option
* @throws NullPointerException if {@code option} is {@code null}
* @throws ClassCastException if the arguments of this option are not of the
* expected type
*/
public <V> V valueOf( OptionSpec<V> option ) {
ensureNotNull( option );
List<V> values = valuesOf( option );
switch ( values.size() ) {
case 0:
return null;
case 1:
return values.get( 0 );
default:
throw new MultipleArgumentsForOptionException( option.options() );
}
}
/**
* <p>Gives any arguments associated with the given option. If the option was given
* an argument type, the arguments will take on that type; otherwise, they will be
* {@link String}s.</p>
*
* @param option the option to search for
* @return the arguments associated with the option, as a list of objects of the
* type given to the arguments; an empty list if no such arguments are present, or if
* the option was not detected
* @throws NullPointerException if {@code option} is {@code null}
*/
public List<?> valuesOf( String option ) {
ensureNotNull( option );
AbstractOptionSpec<?> spec = detectedOptions.get( option );
return spec == null ? defaultValuesFor( option ) : valuesOf( spec );
}
/**
* <p>Gives any arguments associated with the given option. If the option was given
* an argument type, the arguments will take on that type; otherwise, they will be
* {@link String}s.</p>
*
* <p>This method recognizes only instances of options returned from the fluent
* interface methods.</p>
*
* @param <V> represents the type of the arguments the given option accepts
* @param option the option to search for
* @return the arguments associated with the option; an empty list if no such
* arguments are present, or if the option was not detected
* @throws NullPointerException if {@code option} is {@code null}
* @throws OptionException if there is a problem converting the option's arguments to
* the desired type; for example, if the type does not implement a correct conversion
* constructor or method
*/
public <V> List<V> valuesOf( OptionSpec<V> option ) {
ensureNotNull( option );
List<String> values = optionsToArguments.get( option );
if ( values == null || values.isEmpty() )
return defaultValueFor( option );
AbstractOptionSpec<V> spec = (AbstractOptionSpec<V>) option;
List<V> convertedValues = new ArrayList<V>();
for ( String each : values )
convertedValues.add( spec.convert( each ) );
return unmodifiableList( convertedValues );
}
/**
* @return the detected non-option arguments
*/
public List<String> nonOptionArguments() {
return unmodifiableList( nonOptionArguments );
}
void add( AbstractOptionSpec<?> option ) {
addWithArgument( option, null );
}
void addWithArgument( AbstractOptionSpec<?> option, String argument ) {
for ( String each : option.options() )
detectedOptions.put( each, option );
List<String> optionArguments = optionsToArguments.get( option );
if ( optionArguments == null ) {
optionArguments = new ArrayList<String>();
optionsToArguments.put( option, optionArguments );
}
if ( argument != null )
optionArguments.add( argument );
}
void addNonOptionArgument( String argument ) {
nonOptionArguments.add( argument );
}
@Override
public boolean equals( Object that ) {
if ( this == that )
return true;
if ( that == null || !getClass().equals( that.getClass() ) )
return false;
OptionSet other = (OptionSet) that;
Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments =
new HashMap<AbstractOptionSpec<?>, List<String>>( optionsToArguments );
Map<AbstractOptionSpec<?>, List<String>> otherOptionsToArguments =
new HashMap<AbstractOptionSpec<?>, List<String>>( other.optionsToArguments );
return detectedOptions.equals( other.detectedOptions )
&& thisOptionsToArguments.equals( otherOptionsToArguments )
&& nonOptionArguments.equals( other.nonOptionArguments() );
}
@Override
public int hashCode() {
Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments =
new HashMap<AbstractOptionSpec<?>, List<String>>( optionsToArguments );
return detectedOptions.hashCode()
^ thisOptionsToArguments.hashCode()
^ nonOptionArguments.hashCode();
}
private <V> List<V> defaultValuesFor( String option ) {
if ( defaultValues.containsKey( option ) ) {
@SuppressWarnings( "unchecked" )
List<V> defaults = (List<V>) defaultValues.get( option );
return defaults;
}
return emptyList();
}
private <V> List<V> defaultValueFor( OptionSpec<V> option ) {
return defaultValuesFor( option.options().iterator().next() );
}
}

View File

@ -0,0 +1,95 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
import java.util.List;
/**
* <p>Describes options that an option parser recognizes.</p>
*
* <p>Instances of this interface are returned by the "fluent interface" methods to allow
* retrieval of option arguments in a type-safe manner. Here's an example:</p>
* <pre><code>
* OptionParser parser = new OptionParser();
* <strong>OptionSpec&lt;Integer&gt;</strong> count =
* parser.accepts( "count" ).withRequiredArg().ofType( Integer.class );
* OptionSet options = parser.parse( "--count", "2" );
* assert options.has( count );
* int countValue = options.valueOf( count );
* assert countValue == count.value( options );
* List&lt;Integer&gt; countValues = options.valuesOf( count );
* assert countValues.equals( count.values( options ) );
* </code></pre>
*
* @param <V> represents the type of the arguments this option accepts
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionSpec.java,v 1.25 2009/10/25 18:37:06 pholser Exp $
*/
public interface OptionSpec<V> {
/**
* <p>Gives any arguments associated with the given option in the given set of
* detected options.</p>
*
* <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
* for this option will cause this method to return that default value even if this option was not detected on the
* command line, or if this option can take an optional argument but did not have one on the command line.</p>
*
* @param detectedOptions the detected options to search in
* @return the arguments associated with this option; an empty list if no such
* arguments are present, or if this option was not detected
* @throws OptionException if there is a problem converting this option's arguments
* to the desired type; for example, if the type does not implement a correct
* conversion constructor or method
* @throws NullPointerException if {@code detectedOptions} is {@code null}
* @see OptionSet#valuesOf(OptionSpec)
*/
List<V> values( OptionSet detectedOptions );
/**
* <p>Gives the argument associated with the given option in the given set of
* detected options.</p>
*
* <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
* for this option will cause this method to return that default value even if this option was not detected on the
* command line, or if this option can take an optional argument but did not have one on the command line.</p>
*
* @param detectedOptions the detected options to search in
* @return the argument of the this option; {@code null} if no argument is present,
* or that option was not detected
* @throws OptionException if more than one argument was detected for the option
* @throws NullPointerException if {@code detectedOptions} is {@code null}
* @throws ClassCastException if the arguments of this option are not of the
* expected type
* @see OptionSet#valueOf(OptionSpec)
*/
V value( OptionSet detectedOptions );
/**
* @return the string representations of this option
*/
Collection<String> options();
}

View File

@ -0,0 +1,101 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
/**
* <p>Allows callers to specify whether a given option accepts arguments (required or
* optional).</p>
*
* <p>Instances are returned from {@link OptionParser#accepts(String)} to allow the
* formation of parser directives as sentences in a "fluent interface" language. For
* example:</p>
*
* <pre><code>
* OptionParser parser = new OptionParser();
* parser.accepts( "c" ).<strong>withRequiredArg()</strong>.ofType( Integer.class );
* </code></pre>
*
* <p>If no methods are invoked on an instance of this class, then that instance's option
* will accept no argument.</p>
*
* <p>Note that you should not use the fluent interface clauses in a way that would
* defeat the typing of option arguments:</p>
*
* <pre><code>
* OptionParser parser = new OptionParser();
* ArgumentAcceptingOptionSpec&lt;String&gt; optionC =
* parser.accepts( "c" ).withRequiredArg();
* <strong>optionC.ofType( Integer.class ); // DON'T THROW AWAY THE TYPE!</strong>
*
* String value = parser.parse( "-c", "2" ).valueOf( optionC ); // ClassCastException
* </code></pre>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionSpecBuilder.java,v 1.19 2009/10/25 18:37:06 pholser Exp $
*/
public class OptionSpecBuilder extends NoArgumentOptionSpec {
private final OptionParser parser;
OptionSpecBuilder( OptionParser parser, Collection<String> options, String description ) {
super( options, description );
this.parser = parser;
attachToParser();
}
private void attachToParser() {
parser.recognize( this );
}
/**
* <p>Informs an option parser that this builder's option requires an argument.</p>
*
* @return a specification for the option
*/
public ArgumentAcceptingOptionSpec<String> withRequiredArg() {
ArgumentAcceptingOptionSpec<String> newSpec =
new RequiredArgumentOptionSpec<String>( options(), description() );
parser.recognize( newSpec );
return newSpec;
}
/**
* <p>Informs an option parser that this builder's option accepts an optional
* argument.</p>
*
* @return a specification for the option
*/
public ArgumentAcceptingOptionSpec<String> withOptionalArg() {
ArgumentAcceptingOptionSpec<String> newSpec =
new OptionalArgumentOptionSpec<String>( options(), description() );
parser.recognize( newSpec );
return newSpec;
}
}

View File

@ -0,0 +1,119 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.NoSuchElementException;
import static joptsimple.ParserRules.*;
/**
* <p>Tokenizes a short option specification string.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionSpecTokenizer.java,v 1.14 2009/10/25 18:37:06 pholser Exp $
*/
class OptionSpecTokenizer {
private static final char POSIXLY_CORRECT_MARKER = '+';
private String specification;
private int index;
OptionSpecTokenizer( String specification ) {
if ( specification == null )
throw new NullPointerException( "null option specification" );
this.specification = specification;
}
boolean hasMore() {
return index < specification.length();
}
AbstractOptionSpec<?> next() {
if ( !hasMore() )
throw new NoSuchElementException();
String optionCandidate = String.valueOf( specification.charAt( index ) );
index++;
AbstractOptionSpec<?> spec;
if ( RESERVED_FOR_EXTENSIONS.equals( optionCandidate ) ) {
spec = handleReservedForExtensionsToken();
if ( spec != null )
return spec;
}
ensureLegalOption( optionCandidate );
if ( hasMore() )
spec = specification.charAt( index ) == ':'
? handleArgumentAcceptingOption( optionCandidate )
: new NoArgumentOptionSpec( optionCandidate );
else
spec = new NoArgumentOptionSpec( optionCandidate );
return spec;
}
void configure( OptionParser parser ) {
adjustForPosixlyCorrect( parser );
while ( hasMore() )
parser.recognize( next() );
}
private void adjustForPosixlyCorrect( OptionParser parser ) {
if ( POSIXLY_CORRECT_MARKER == specification.charAt( 0 ) ) {
parser.posixlyCorrect( true );
specification = specification.substring( 1 );
}
}
private AbstractOptionSpec<?> handleReservedForExtensionsToken() {
if ( !hasMore() )
return new NoArgumentOptionSpec( RESERVED_FOR_EXTENSIONS );
if ( specification.charAt( index ) == ';' ) {
++index;
return new AlternativeLongOptionSpec();
}
return null;
}
private AbstractOptionSpec<?> handleArgumentAcceptingOption( String candidate ) {
index++;
if ( hasMore() && specification.charAt( index ) == ':' ) {
index++;
return new OptionalArgumentOptionSpec<String>( candidate );
}
return new RequiredArgumentOptionSpec<String>( candidate );
}
}

View File

@ -0,0 +1,42 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
/**
* <p>Visitor interface for option specifications.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionSpecVisitor.java,v 1.6 2009/08/13 00:34:35 pholser Exp $
*/
interface OptionSpecVisitor {
void visit( NoArgumentOptionSpec spec );
void visit( RequiredArgumentOptionSpec<?> spec );
void visit( OptionalArgumentOptionSpec<?> spec );
void visit( AlternativeLongOptionSpec spec );
}

View File

@ -0,0 +1,75 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
/**
* <p>Specification of an option that accepts an optional argument.</p>
*
* @param <V> represents the type of the arguments this option accepts
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: OptionalArgumentOptionSpec.java,v 1.17 2009/09/28 01:12:48 pholser Exp $
*/
class OptionalArgumentOptionSpec<V> extends ArgumentAcceptingOptionSpec<V> {
OptionalArgumentOptionSpec( String option ) {
super( option, false );
}
OptionalArgumentOptionSpec( Collection<String> options, String description ) {
super( options, false, description );
}
@Override
protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
if ( arguments.hasMore() ) {
String nextArgument = arguments.peek();
if ( !parser.looksLikeAnOption( nextArgument ) )
handleOptionArgument( parser, detectedOptions, arguments );
else if ( isArgumentOfNumberType() && canConvertArgument( nextArgument ) )
addArguments( detectedOptions, arguments.next() );
else
detectedOptions.add( this );
}
else
detectedOptions.add( this );
}
private void handleOptionArgument( OptionParser parser, OptionSet detectedOptions, ArgumentList arguments ) {
if ( parser.posixlyCorrect() ) {
detectedOptions.add( this );
parser.noMoreOptions();
}
else
addArguments( detectedOptions, arguments.next() );
}
@Override
void accept( OptionSpecVisitor visitor ) {
visitor.visit( this );
}
}

View File

@ -0,0 +1,88 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
import static java.lang.Character.*;
/**
* <p>Can tell whether or not options are well-formed.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ParserRules.java,v 1.14 2009/09/23 00:39:14 pholser Exp $
*/
final class ParserRules {
static final char HYPHEN_CHAR = '-';
static final String HYPHEN = String.valueOf( HYPHEN_CHAR );
static final String DOUBLE_HYPHEN = "--";
static final String OPTION_TERMINATOR = DOUBLE_HYPHEN;
static final String RESERVED_FOR_EXTENSIONS = "W";
static {
new ParserRules();
}
private ParserRules() {
// nothing to do here
}
static boolean isShortOptionToken( String argument ) {
return argument.startsWith( HYPHEN )
&& !HYPHEN.equals( argument )
&& !isLongOptionToken( argument );
}
static boolean isLongOptionToken( String argument ) {
return argument.startsWith( DOUBLE_HYPHEN ) && !isOptionTerminator( argument );
}
static boolean isOptionTerminator( String argument ) {
return OPTION_TERMINATOR.equals( argument );
}
static void ensureLegalOption( String option ) {
if ( option.startsWith( HYPHEN ) )
throw new IllegalOptionSpecificationException( String.valueOf( option ) );
for ( int i = 0; i < option.length(); ++i )
ensureLegalOptionCharacter( option.charAt( i ) );
}
static void ensureLegalOptions( Collection<String> options ) {
for ( String each : options )
ensureLegalOption( each );
}
private static void ensureLegalOptionCharacter( char option ) {
if ( !( isLetterOrDigit( option ) || isAllowedPunctuation( option ) ) )
throw new IllegalOptionSpecificationException( String.valueOf( option ) );
}
private static boolean isAllowedPunctuation( char option ) {
String allowedPunctuation = "?." + HYPHEN_CHAR;
return allowedPunctuation.indexOf( option ) != -1;
}
}

View File

@ -0,0 +1,58 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import java.util.Collection;
/**
* <p>Specification of an option that accepts a required argument.</p>
*
* @param <V> represents the type of the arguments this option accepts
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: RequiredArgumentOptionSpec.java,v 1.16 2009/09/28 01:12:48 pholser Exp $
*/
class RequiredArgumentOptionSpec<V> extends ArgumentAcceptingOptionSpec<V> {
RequiredArgumentOptionSpec( String option ) {
super( option, true );
}
RequiredArgumentOptionSpec( Collection<String> options, String description ) {
super( options, true, description );
}
@Override
protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
if ( !arguments.hasMore() )
throw new OptionMissingRequiredArgumentException( options() );
addArguments( detectedOptions, arguments.next() );
}
@Override
void accept( OptionSpecVisitor visitor ) {
visitor.visit( this );
}
}

View File

@ -0,0 +1,47 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
import static java.util.Collections.*;
/**
* <p>Thrown when the option parser encounters an unrecognized option.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: UnrecognizedOptionException.java,v 1.10 2009/10/25 18:37:06 pholser Exp $
*/
class UnrecognizedOptionException extends OptionException {
private static final long serialVersionUID = -1L;
UnrecognizedOptionException( String option ) {
super( singletonList( option ) );
}
@Override
public String getMessage() {
return singleOptionMessage() + " is not a recognized option";
}
}

View File

@ -0,0 +1,56 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
/**
* Thrown by {@link ValueConverter}s when problems occur in converting string values to
* other Java types.
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ValueConversionException.java,v 1.5 2009/08/13 00:34:35 pholser Exp $
*/
public class ValueConversionException extends RuntimeException {
private static final long serialVersionUID = -1L;
/**
* Creates a new exception with the specified detail message.
*
* @param message the detail message
*/
public ValueConversionException( String message ) {
this( message, null );
}
/**
* Creates a new exception with the specified detail message and cause.
*
* @param message the detail message
* @param cause the original exception
*/
public ValueConversionException( String message, Throwable cause ) {
super( message, cause );
}
}

View File

@ -0,0 +1,61 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple;
/**
* Instances of this interface are used to convert arguments of options into specific
* Java types.
*
* @param <V> constraint on the type of values being converted to
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ValueConverter.java,v 1.9 2009/08/13 00:34:35 pholser Exp $
*/
public interface ValueConverter<V> {
/**
* Converts the given string value into a Java type.
*
* @param value the string to convert
* @return the converted value
* @throws ValueConversionException if a problem occurs while converting the value
*/
V convert( String value );
/**
* Gives the class of the type of values this converter converts to.
*
* @return the target class for conversion
*/
Class<V> valueType();
/**
* Gives a string that describes the pattern of the values this converter expects,
* if any. For example, a date converter can respond with a
* {@link java.text.SimpleDateFormat date format string}.
*
* @return a value pattern, or {@code null} if there's nothing interesting here
*/
String valuePattern();
}

View File

@ -0,0 +1,239 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
import java.util.Map;
import java.util.TreeMap;
/**
* <p>A map whose keys are strings; when a key/value pair is added to the map,
* the longest unique abbreviations of that key are added as well, and associated with
* the value. Thus:</p>
*
* <pre>
* <code>
* abbreviations.put( "good", "bye" );
* </code>
* </pre>
*
* <p>would make it such that you could retrieve the value {@code "bye"} from the map
* using the keys {@code "good"}, {@code "goo"}, {@code "go"}, and {@code "g"}.
* A subsequent invocation of:</p>
* <pre>
* <code>
* abbreviations.put( "go", "fish" );
* </code>
* </pre>
*
* <p>would make it such that you could retrieve the value {@code "bye"} using the keys
* {@code "good"} and {@code "goo"}, and the value {@code "fish"} using the key
* {@code "go"}. The key {@code "g"} would yield {@code null}, since it would no longer
* be a unique abbreviation.</p>
*
* <p>The data structure is much like a "trie".</p>
*
* @param <V> a constraint on the types of the values in the map
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: AbbreviationMap.java,v 1.14 2009/10/25 18:37:08 pholser Exp $
* @see <a href="http://www.perldoc.com/perl5.8.0/lib/Text/Abbrev.html"> Perl's
* Text::Abbrev module</a>
*/
public class AbbreviationMap<V> {
private String key;
private V value;
private final Map<Character, AbbreviationMap<V>> children = new TreeMap<Character, AbbreviationMap<V>>();
private int keysBeyond;
/**
* <p>Tells whether the given key is in the map, or whether the given key is a unique
* abbreviation of a key that is in the map.</p>
*
* @param aKey key to look up
* @return {@code true} if {@code key} is present in the map
* @throws NullPointerException if {@code key} is {@code null}
*/
public boolean contains( String aKey ) {
return get( aKey ) != null;
}
/**
* <p>Answers the value associated with the given key. The key can be a unique
* abbreviation of a key that is in the map. </p>
*
* @param aKey key to look up
* @return the value associated with {@code aKey}; or {@code null} if there is no
* such value or {@code aKey} is not a unique abbreviation of a key in the map
* @throws NullPointerException if {@code aKey} is {@code null}
*/
public V get( String aKey ) {
char[] chars = charsOf( aKey );
AbbreviationMap<V> child = this;
for ( char each : chars ) {
child = child.children.get( each );
if ( child == null )
return null;
}
return child.value;
}
/**
* <p>Associates a given value with a given key. If there was a previous
* association, the old value is replaced with the new one.</p>
*
* @param aKey key to create in the map
* @param newValue value to associate with the key
* @throws NullPointerException if {@code aKey} or {@code newValue} is {@code null}
* @throws IllegalArgumentException if {@code aKey} is a zero-length string
*/
public void put( String aKey, V newValue ) {
if ( newValue == null )
throw new NullPointerException();
if ( aKey.length() == 0 )
throw new IllegalArgumentException();
char[] chars = charsOf( aKey );
add( chars, newValue, 0, chars.length );
}
/**
* <p>Associates a given value with a given set of keys. If there was a previous
* association, the old value is replaced with the new one.</p>
*
* @param keys keys to create in the map
* @param newValue value to associate with the key
* @throws NullPointerException if {@code keys} or {@code newValue} is {@code null}
* @throws IllegalArgumentException if any of {@code keys} is a zero-length string
*/
public void putAll( Iterable<String> keys, V newValue ) {
for ( String each : keys )
put( each, newValue );
}
private boolean add( char[] chars, V newValue, int offset, int length ) {
if ( offset == length ) {
value = newValue;
boolean wasAlreadyAKey = key != null;
key = new String( chars );
return !wasAlreadyAKey;
}
char nextChar = chars[ offset ];
AbbreviationMap<V> child = children.get( nextChar );
if ( child == null ) {
child = new AbbreviationMap<V>();
children.put( nextChar, child );
}
boolean newKeyAdded = child.add( chars, newValue, offset + 1, length );
if ( newKeyAdded )
++keysBeyond;
if ( key == null )
value = keysBeyond > 1 ? null : newValue;
return newKeyAdded;
}
/**
* <p>If the map contains the given key, dissociates the key from its value.</p>
*
* @param aKey key to remove
* @throws NullPointerException if {@code aKey} is {@code null}
* @throws IllegalArgumentException if {@code aKey} is a zero-length string
*/
public void remove( String aKey ) {
if ( aKey.length() == 0 )
throw new IllegalArgumentException();
char[] keyChars = charsOf( aKey );
remove( keyChars, 0, keyChars.length );
}
private boolean remove( char[] aKey, int offset, int length ) {
if ( offset == length )
return removeAtEndOfKey();
char nextChar = aKey[ offset ];
AbbreviationMap<V> child = children.get( nextChar );
if ( child == null || !child.remove( aKey, offset + 1, length ) )
return false;
--keysBeyond;
if ( child.keysBeyond == 0 )
children.remove( nextChar );
if ( keysBeyond == 1 && key == null )
setValueToThatOfOnlyChild();
return true;
}
private void setValueToThatOfOnlyChild() {
Map.Entry<Character, AbbreviationMap<V>> entry = children.entrySet().iterator().next();
AbbreviationMap<V> onlyChild = entry.getValue();
value = onlyChild.value;
}
private boolean removeAtEndOfKey() {
if ( key == null )
return false;
key = null;
if ( keysBeyond == 1 )
setValueToThatOfOnlyChild();
else
value = null;
return true;
}
/**
* Gives a Java map representation of this abbreviation map.
*
* @return a Java map corresponding to this abbreviation map
*/
public Map<String, V> toJavaUtilMap() {
Map<String, V> mappings = new TreeMap<String, V>();
addToMappings( mappings );
return mappings;
}
private void addToMappings( Map<String, V> mappings ) {
if ( key != null )
mappings.put( key, value );
for ( AbbreviationMap<V> each : children.values() )
each.addToMappings( mappings );
}
private static char[] charsOf( String aKey ) {
char[] chars = new char[ aKey.length() ];
aKey.getChars( 0, aKey.length(), chars, 0 );
return chars;
}
}

View File

@ -0,0 +1,51 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
/**
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: Classes.java,v 1.10 2009/08/13 01:05:35 pholser Exp $
*/
public final class Classes {
static {
new Classes();
}
private Classes() {
// nothing to do here
}
/**
* Gives the "short version" of the given class name. Somewhat naive to inner
* classes.
*
* @param className class name to chew on
* @return the short name of the class
*/
public static String shortNameOf( String className ) {
return className.substring( className.lastIndexOf( '.' ) + 1 );
}
}

View File

@ -0,0 +1,132 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
import java.text.BreakIterator;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import static java.lang.System.*;
import static java.text.BreakIterator.*;
import static joptsimple.internal.Strings.*;
/**
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: Column.java,v 1.16 2009/10/25 18:37:08 pholser Exp $
*/
public class Column {
static final Comparator<Column> BY_HEIGHT = new Comparator<Column>() {
public int compare( Column first, Column second ) {
if ( first.height() < second.height() )
return -1;
return first.height() == second.height() ? 0 : 1;
}
};
private final String header;
private final List<String> data;
private final int width;
private int height;
Column( String header, int width ) {
this.header = header;
this.width = Math.max( width, header.length() );
data = new LinkedList<String>();
height = 0;
}
int addCells( Object cellCandidate ) {
int originalHeight = height;
String source = String.valueOf( cellCandidate ).trim();
for ( String eachPiece : source.split( getProperty( "line.separator" ) ) )
processNextEmbeddedLine( eachPiece );
return height - originalHeight;
}
private void processNextEmbeddedLine( String line ) {
BreakIterator words = BreakIterator.getLineInstance( Locale.US );
words.setText( line );
StringBuilder nextCell = new StringBuilder();
int start = words.first();
for ( int end = words.next(); end != DONE; start = end, end = words.next() )
nextCell = processNextWord( line, nextCell, start, end );
if ( nextCell.length() > 0 )
addCell( nextCell.toString() );
}
private StringBuilder processNextWord( String source, StringBuilder nextCell, int start, int end ) {
StringBuilder augmented = nextCell;
String word = source.substring( start, end );
if ( augmented.length() + word.length() > width ) {
addCell( augmented.toString() );
augmented = new StringBuilder( " " ).append( word );
}
else
augmented.append( word );
return augmented;
}
void addCell( String newCell ) {
data.add( newCell );
++height;
}
void writeHeaderOn( StringBuilder buffer, boolean appendSpace ) {
buffer.append( header ).append( repeat( ' ', width - header.length() ) );
if ( appendSpace )
buffer.append( ' ' );
}
void writeSeparatorOn( StringBuilder buffer, boolean appendSpace ) {
buffer.append( repeat( '-', header.length() ) ).append( repeat( ' ', width - header.length() ) );
if ( appendSpace )
buffer.append( ' ' );
}
void writeCellOn( int index, StringBuilder buffer, boolean appendSpace ) {
if ( index < data.size() ) {
String item = data.get( index );
buffer.append( item ).append( repeat( ' ', width - item.length() ) );
if ( appendSpace )
buffer.append( ' ' );
}
}
int height() {
return height;
}
}

View File

@ -0,0 +1,42 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
/**
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ColumnWidthCalculator.java,v 1.4 2009/08/13 00:34:36 pholser Exp $
*/
class ColumnWidthCalculator {
int calculate( int totalWidth, int numberOfColumns ) {
if ( numberOfColumns == 1 )
return totalWidth;
int remainder = totalWidth % numberOfColumns;
if ( remainder == numberOfColumns - 1 )
return totalWidth / numberOfColumns;
return totalWidth / numberOfColumns - 1;
}
}

View File

@ -0,0 +1,162 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import static java.lang.Integer.*;
import static java.lang.System.*;
import static java.util.Collections.*;
import static joptsimple.internal.Column.*;
import static joptsimple.internal.Strings.*;
/**
* <p>A means to display data in a text grid.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ColumnarData.java,v 1.17 2009/10/25 18:37:08 pholser Exp $
*/
public class ColumnarData {
private static final String LINE_SEPARATOR = getProperty( "line.separator" );
private static final int TOTAL_WIDTH = 80;
private final ColumnWidthCalculator widthCalculator;
private final List<Column> columns;
private final String[] headers;
/**
* Creates a new grid with the given column headers.
*
* @param headers column headers
*/
public ColumnarData( String... headers ) {
this.headers = headers.clone();
widthCalculator = new ColumnWidthCalculator();
columns = new LinkedList<Column>();
clear();
}
/**
* Adds a row to the grid. The data will fall under the corresponding headers.
* There can be fewer elements in the row than headers. Any data in columns outside
* of the number of headers will not be added to the grid.
*
* @param rowData row data to add
*/
public void addRow( Object... rowData ) {
int[] numberOfCellsAddedAt = addRowCells( rowData );
addPaddingCells( numberOfCellsAddedAt );
}
/**
* Gives a string that represents the data formatted in columns.
*
* @return the formatted grid
*/
public String format() {
StringBuilder buffer = new StringBuilder();
writeHeadersOn( buffer );
writeSeparatorsOn( buffer );
writeRowsOn( buffer );
return buffer.toString();
}
/**
* Removes all data from the grid, but preserves the headers.
*/
public final void clear() {
columns.clear();
int desiredColumnWidth = widthCalculator.calculate( TOTAL_WIDTH, headers.length );
for ( String each : headers )
columns.add( new Column( each, desiredColumnWidth ) );
}
private void writeHeadersOn( StringBuilder buffer ) {
for ( Iterator<Column> iter = columns.iterator(); iter.hasNext(); )
iter.next().writeHeaderOn( buffer, iter.hasNext() );
buffer.append( LINE_SEPARATOR );
}
private void writeSeparatorsOn( StringBuilder buffer ) {
for ( Iterator<Column> iter = columns.iterator(); iter.hasNext(); )
iter.next().writeSeparatorOn( buffer, iter.hasNext() );
buffer.append( LINE_SEPARATOR );
}
private void writeRowsOn( StringBuilder buffer ) {
int maxHeight = max( columns, BY_HEIGHT ).height();
for ( int i = 0; i < maxHeight; ++i )
writeRowOn( buffer, i );
}
private void writeRowOn( StringBuilder buffer, int rowIndex ) {
for ( Iterator<Column> iter = columns.iterator(); iter.hasNext(); )
iter.next().writeCellOn( rowIndex, buffer, iter.hasNext() );
buffer.append( LINE_SEPARATOR );
}
private int arrayMax( int[] numbers ) {
int maximum = MIN_VALUE;
for ( int each : numbers )
maximum = Math.max( maximum, each );
return maximum;
}
private int[] addRowCells( Object... rowData ) {
int[] cellsAddedAt = new int[ rowData.length ];
Iterator<Column> iter = columns.iterator();
for ( int i = 0; iter.hasNext() && i < rowData.length; ++i )
cellsAddedAt[ i ] = iter.next().addCells( rowData[ i ] );
return cellsAddedAt;
}
private void addPaddingCells( int... numberOfCellsAddedAt ) {
int maxHeight = arrayMax( numberOfCellsAddedAt );
Iterator<Column> iter = columns.iterator();
for ( int i = 0; iter.hasNext() && i < numberOfCellsAddedAt.length; ++i )
addPaddingCellsForColumn( iter.next(), maxHeight, numberOfCellsAddedAt[ i ] );
}
private void addPaddingCellsForColumn( Column column, int maxHeight, int numberOfCellsAdded ) {
for ( int i = 0; i < maxHeight - numberOfCellsAdded; ++i )
column.addCell( EMPTY );
}
}

View File

@ -0,0 +1,56 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
import java.lang.reflect.Constructor;
import joptsimple.ValueConverter;
import static joptsimple.internal.Reflection.*;
/**
* @param <V> constraint on the type of values being converted to
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ConstructorInvokingValueConverter.java,v 1.4 2009/10/25 18:37:08 pholser Exp $
*/
class ConstructorInvokingValueConverter<V> implements ValueConverter<V> {
private final Constructor<V> ctor;
ConstructorInvokingValueConverter( Constructor<V> ctor ) {
this.ctor = ctor;
}
public V convert( String value ) {
return instantiate( ctor, value );
}
public Class<V> valueType() {
return ctor.getDeclaringClass();
}
public String valuePattern() {
return null;
}
}

View File

@ -0,0 +1,58 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
import java.lang.reflect.Method;
import joptsimple.ValueConverter;
import static joptsimple.internal.Reflection.*;
/**
* @param <V> constraint on the type of values being converted to
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: MethodInvokingValueConverter.java,v 1.4 2009/10/25 18:37:08 pholser Exp $
*/
class MethodInvokingValueConverter<V> implements ValueConverter<V> {
private final Method method;
private final Class<V> clazz;
MethodInvokingValueConverter( Method method, Class<V> clazz ) {
this.method = method;
this.clazz = clazz;
}
public V convert( String value ) {
return clazz.cast( invoke( method, value ) );
}
public Class<V> valueType() {
return clazz;
}
public String valuePattern() {
return null;
}
}

View File

@ -0,0 +1,51 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
/**
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: Objects.java,v 1.2 2009/10/25 18:37:08 pholser Exp $
*/
public final class Objects {
static {
new Objects();
}
private Objects() {
// nothing to do here
}
/**
* Rejects {@code null} references.
*
* @param target reference to check
* @throws NullPointerException if {@code target} is {@code null}
*/
public static void ensureNotNull( Object target ) {
if ( target == null )
throw new NullPointerException();
}
}

View File

@ -0,0 +1,142 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static java.lang.reflect.Modifier.*;
import joptsimple.ValueConverter;
/**
* <p>Helper methods for reflection.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: Reflection.java,v 1.20 2009/09/28 01:12:48 pholser Exp $
*/
public final class Reflection {
static {
new Reflection();
}
private Reflection() {
// nothing to do here
}
/**
* Finds an appropriate value converter for the given class.
*
* @param <V> a constraint on the class object to introspect
* @param clazz class to introspect on
* @return a converter method or constructor
*/
public static <V> ValueConverter<V> findConverter( Class<V> clazz ) {
ValueConverter<V> valueOf = valueOfConverter( clazz );
if ( valueOf != null )
return valueOf;
ValueConverter<V> constructor = constructorConverter( clazz );
if ( constructor != null )
return constructor;
throw new IllegalArgumentException( clazz + " is not a value type" );
}
private static <V> ValueConverter<V> valueOfConverter( Class<V> clazz ) {
try {
Method valueOf = clazz.getDeclaredMethod( "valueOf", String.class );
if ( !meetsConverterRequirements( valueOf, clazz ) )
return null;
return new MethodInvokingValueConverter<V>( valueOf, clazz );
}
catch ( NoSuchMethodException ignored ) {
return null;
}
}
private static <V> ValueConverter<V> constructorConverter( Class<V> clazz ) {
try {
return new ConstructorInvokingValueConverter<V>(
clazz.getConstructor( String.class ) );
}
catch ( NoSuchMethodException ignored ) {
return null;
}
}
/**
* Invokes the given constructor with the given arguments.
*
* @param <T> constraint on the type of the objects yielded by the constructor
* @param constructor constructor to invoke
* @param args arguments to hand to the constructor
* @return the result of invoking the constructor
* @throws ReflectionException in lieu of the gaggle of reflection-related exceptions
*/
public static <T> T instantiate( Constructor<T> constructor, Object... args ) {
try {
return constructor.newInstance( args );
}
catch ( Exception ex ) {
throw reflectionException( ex );
}
}
/**
* Invokes the given static method with the given arguments.
*
* @param method method to invoke
* @param args arguments to hand to the method
* @return the result of invoking the method
* @throws ReflectionException in lieu of the gaggle of reflection-related exceptions
*/
public static Object invoke( Method method, Object... args ) {
try {
return method.invoke( null, args );
}
catch ( Exception ex ) {
throw reflectionException( ex );
}
}
private static boolean meetsConverterRequirements( Method method, Class<?> expectedReturnType ) {
int modifiers = method.getModifiers();
return isPublic( modifiers ) && isStatic( modifiers ) && expectedReturnType.equals( method.getReturnType() );
}
private static RuntimeException reflectionException( Exception ex ) {
if ( ex instanceof IllegalArgumentException )
return new ReflectionException( ex );
if ( ex instanceof InvocationTargetException )
return new ReflectionException( ex.getCause() );
if ( ex instanceof RuntimeException )
return (RuntimeException) ex;
return new ReflectionException( ex );
}
}

View File

@ -0,0 +1,40 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
/**
* <p>This unchecked exception wraps reflection-oriented exceptions.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: ReflectionException.java,v 1.5 2009/08/13 00:34:36 pholser Exp $
*/
public class ReflectionException extends RuntimeException {
private static final long serialVersionUID = -2L;
ReflectionException( Throwable cause ) {
super( cause.toString() );
}
}

View File

@ -0,0 +1,124 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.internal;
import java.util.Iterator;
import java.util.List;
import static java.lang.System.*;
import static java.util.Arrays.*;
/**
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: Strings.java,v 1.16 2009/08/13 01:05:35 pholser Exp $
*/
public final class Strings {
public static final String EMPTY = "";
public static final String SINGLE_QUOTE = "'";
public static final String LINE_SEPARATOR = getProperty( "line.separator" );
static {
new Strings();
}
private Strings() {
// nothing to do here
}
/**
* <p>Gives a string consisting of the given character repeated the given number of
* times.</p>
*
* @param ch the character to repeat
* @param count how many times to repeat the character
* @return the resultant string
*/
public static String repeat( char ch, int count ) {
StringBuilder buffer = new StringBuilder();
for ( int i = 0; i < count; ++i )
buffer.append( ch );
return buffer.toString();
}
/**
* <p>Tells whether the given string is either {@code} or consists solely of
* whitespace characters.</p>
*
* @param target string to check
* @return {@code true} if the target string is null or empty
*/
public static boolean isNullOrEmpty( String target ) {
return target == null || EMPTY.equals( target );
}
/**
* <p>Gives a string consisting of a given string prepended and appended with
* surrounding characters.</p>
*
* @param target a string
* @param begin character to prepend
* @param end character to append
* @return the surrounded string
*/
public static String surround( String target, char begin, char end ) {
return begin + target + end;
}
/**
* Gives a string consisting of the elements of a given array of strings, each
* separated by a given separator string.
*
* @param pieces the strings to join
* @param separator the separator
* @return the joined string
*/
public static String join( String[] pieces, String separator ) {
return join( asList( pieces ), separator );
}
/**
* Gives a string consisting of the string representations of the elements of a
* given array of objects, each separated by a given separator string.
*
* @param pieces the elements whose string representations are to be joined
* @param separator the separator
* @return the joined string
*/
public static String join( List<String> pieces, String separator ) {
StringBuilder buffer = new StringBuilder();
for ( Iterator<String> iter = pieces.iterator(); iter.hasNext(); ) {
buffer.append( iter.next() );
if ( iter.hasNext() )
buffer.append( separator );
}
return buffer.toString();
}
}

View File

@ -0,0 +1,101 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.util;
import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import joptsimple.ValueConversionException;
import joptsimple.ValueConverter;
/**
* Converts values to {@link Date}s using a {@link DateFormat} object.
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: DateConverter.java,v 1.6 2009/10/25 18:37:09 pholser Exp $
*/
public class DateConverter implements ValueConverter<Date> {
private final DateFormat formatter;
/**
* Creates a converter that uses the given date formatter/parser.
*
* @param formatter the formatter/parser to use
* @throws NullPointerException if {@code formatter} is {@code null}
*/
public DateConverter( DateFormat formatter ) {
if ( formatter == null )
throw new NullPointerException( "illegal null formatter" );
this.formatter = formatter;
}
/**
* Creates a converter that uses a {@link SimpleDateFormat} with the given date/time
* pattern. The date formatter created is not
* {@link SimpleDateFormat#setLenient(boolean) lenient}.
*
* @param pattern expected date/time pattern
* @return the new converter
* @throws NullPointerException if {@code pattern} is {@code null}
* @throws IllegalArgumentException if {@code pattern} is invalid
*/
public static DateConverter datePattern( String pattern ) {
SimpleDateFormat formatter = new SimpleDateFormat( pattern );
formatter.setLenient( false );
return new DateConverter( formatter );
}
public Date convert( String value ) {
ParsePosition position = new ParsePosition( 0 );
Date date = formatter.parse( value, position );
if ( position.getIndex() != value.length() )
throw new ValueConversionException( message( value ) );
return date;
}
public Class<Date> valueType() {
return Date.class;
}
public String valuePattern() {
return formatter instanceof SimpleDateFormat
? ( (SimpleDateFormat) formatter ).toLocalizedPattern()
: "";
}
private String message( String value ) {
String message = "Value [" + value + "] does not match date/time pattern";
if ( formatter instanceof SimpleDateFormat )
message += " [" + ( (SimpleDateFormat) formatter ).toLocalizedPattern() + ']';
return message;
}
}

View File

@ -0,0 +1,84 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.util;
import static joptsimple.internal.Strings.*;
/**
* <p>A simple string key/string value pair.</p>
*
* <p>This is useful as an argument type for options whose values take on the form
* <kbd>key=value</kbd>, such as JVM command line system properties.</p>
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: KeyValuePair.java,v 1.10 2009/10/25 18:37:09 pholser Exp $
*/
public final class KeyValuePair {
public final String key;
public final String value;
private KeyValuePair( String key, String value ) {
this.key = key;
this.value = value;
}
/**
* Parses a string assumed to be of the form <kbd>key=value</kbd> into its parts.
*
* @param asString key-value string
* @return a key-value pair
* @throws NullPointerException if {@code stringRepresentation} is {@code null}
*/
public static KeyValuePair valueOf( String asString ) {
int equalsIndex = asString.indexOf( '=' );
if ( equalsIndex == -1 )
return new KeyValuePair( asString, EMPTY );
String aKey = asString.substring( 0, equalsIndex );
String aValue = equalsIndex == asString.length() - 1 ? EMPTY : asString.substring( equalsIndex + 1 );
return new KeyValuePair( aKey, aValue );
}
@Override
public boolean equals( Object that ) {
if ( !( that instanceof KeyValuePair ) )
return false;
KeyValuePair other = (KeyValuePair) that;
return key.equals( other.key ) && value.equals( other.value );
}
@Override
public int hashCode() {
return key.hashCode() ^ value.hashCode();
}
@Override
public String toString() {
return key + '=' + value;
}
}

View File

@ -0,0 +1,86 @@
/*
The MIT License
Copyright (c) 2009 Paul R. Holser, Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package joptsimple.util;
import java.util.regex.Pattern;
import static java.util.regex.Pattern.*;
import joptsimple.ValueConversionException;
import joptsimple.ValueConverter;
/**
* Ensures that values entirely match a regular expression.
*
* @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
* @version $Id: RegexMatcher.java,v 1.6 2009/10/25 18:37:09 pholser Exp $
*/
public class RegexMatcher implements ValueConverter<String> {
private final Pattern pattern;
/**
* Creates a matcher that uses the given regular expression, modified by the given
* flags.
*
* @param pattern the regular expression pattern
* @param flags modifying regex flags
* @throws IllegalArgumentException if bit values other than those corresponding to
* the defined match flags are set in {@code flags}
* @throws java.util.regex.PatternSyntaxException if the expression's syntax is
* invalid
*/
public RegexMatcher( String pattern, int flags ) {
this.pattern = compile( pattern, flags );
}
/**
* Gives a matcher that uses the given regular expression.
*
* @param pattern the regular expression pattern
* @return the new converter
* @throws java.util.regex.PatternSyntaxException if the expression's syntax is
* invalid
*/
public static ValueConverter<String> regex( String pattern ) {
return new RegexMatcher( pattern, 0 );
}
public String convert( String value ) {
if ( !pattern.matcher( value ).matches() ) {
throw new ValueConversionException(
"Value [" + value + "] did not match regex [" + pattern.pattern() + ']' );
}
return value;
}
public Class<String> valueType() {
return String.class;
}
public String valuePattern() {
return pattern.pattern();
}
}