Skip to content

Commit

Permalink
Allow several sets of arguments to be printed in usage
Browse files Browse the repository at this point in the history
  • Loading branch information
spacecowboy committed Nov 1, 2016
1 parent 7a016e4 commit 3ba8aeb
Show file tree
Hide file tree
Showing 23 changed files with 155 additions and 43 deletions.
Expand Up @@ -20,6 +20,8 @@
package org.neo4j.commandline.admin; package org.neo4j.commandline.admin;


import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Set; import java.util.Set;


import org.neo4j.commandline.arguments.Arguments; import org.neo4j.commandline.arguments.Arguments;
Expand Down Expand Up @@ -62,7 +64,16 @@ public String name()
/** /**
* @return The arguments this command accepts. * @return The arguments this command accepts.
*/ */
public abstract Arguments arguments(); public abstract Arguments allArguments();

/**
*
* @return A list of possibly mutually-exclusive argument sets for this command.
*/
public List<Arguments> possibleArguments()
{
return Arrays.asList( allArguments() );
}


/** /**
* @return A single-line summary for the command. Should be 70 characters or less. * @return A single-line summary for the command. Should be 70 characters or less.
Expand Down
Expand Up @@ -40,7 +40,7 @@ public Provider( Usage usage )
} }


@Override @Override
public Arguments arguments() public Arguments allArguments()
{ {
return new Arguments().withOptionalPositionalArgument( 0, "command" ); return new Arguments().withOptionalPositionalArgument( 0, "command" );
} }
Expand Down
Expand Up @@ -82,13 +82,16 @@ public void printIndentedSummary( Consumer<String> output )


public void printDetailed( Consumer<String> output ) public void printDetailed( Consumer<String> output )
{ {
Arguments arguments = command.arguments(); for (Arguments arguments: command.possibleArguments())
{
//Arguments arguments = command.arguments();


String left = format( "usage: %s %s", scriptName, command.name() ); String left = format( "usage: %s %s", scriptName, command.name() );


output.accept( Arguments.rightColumnFormatted( left, arguments.usage(), left.length() + 1 ) ); output.accept( Arguments.rightColumnFormatted( left, arguments.usage(), left.length() + 1 ) );
}
output.accept( "" ); output.accept( "" );
output.accept( arguments.description( command.description() ) ); output.accept( command.allArguments().description( command.description() ) );
} }
} }
} }
Expand Up @@ -120,8 +120,9 @@ public String description( String text )
wrappedText = String.join( "\n\n", wrappedText, "options:" ); wrappedText = String.join( "\n\n", wrappedText, "options:" );


//noinspection OptionalGetWithoutIsPresent handled by if-statement above //noinspection OptionalGetWithoutIsPresent handled by if-statement above
final int alignLength = namedArgs.values().stream().map( NamedArgument::alignmentLength ).reduce( 0, final int alignLength = namedArgs.values().stream()
Integer::max ); .map( a -> a.optionsListing().length() )
.reduce( 0, Integer::max );


return String.join( "\n", wrappedText, return String.join( "\n", wrappedText,
namedArgs.values().stream() namedArgs.values().stream()
Expand All @@ -131,7 +132,7 @@ public String description( String text )


public String formatArgumentDescription( final int longestAlignmentLength, final NamedArgument argument ) public String formatArgumentDescription( final int longestAlignmentLength, final NamedArgument argument )
{ {
final String left = String.format( " --%s=<%s>", argument.name(), argument.exampleValue() ); final String left = String.format( " %s", argument.optionsListing() );
final String right; final String right;
if ( argument instanceof OptionalNamedArg ) if ( argument instanceof OptionalNamedArg )
{ {
Expand Down
Expand Up @@ -39,10 +39,9 @@ public MandatoryNamedArg( String name, String exampleValue, String description )
} }


@Override @Override
public int alignmentLength() public String optionsListing()
{ {
// length of "--NAME=<VALUE>" return usage();
return 5 + name.length() + exampleValue.length();
} }


@Override @Override
Expand Down
Expand Up @@ -21,15 +21,33 @@


public interface NamedArgument public interface NamedArgument
{ {
int alignmentLength(); /**
* Represents the option in the options list.
*/
String optionsListing();


/**
* Represents the option in the usage string.
*/
String usage(); String usage();


/**
* An explanation of the option in the options list.
*/
String description(); String description();


/**
* Name of the option as in '--name=<value>'
*/
String name(); String name();


/**
* Example value listed in usage between brackets like '--name=<example-value>'
*/
String exampleValue(); String exampleValue();


/**
* Parses the option (or possible default value) out of program arguments.
*/
String parse( String... args ); String parse( String... args );
} }
Expand Up @@ -19,8 +19,6 @@
*/ */
package org.neo4j.commandline.arguments; package org.neo4j.commandline.arguments;


import org.apache.commons.lang3.text.WordUtils;

import org.neo4j.helpers.Args; import org.neo4j.helpers.Args;


import static org.neo4j.kernel.impl.util.Converters.identity; import static org.neo4j.kernel.impl.util.Converters.identity;
Expand Down Expand Up @@ -53,16 +51,15 @@ public OptionalNamedArg( String name, String[] allowedValues, String defaultValu
} }


@Override @Override
public int alignmentLength() public String optionsListing()
{ {
// length of "--NAME=<VALUE>" return String.format( "--%s=<%s>", name, exampleValue );
return 5 + name.length() + exampleValue.length();
} }


@Override @Override
public String usage() public String usage()
{ {
return WordUtils.wrap( String.format( "[--%s=<%s>]", name, exampleValue ), 60 ); return String.format( "[--%s=<%s>]", name, exampleValue );
} }


@Override @Override
Expand Down
Expand Up @@ -21,7 +21,13 @@


public interface PositionalArgument public interface PositionalArgument
{ {
/**
* Index of the argument in arguments listing.
*/
int position(); int position();


/**
* Represents the option in the usage string.
*/
String usage(); String usage();
} }
Expand Up @@ -245,7 +245,7 @@ private CannedLocator cannedCommand( final String name, AdminCommand command )
return new CannedLocator( new AdminCommand.Provider( name ) return new CannedLocator( new AdminCommand.Provider( name )
{ {
@Override @Override
public Arguments arguments() public Arguments allArguments()
{ {
return Arguments.NO_ARGS; return Arguments.NO_ARGS;
} }
Expand Down Expand Up @@ -293,7 +293,7 @@ protected NullCommandProvider()
} }


@Override @Override
public Arguments arguments() public Arguments allArguments()
{ {
return Arguments.NO_ARGS; return Arguments.NO_ARGS;
} }
Expand Down
Expand Up @@ -107,7 +107,9 @@ public void showsArgumentsAndDescriptionForSpecifiedCommand() throws Exception
AdminCommand.Provider commandProvider = mock( AdminCommand.Provider.class ); AdminCommand.Provider commandProvider = mock( AdminCommand.Provider.class );
when( commandProvider.name() ).thenReturn( "foobar" ); when( commandProvider.name() ).thenReturn( "foobar" );
//when( commandProvider.arguments() ).thenReturn( Optional.of( "--baz --qux" ) ); //when( commandProvider.arguments() ).thenReturn( Optional.of( "--baz --qux" ) );
when( commandProvider.arguments() ).thenReturn( new Arguments().withDatabase() ); Arguments arguments = new Arguments().withDatabase();
when( commandProvider.allArguments() ).thenReturn( arguments );
when( commandProvider.possibleArguments() ).thenReturn( Collections.singletonList( arguments ) );
when( commandProvider.description() ).thenReturn( "This is a description of the foobar command." ); when( commandProvider.description() ).thenReturn( "This is a description of the foobar command." );
when( commandLocator.findProvider( "foobar" ) ).thenReturn( commandProvider ); when( commandLocator.findProvider( "foobar" ) ).thenReturn( commandProvider );


Expand Down
Expand Up @@ -79,7 +79,7 @@ public StubProvider( String name, String summary )
} }


@Override @Override
public Arguments arguments() public Arguments allArguments()
{ {
return Arguments.NO_ARGS; return Arguments.NO_ARGS;
} }
Expand Down
@@ -1,3 +1,22 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j 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 org.neo4j.commandline.arguments; package org.neo4j.commandline.arguments;


import org.junit.Test; import org.junit.Test;
Expand Down
Expand Up @@ -71,7 +71,7 @@ public Provider()
} }


@Override @Override
public Arguments arguments() public Arguments allArguments()
{ {
return arguments; return arguments;
} }
Expand Down
Expand Up @@ -64,7 +64,7 @@ public Provider()
} }


@Override @Override
public Arguments arguments() public Arguments allArguments()
{ {
return arguments; return arguments;
} }
Expand Down
Expand Up @@ -32,6 +32,7 @@
import org.neo4j.commandline.admin.IncorrectUsage; import org.neo4j.commandline.admin.IncorrectUsage;
import org.neo4j.commandline.admin.OutsideWorld; import org.neo4j.commandline.admin.OutsideWorld;
import org.neo4j.commandline.arguments.Arguments; import org.neo4j.commandline.arguments.Arguments;
import org.neo4j.commandline.arguments.MandatoryNamedArg;
import org.neo4j.commandline.arguments.OptionalNamedArg; import org.neo4j.commandline.arguments.OptionalNamedArg;
import org.neo4j.dbms.DatabaseManagementSystemSettings; import org.neo4j.dbms.DatabaseManagementSystemSettings;
import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.factory.GraphDatabaseSettings;
Expand All @@ -45,15 +46,61 @@ public class ImportCommand implements AdminCommand
{ {
public static final String DEFAULT_REPORT_FILE_NAME = "import.report"; public static final String DEFAULT_REPORT_FILE_NAME = "import.report";
private static final String[] allowedModes = {"database", "csv"}; private static final String[] allowedModes = {"database", "csv"};
private static final Arguments arguments = new Arguments() private static final Arguments databaseArguments = new Arguments()
.withArgument( new MandatoryNamedArg( "mode", "database", "Import a pre-3.0 installation." ) {
@Override
public String usage()
{
return String.format( "--%s=%s", name(), exampleValue() );
}
} )
.withDatabase()
.withAdditionalConfig()
.withArgument( new OptionalNamedArg( "from", "source-directory", "",
"The location of the pre-3.0 database (e.g. <neo4j-root>/data/graph.db)." ) );
private static final Arguments csvArguments = new Arguments()
.withArgument( new OptionalNamedArg( "mode", "csv", "csv", "Import a collection of CSV files." ) {
@Override
public String usage()
{
return String.format( "[--%s=%s]", name(), exampleValue() );
}
} )
.withDatabase()
.withAdditionalConfig()
.withArgument( new OptionalNamedArg( "report-file", "filename", DEFAULT_REPORT_FILE_NAME,
"File in which to store the report of the csv-import." ) )
.withArgument( new OptionalNamedArg( "nodes[:Label1:Label2]", "\"file1,file2,...\"", "",
"Node CSV header and data. Multiple files will be logically seen as " +
"one big file from the perspective of the importer. The first line " +
"must contain the header. Multiple data sources like these can be " +
"specified in one import, where each data source has its own header. " +
"Note that file groups must be enclosed in quotation marks." ) )
.withArgument( new OptionalNamedArg( "relationships[:RELATIONSHIP_TYPE]", "\"file1,file2,...\"", "",
"Relationship CSV header and data. Multiple files will be logically " +
"seen as one big file from the perspective of the importer. The first " +
"line must contain the header. Multiple data sources like these can be " +
"specified in one import, where each data source has its own header. " +
"Note that file groups must be enclosed in quotation marks." ) )
.withArgument( new OptionalNamedArg( "id-type", new String[]{"STRING", "INTEGER", "ACTUAL"},
"STRING", "Each node must provide a unique id. This is used to find the correct " +
"nodes when creating relationships. Possible values are " +
"STRING: arbitrary strings for identifying nodes, " +
"INTEGER: arbitrary integer values for identifying nodes, " +
"ACTUAL: (advanced) actual node ids. " +
"For more information on id handling, please see the Neo4j Manual: " +
"http://neo4j.com/docs/operations-manual/current/deployment/#import-tool" ) )
.withArgument( new OptionalNamedArg( "input-encoding", "character-set", "UTF-8",
"Character set that input data is encoded in." ) );
private static final Arguments allArguments = new Arguments()
.withDatabase() .withDatabase()
.withAdditionalConfig() .withAdditionalConfig()
.withArgument( new OptionalNamedArg( "mode", allowedModes, "csv", .withArgument( new OptionalNamedArg( "mode", allowedModes, "csv",
"Import a collection of CSV files or a pre-3.0 installation." ) ) "Import a collection of CSV files or a pre-3.0 installation." ) )
.withArgument( new OptionalNamedArg( "from", "source-directory", "", .withArgument( new OptionalNamedArg( "from", "source-directory", "",
"The location of the pre-3.0 database (e.g. <neo4j-root>/data/graph.db)." ) ) "The location of the pre-3.0 database (e.g. <neo4j-root>/data/graph.db)." ) )
.withArgument( new OptionalNamedArg( "report-file", "filename", ImportCommand.DEFAULT_REPORT_FILE_NAME, .withArgument( new OptionalNamedArg( "report-file", "filename", DEFAULT_REPORT_FILE_NAME,
"File in which to store the report of the import." ) ) "File in which to store the report of the csv-import." ) )
.withArgument( new OptionalNamedArg( "nodes[:Label1:Label2]", "\"file1,file2,...\"", "", .withArgument( new OptionalNamedArg( "nodes[:Label1:Label2]", "\"file1,file2,...\"", "",
"Node CSV header and data. Multiple files will be logically seen as " + "Node CSV header and data. Multiple files will be logically seen as " +
"one big file from the perspective of the importer. The first line " + "one big file from the perspective of the importer. The first line " +
Expand Down Expand Up @@ -85,9 +132,15 @@ public Provider()
} }


@Override @Override
public Arguments arguments() public Arguments allArguments()
{
return allArguments;
}

@Override
public List<Arguments> possibleArguments()
{ {
return arguments; return Arrays.asList( csvArguments, databaseArguments );
} }


@Override @Override
Expand Down Expand Up @@ -137,9 +190,9 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed


try try
{ {
mode = arguments.parse("mode", args); mode = allArguments.parse("mode", args);
database = arguments.parse( "database", args ); database = allArguments.parse( "database", args );
additionalConfigFile = arguments.parseOptionalPath( "additional-config", args ); additionalConfigFile = allArguments.parseOptionalPath( "additional-config", args );
} }
catch ( IllegalArgumentException e ) catch ( IllegalArgumentException e )
{ {
Expand Down
Expand Up @@ -66,7 +66,7 @@ public Provider()
} }


@Override @Override
public Arguments arguments() public Arguments allArguments()
{ {
return arguments; return arguments;
} }
Expand Down
Expand Up @@ -141,14 +141,16 @@ public void shouldPrintNiceHelp() throws Throwable
Consumer<String> out = mock( Consumer.class ); Consumer<String> out = mock( Consumer.class );
usage.printUsageForCommand( new ImportCommand.Provider(), out ); usage.printUsageForCommand( new ImportCommand.Provider(), out );


verify( out ).accept( "usage: neo4j-admin import [--database=<name>]\n" + verify( out ).accept( "usage: neo4j-admin import [--mode=csv] [--database=<name>]\n" +
" [--additional-config=<config-file-path>]\n" + " [--additional-config=<config-file-path>]\n" +
" [--mode=<database|csv>] [--from=<source-directory>]\n" +
" [--report-file=<filename>]\n" + " [--report-file=<filename>]\n" +
" [--nodes[:Label1:Label2]=<\"file1,file2,...\">]\n" + " [--nodes[:Label1:Label2]=<\"file1,file2,...\">]\n" +
" [--relationships[:RELATIONSHIP_TYPE]=<\"file1,file2,...\">]\n" + " [--relationships[:RELATIONSHIP_TYPE]=<\"file1,file2,...\">]\n" +
" [--id-type=<STRING|INTEGER|ACTUAL>]\n" + " [--id-type=<STRING|INTEGER|ACTUAL>]\n" +
" [--input-encoding=<character-set>]" ); " [--input-encoding=<character-set>]" );
verify( out ).accept( "usage: neo4j-admin import --mode=database [--database=<name>]\n" +
" [--additional-config=<config-file-path>]\n" +
" [--from=<source-directory>]" );
verify( out ).accept( "" ); verify( out ).accept( "" );
verify( out ).accept( "Import a collection of CSV files with --mode=csv (default), or a database from a\n" + verify( out ).accept( "Import a collection of CSV files with --mode=csv (default), or a database from a\n" +
"pre-3.0 installation with --mode=database.\n" + "pre-3.0 installation with --mode=database.\n" +
Expand All @@ -165,7 +167,8 @@ public void shouldPrintNiceHelp() throws Throwable
" The location of the pre-3.0 database (e.g. <neo4j-root>/data/graph.db).\n" + " The location of the pre-3.0 database (e.g. <neo4j-root>/data/graph.db).\n" +
" [default:]\n" + " [default:]\n" +
" --report-file=<filename>\n" + " --report-file=<filename>\n" +
" File in which to store the report of the import. [default:import.report]\n" + " File in which to store the report of the csv-import.\n" +
" [default:import.report]\n" +
" --nodes[:Label1:Label2]=<\"file1,file2,...\">\n" + " --nodes[:Label1:Label2]=<\"file1,file2,...\">\n" +
" Node CSV header and data. Multiple files will be logically seen as one\n" + " Node CSV header and data. Multiple files will be logically seen as one\n" +
" big file from the perspective of the importer. The first line must\n" + " big file from the perspective of the importer. The first line must\n" +
Expand Down
Expand Up @@ -296,7 +296,7 @@ public void shouldPrintNiceHelp() throws Throwable
usage.printUsageForCommand( new LoadCommand.Provider(), out ); usage.printUsageForCommand( new LoadCommand.Provider(), out );


verify( out ).accept( "usage: neo4j-admin load --from=<archive-path> [--database=<name>]\n" + verify( out ).accept( "usage: neo4j-admin load --from=<archive-path> [--database=<name>]\n" +
" [--force=<true|false>]" ); " [--force[=<true|false>]]" );
verify( out ).accept( "" ); verify( out ).accept( "" );
verify( out ).accept( "Load a database from an archive. <archive-path> must be an archive created with\n" + verify( out ).accept( "Load a database from an archive. <archive-path> must be an archive created with\n" +
"the dump command. <database> is the name of the database to create. Existing\n" + "the dump command. <database> is the name of the database to create. Existing\n" +
Expand Down

0 comments on commit 3ba8aeb

Please sign in to comment.