diff --git a/community/shell/src/main/java/org/neo4j/shell/TextUtil.java b/community/common/src/main/java/org/neo4j/helpers/TextUtil.java
similarity index 79%
rename from community/shell/src/main/java/org/neo4j/shell/TextUtil.java
rename to community/common/src/main/java/org/neo4j/helpers/TextUtil.java
index 9b8c9957b728..73fd84ceff89 100644
--- a/community/shell/src/main/java/org/neo4j/shell/TextUtil.java
+++ b/community/common/src/main/java/org/neo4j/helpers/TextUtil.java
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.shell;
+package org.neo4j.helpers;
import java.util.ArrayList;
import java.util.Collection;
@@ -44,7 +44,7 @@ public static String templateString( String templateString,
{
// Sort data strings on length.
Map> lengthMap =
- new HashMap>();
+ new HashMap<>();
int longest = 0;
for ( String key : data.keySet() )
{
@@ -62,7 +62,7 @@ public static String templateString( String templateString,
}
else
{
- innerList = new ArrayList();
+ innerList = new ArrayList<>();
lengthMap.put( innerKey, innerList );
}
innerList.add( key );
@@ -116,17 +116,17 @@ public static String lastWordOrQuoteOf( String text, boolean preserveQuotation )
return lastWord;
}
- public static String[] splitAndKeepEscapedSpaces( String string, boolean preserveEscapes )
+ private static String[] splitAndKeepEscapedSpaces( String string, boolean preserveEscapes )
{
- Collection result = new ArrayList();
+ Collection result = new ArrayList<>();
StringBuilder current = new StringBuilder();
for ( int i = 0; i < string.length(); i++ )
{
char ch = string.charAt( i );
if ( ch == ' ' )
{
- boolean isGluedSpace = i > 0 && string.charAt( i - 1 ) == '\\';
- if ( !isGluedSpace )
+ boolean isEscapedSpace = i > 0 && string.charAt( i - 1 ) == '\\';
+ if ( !isEscapedSpace )
{
result.add( current.toString() );
current = new StringBuilder();
@@ -146,29 +146,6 @@ public static String[] splitAndKeepEscapedSpaces( String string, boolean preserv
return result.toArray( new String[result.size()] );
}
- public static String multiplyString( String string, int times )
- {
- StringBuilder result = new StringBuilder();
- for ( int i = 0; i < times; i++ )
- {
- result.append( string );
- }
- return result.toString();
- }
-
- public static String removeSpaces( String command )
- {
- while ( command.length() > 0 && command.charAt( 0 ) == ' ' )
- {
- command = command.substring( 1 );
- }
- while ( command.length() > 0 && command.charAt( command.length() - 1 ) == ' ' )
- {
- command = command.substring( 0, command.length() - 1 );
- }
- return command;
- }
-
/**
* Tokenizes a string, regarding quotes.
*
@@ -177,7 +154,7 @@ public static String removeSpaces( String command )
*/
public static String[] tokenizeStringWithQuotes( String string )
{
- return tokenizeStringWithQuotes( string, true );
+ return tokenizeStringWithQuotes( string, true, false );
}
/**
@@ -189,15 +166,16 @@ public static String[] tokenizeStringWithQuotes( String string )
*
* @param string the string to tokenize.
* @param trim whether or not to trim each token.
+ * @param preserveEscapeCharacters whether or not to preserve escape characters '\', otherwise skip them.
* @return the tokens from the line.
*/
- public static String[] tokenizeStringWithQuotes( String string, boolean trim )
+ public static String[] tokenizeStringWithQuotes( String string, boolean trim, boolean preserveEscapeCharacters )
{
if ( trim )
{
string = string.trim();
}
- ArrayList result = new ArrayList();
+ ArrayList result = new ArrayList<>();
string = string.trim();
boolean inside = string.startsWith( "\"" );
StringTokenizer quoteTokenizer = new StringTokenizer( string, "\"" );
@@ -219,22 +197,10 @@ else if ( inside )
}
else
{
- Collections.addAll( result, TextUtil.splitAndKeepEscapedSpaces( token, false ) );
+ Collections.addAll( result, TextUtil.splitAndKeepEscapedSpaces( token, preserveEscapeCharacters ) );
}
inside = !inside;
}
return result.toArray( new String[result.size()] );
}
-
- public static String stripFromQuotes( String string )
- {
- if ( string != null )
- {
- if ( string.startsWith( "\"" ) && string.endsWith( "\"" ) )
- {
- return string.substring( 1, string.length() - 1 );
- }
- }
- return string;
- }
}
diff --git a/community/common/src/test/java/org/neo4j/helpers/TextUtilTest.java b/community/common/src/test/java/org/neo4j/helpers/TextUtilTest.java
new file mode 100644
index 000000000000..65ecd86fcf08
--- /dev/null
+++ b/community/common/src/test/java/org/neo4j/helpers/TextUtilTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2002-2017 "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 .
+ */
+package org.neo4j.helpers;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class TextUtilTest
+{
+ @Test
+ public void shouldReplaceVariablesWithValuesInTemplateString() throws Exception
+ {
+ // given
+ String template = "This is a $FIRST that $SECOND $THIRD!";
+ Map values = new HashMap<>();
+ values.put( "FIRST", "String" );
+ values.put( "SECOND", "should" );
+ values.put( "THIRD", "act as a template!" );
+
+ // when
+ String string = TextUtil.templateString( template, values );
+
+ // then
+ assertEquals( "This is a String that should act as a template!!", string );
+ }
+
+ @Test
+ public void shouldTokenizeStringWithWithoutQuotes() throws Exception
+ {
+ // given
+ String untokenized = "First Second Third";
+
+ // when
+ String[] tokenized = TextUtil.tokenizeStringWithQuotes( untokenized );
+
+ // then
+ assertArrayEquals( new String[] {"First", "Second", "Third"}, tokenized );
+ }
+
+ @Test
+ public void shouldTokenizeStringWithQuotes() throws Exception
+ {
+ // given
+ String untokenized = "First \"Second one\" Third \"And a fourth\"";
+
+ // when
+ String[] tokenized = TextUtil.tokenizeStringWithQuotes( untokenized );
+
+ // then
+ assertArrayEquals( new String[] {"First", "Second one", "Third", "And a fourth"}, tokenized );
+ }
+
+ @Test
+ public void shouldTokenStringWithWithQuotesAndEscapedSpaces() throws Exception
+ {
+ // given
+ String untokenized = "First \"Second one\" Third And\\ a\\ fourth";
+
+ // when
+ String[] tokenized = TextUtil.tokenizeStringWithQuotes( untokenized );
+
+ // then
+ assertArrayEquals( new String[] {"First", "Second one", "Third", "And a fourth"}, tokenized );
+ }
+
+ @Test
+ public void shouldPreserveBackslashes() throws Exception
+ {
+ // given
+ String untokenized = "First C:\\a\\b\\c";
+
+ // when
+ String[] tokenized = TextUtil.tokenizeStringWithQuotes( untokenized, true, true );
+
+ // then
+ assertArrayEquals( new String[] {"First", "C:\\a\\b\\c"}, tokenized );
+ }
+}
diff --git a/community/dbms/src/main/java/org/neo4j/commandline/dbms/ImportCommand.java b/community/dbms/src/main/java/org/neo4j/commandline/dbms/ImportCommand.java
index 2eb17447511a..7be595cf408c 100644
--- a/community/dbms/src/main/java/org/neo4j/commandline/dbms/ImportCommand.java
+++ b/community/dbms/src/main/java/org/neo4j/commandline/dbms/ImportCommand.java
@@ -20,6 +20,7 @@
package org.neo4j.commandline.dbms;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
@@ -41,6 +42,7 @@
import org.neo4j.kernel.impl.util.Validators;
import static org.neo4j.csv.reader.Configuration.DEFAULT;
+import static org.neo4j.tooling.ImportTool.parseFileArgumentList;
import static org.neo4j.unsafe.impl.batchimport.Configuration.DEFAULT_MAX_MEMORY_PERCENT;
import static org.neo4j.unsafe.impl.batchimport.input.csv.Configuration.COMMAS;
@@ -58,9 +60,8 @@ public String usage()
}
} )
.withDatabase()
- .withAdditionalConfig()
- .withArgument( new OptionalNamedArg( "from", "source-directory", "",
- "The location of the pre-3.0 database (e.g. /data/graph.db)." ) );
+ .withAdditionalConfig();
+
private static final Arguments csvArguments = new Arguments()
.withArgument( new OptionalNamedArg( "mode", "csv", "csv", "Import a collection of CSV files." )
{
@@ -71,70 +72,24 @@ public String usage()
}
} )
.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\n" +
- " STRING: arbitrary strings for identifying nodes,\n" +
- " INTEGER: arbitrary integer values for identifying nodes,\n" +
- " ACTUAL: (advanced) actual node ids.\n" +
- "For more information on id handling, please see the Neo4j Manual: " +
- "https://neo4j.com/docs/operations-manual/current/tools/import/" ) )
- .withArgument( new OptionalNamedArg( "input-encoding", "character-set", "UTF-8",
- "Character set that input data is encoded in." ) )
- .withArgument( new OptionalBooleanArg( "ignore-extra-columns", false,
- "If un-specified columns should be ignored during the import." ) )
- .withArgument( new OptionalBooleanArg( "ignore-duplicate-nodes", false,
- "If duplicate nodes should be ignored during the import." ) )
- .withArgument( new OptionalBooleanArg( "ignore-missing-nodes", false,
- "If relationships referring to missing nodes should be ignored during the import." ) )
- .withArgument( new OptionalBooleanArg( "multiline-fields",
- DEFAULT.multilineFields(),
- "Whether or not fields from input source can span multiple lines," +
- " i.e. contain newline characters." ) )
- .withArgument( new OptionalNamedArg( "delimiter",
- "delimiter-character",
- String.valueOf( COMMAS.delimiter() ),
- "Delimiter character between values in CSV data." ) )
- .withArgument( new OptionalNamedArg( "array-delimiter",
- "array-delimiter-character",
- String.valueOf( COMMAS.arrayDelimiter() ),
- "Delimiter character between array elements within a value in CSV data." ) )
- .withArgument( new OptionalNamedArg( "quote",
- "quotation-character",
- String.valueOf( COMMAS.quotationCharacter() ),
- "Character to treat as quotation character for values in CSV data. "
- + "Quotes can be escaped as per RFC 4180 by doubling them, for example \"\" would be " +
- "interpreted as a literal \". You cannot escape using \\." ) )
- .withArgument( new OptionalNamedArg( "max-memory",
- "max-memory-that-importer-can-use",
- String.valueOf( DEFAULT_MAX_MEMORY_PERCENT ) + "%",
- "Maximum memory that neo4j-admin can use for various data structures and caching " +
- "to improve performance. " +
- "Values can be plain numbers, like 10000000 or e.g. 20G for 20 gigabyte, or even e.g. 70%" +
- "." ) );
+ .withAdditionalConfig();
private static final Arguments allArguments = new Arguments()
.withDatabase()
.withAdditionalConfig()
.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." ) );
+
+ private static void includeDatabaseArguments( Arguments arguments )
+ {
+ arguments
.withArgument( new OptionalNamedArg( "from", "source-directory", "",
- "The location of the pre-3.0 database (e.g. /data/graph.db)." ) )
+ "The location of the pre-3.0 database (e.g. /data/graph.db)." ) );
+ }
+
+ private static void includeCsvArguments( Arguments arguments )
+ {
+ arguments
.withArgument( new OptionalNamedArg( "report-file", "filename", DEFAULT_REPORT_FILE_NAME,
"File in which to store the report of the csv-import." ) )
.withArgument( new OptionalNamedArgWithMetadata( "nodes",
@@ -144,7 +99,7 @@ public String usage()
"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." ) )
+ "Note that file groups must be enclosed in quotation marks." ) )
.withArgument( new OptionalNamedArgWithMetadata( "relationships",
":RELATIONSHIP_TYPE",
"\"file1,file2,...\"",
@@ -153,7 +108,7 @@ public String usage()
"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." ) )
+ "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:\n" +
@@ -175,10 +130,11 @@ public String usage()
"Whether or not fields from input source can span multiple lines," +
" i.e. contain newline characters." ) )
.withArgument( new OptionalNamedArg( "delimiter",
+ "delimiter-character",
String.valueOf( COMMAS.delimiter() ),
- String.valueOf( COMMAS.delimiter() ), "Delimiter character between values in CSV data." ) )
+ "Delimiter character between values in CSV data." ) )
.withArgument( new OptionalNamedArg( "array-delimiter",
- String.valueOf( COMMAS.delimiter() ),
+ "array-delimiter-character",
String.valueOf( COMMAS.arrayDelimiter() ),
"Delimiter character between array elements within a value in CSV data." ) )
.withArgument( new OptionalNamedArg( "quote",
@@ -193,7 +149,25 @@ public String usage()
"Maximum memory that neo4j-admin can use for various data structures and caching " +
"to improve performance. " +
"Values can be plain numbers, like 10000000 or e.g. 20G for 20 gigabyte, or even e.g. 70%" +
- "." ) );
+ "." ) )
+ .withArgument( new OptionalNamedArg( "f",
+ "File containing all arguments to this import",
+ "",
+ "File containing all arguments, used as an alternative to supplying all arguments on the command line directly."
+ + "Each argument can be on a separate line or multiple arguments per line separated by space."
+ + "Arguments containing spaces needs to be quoted."
+ + "Supplying other arguments in addition to this file argument is not supported." ) );
+ }
+
+ static
+ {
+ includeDatabaseArguments( databaseArguments );
+ includeDatabaseArguments( allArguments );
+
+ includeCsvArguments( csvArguments );
+ includeCsvArguments( allArguments );
+ }
+
public static Arguments databaseArguments()
{
return databaseArguments;
@@ -236,7 +210,12 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed
try
{
- mode = allArguments.parse( args ).get("mode" );
+ mode = allArguments.parse( args ).get( "mode" );
+ Optional fileArgument = allArguments.getOptionalPath( "f" );
+ if ( fileArgument.isPresent() )
+ {
+ allArguments.parse( parseFileArgumentList( fileArgument.get().toFile() ) );
+ }
database = allArguments.get( "database" );
additionalConfigFile = allArguments.getOptionalPath( "additional-config" );
}
@@ -244,6 +223,10 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed
{
throw new IncorrectUsage( e.getMessage() );
}
+ catch ( IOException e )
+ {
+ throw new UncheckedIOException( e );
+ }
try
{
@@ -261,11 +244,11 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed
}
catch ( IOException e )
{
- throw new RuntimeException( e );
+ throw new UncheckedIOException( e );
}
}
- private Map loadAdditionalConfig( Optional additionalConfigFile )
+ private static Map loadAdditionalConfig( Optional additionalConfigFile )
{
if ( additionalConfigFile.isPresent() )
{
diff --git a/community/dbms/src/test/java/org/neo4j/commandline/dbms/ImportCommandTest.java b/community/dbms/src/test/java/org/neo4j/commandline/dbms/ImportCommandTest.java
index 9cff1fedfc87..5c4b9fb39733 100644
--- a/community/dbms/src/test/java/org/neo4j/commandline/dbms/ImportCommandTest.java
+++ b/community/dbms/src/test/java/org/neo4j/commandline/dbms/ImportCommandTest.java
@@ -213,6 +213,7 @@ public void shouldPrintNiceHelp() throws Throwable
" [--array-delimiter=]%n" +
" [--quote=]%n" +
" [--max-memory=]%n" +
+ " [--f=]%n" +
"usage: neo4j-admin import --mode=database [--database=]%n" +
" [--additional-config=]%n" +
" [--from=]%n" +
@@ -274,9 +275,9 @@ public void shouldPrintNiceHelp() throws Throwable
" --multiline-fields=%n" +
" Whether or not fields from input source can span multiple lines, i.e.%n" +
" contain newline characters. [default:false]%n" +
- " --delimiter=<,>%n" +
+ " --delimiter=%n" +
" Delimiter character between values in CSV data. [default:,]%n" +
- " --array-delimiter=<,>%n" +
+ " --array-delimiter=%n" +
" Delimiter character between array elements within a value in CSV data.%n" +
" [default:;]%n" +
" --quote=%n" +
@@ -286,7 +287,13 @@ public void shouldPrintNiceHelp() throws Throwable
" --max-memory=%n" +
" Maximum memory that neo4j-admin can use for various data structures and%n" +
" caching to improve performance. Values can be plain numbers, like 10000000%n" +
- " or e.g. 20G for 20 gigabyte, or even e.g. 70%%. [default:90%%]%n"),
+ " or e.g. 20G for 20 gigabyte, or even e.g. 70%%. [default:90%%]%n" +
+ " --f=%n" +
+ " File containing all arguments, used as an alternative to supplying all%n" +
+ " arguments on the command line directly.Each argument can be on a separate%n" +
+ " line or multiple arguments per line separated by space.Arguments%n" +
+ " containing spaces needs to be quoted.Supplying other arguments in addition%n" +
+ " to this file argument is not supported. [default:]%n" ),
baos.toString() );
}
}
diff --git a/community/import-tool/src/main/java/org/neo4j/tooling/ImportTool.java b/community/import-tool/src/main/java/org/neo4j/tooling/ImportTool.java
index b43bae798cfa..daef03cc9054 100644
--- a/community/import-tool/src/main/java/org/neo4j/tooling/ImportTool.java
+++ b/community/import-tool/src/main/java/org/neo4j/tooling/ImportTool.java
@@ -25,8 +25,10 @@
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.Map.Entry;
import java.util.function.Function;
@@ -72,13 +74,16 @@
import org.neo4j.unsafe.impl.batchimport.staging.ExecutionMonitors;
import static java.nio.charset.Charset.defaultCharset;
+import static java.util.Arrays.asList;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.logs_directory;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.store_internal_log_path;
import static org.neo4j.helpers.Exceptions.launderedException;
import static org.neo4j.helpers.Format.bytes;
import static org.neo4j.helpers.Strings.TAB;
+import static org.neo4j.helpers.TextUtil.tokenizeStringWithQuotes;
import static org.neo4j.io.ByteUnit.mebiBytes;
+import static org.neo4j.io.fs.FileUtils.readTextFile;
import static org.neo4j.kernel.configuration.Settings.parseLongWithUnit;
import static org.neo4j.kernel.impl.util.Converters.withDefault;
import static org.neo4j.unsafe.impl.batchimport.AdditionalInitialIds.EMPTY;
@@ -118,6 +123,12 @@ public class ImportTool
enum Options
{
+ FILE( "f", null,
+ "",
+ "File containing all arguments, used as an alternative to supplying all arguments on the command line directly."
+ + "Each argument can be on a separate line or multiple arguments per line separated by space."
+ + "Arguments containing spaces needs to be quoted."
+ + "Supplying other arguments in addition to this file argument is not supported." ),
STORE_DIR( "into", null,
"",
"Database directory to import into. " + "Must not contain existing database." ),
@@ -266,9 +277,7 @@ enum Options
this( key, defaultValue, usage, description, supported, false );
}
- Options( String key, Object defaultValue, String usage, String description, boolean supported, boolean
- keyAndUsageGoTogether
- )
+ Options( String key, Object defaultValue, String usage, String description, boolean supported, boolean keyAndUsageGoTogether )
{
this.key = key;
this.defaultValue = defaultValue;
@@ -408,6 +417,8 @@ public static void main( String[] incomingArguments, boolean defaultSettingsSuit
boolean success = false;
try ( FileSystemAbstraction fs = new DefaultFileSystemAbstraction() )
{
+ args = useArgumentsFromFileArgumentIfPresent( args );
+
storeDir = args.interpretOption( Options.STORE_DIR.key(), Converters.mandatory(),
Converters.toFile(), Validators.DIRECTORY_IS_WRITABLE, Validators.CONTAINS_NO_EXISTING_DATABASE );
Config config = Config.defaults( GraphDatabaseSettings.neo4j_home, storeDir.getAbsolutePath() );
@@ -482,6 +493,31 @@ idType, csvConfiguration( args, defaultSettingsSuitableForTests ), badCollector,
}
}
+ public static Args useArgumentsFromFileArgumentIfPresent( Args args ) throws IOException
+ {
+ String fileArgument = args.get( Options.FILE.key(), null );
+ if ( fileArgument != null )
+ {
+ // Are there any other arguments supplied, in addition to this -f argument?
+ if ( args.asMap().size() > 1 )
+ {
+ throw new IllegalArgumentException(
+ "Supplying arguments in addition to " + Options.FILE.argument() + " isn't supported." );
+ }
+
+ // Read the arguments from the -f file and use those instead
+ args = Args.parse( parseFileArgumentList( new File( fileArgument ) ) );
+ }
+ return args;
+ }
+
+ public static String[] parseFileArgumentList( File file ) throws IOException
+ {
+ List arguments = new ArrayList<>();
+ readTextFile( file, line -> arguments.addAll( asList( tokenizeStringWithQuotes( line, true, true ) ) ) );
+ return arguments.toArray( new String[arguments.size()] );
+ }
+
static Long parseMaxMemory( String maxMemoryString )
{
if ( maxMemoryString != null )
diff --git a/community/import-tool/src/test/java/org/neo4j/tooling/ImportToolTest.java b/community/import-tool/src/test/java/org/neo4j/tooling/ImportToolTest.java
index 261b1ff6ce7d..bfa29daf5d28 100644
--- a/community/import-tool/src/test/java/org/neo4j/tooling/ImportToolTest.java
+++ b/community/import-tool/src/test/java/org/neo4j/tooling/ImportToolTest.java
@@ -89,6 +89,7 @@
import static org.neo4j.helpers.collection.Iterators.count;
import static org.neo4j.helpers.collection.MapUtil.store;
import static org.neo4j.helpers.collection.MapUtil.stringMap;
+import static org.neo4j.io.fs.FileUtils.writeToFile;
import static org.neo4j.kernel.impl.store.MetaDataStore.DEFAULT_NAME;
import static org.neo4j.tooling.ImportTool.MULTI_FILE_DELIMITER;
import static org.neo4j.unsafe.impl.batchimport.Configuration.BAD_FILE_NAME;
@@ -1906,6 +1907,57 @@ public void shouldKeepStoreFilesAfterFailedImport() throws Exception
}
}
+ @Test
+ public void shouldSupplyArgumentsAsFile() throws Exception
+ {
+ // given
+ List nodeIds = nodeIds();
+ Configuration config = Configuration.COMMAS;
+ File argumentFile = file( "args" );
+ String arguments = format(
+ "--into %s%n" +
+ "--nodes %s --relationships %s",
+ dbRule.getStoreDirAbsolutePath(),
+ nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(),
+ relationshipData( true, config, nodeIds, TRUE, true ).getAbsolutePath() );
+ writeToFile( argumentFile, arguments, false );
+
+ // when
+ importTool( "-f", argumentFile.getAbsolutePath() );
+
+ // then
+ verifyData();
+ }
+
+ @Test
+ public void shouldFailIfSupplyingBothFileArgumentAndAnyOtherArgument() throws Exception
+ {
+ // given
+ List nodeIds = nodeIds();
+ Configuration config = Configuration.COMMAS;
+ File argumentFile = file( "args" );
+ String arguments = format(
+ "--into %s%n" +
+ "--nodes %s --relationships %s",
+ dbRule.getStoreDirAbsolutePath(),
+ nodeData( true, config, nodeIds, TRUE ).getAbsolutePath(),
+ relationshipData( true, config, nodeIds, TRUE, true ).getAbsolutePath() );
+ writeToFile( argumentFile, arguments, false );
+
+ try
+ {
+ // when
+ importTool( "-f", argumentFile.getAbsolutePath(), "--into", dbRule.getStoreDirAbsolutePath() );
+ fail( "Should have failed" );
+ }
+ catch ( IllegalArgumentException e )
+ {
+ // then good
+ assertThat( e.getMessage(), containsString( "in addition to" ) );
+ assertThat( e.getMessage(), containsString( ImportTool.Options.FILE.argument() ) );
+ }
+ }
+
private void assertContains( List errorLines, String string )
{
for ( String line : errorLines )
diff --git a/community/shell/src/main/java/org/neo4j/shell/AppCommandParser.java b/community/shell/src/main/java/org/neo4j/shell/AppCommandParser.java
index a8a800afb410..bc3ae070de33 100644
--- a/community/shell/src/main/java/org/neo4j/shell/AppCommandParser.java
+++ b/community/shell/src/main/java/org/neo4j/shell/AppCommandParser.java
@@ -26,7 +26,7 @@
import org.neo4j.shell.apps.NoopApp;
-import static org.neo4j.shell.TextUtil.tokenizeStringWithQuotes;
+import static org.neo4j.helpers.TextUtil.tokenizeStringWithQuotes;
/**
* Parses a line from the client with the intention of interpreting it as
@@ -126,7 +126,7 @@ private void parseApp( String line ) throws Exception
private void parseParameters( String line ) throws ShellException
{
String rest = line.substring( appName.length() ).trim();
- String[] parsed = tokenizeStringWithQuotes( rest, false );
+ String[] parsed = tokenizeStringWithQuotes( rest, false, false );
for ( int i = 0; i < parsed.length; i++ )
{
String string = parsed[i];
diff --git a/community/shell/src/main/java/org/neo4j/shell/apps/Export.java b/community/shell/src/main/java/org/neo4j/shell/apps/Export.java
index fc2d164f1031..9492805c6d29 100644
--- a/community/shell/src/main/java/org/neo4j/shell/apps/Export.java
+++ b/community/shell/src/main/java/org/neo4j/shell/apps/Export.java
@@ -30,8 +30,6 @@
import org.neo4j.shell.impl.AbstractApp;
import org.neo4j.shell.util.json.JSONParser;
-import static org.neo4j.shell.TextUtil.stripFromQuotes;
-
/**
* Mimics the Bash application "export" and uses the client session
* {@link Session} as the data container.
@@ -91,4 +89,16 @@ private Object stripFromQuotesIfString( Object value )
{
return value instanceof String ? stripFromQuotes( value.toString() ) : value;
}
+
+ private static String stripFromQuotes( String string )
+ {
+ if ( string != null )
+ {
+ if ( string.startsWith( "\"" ) && string.endsWith( "\"" ) )
+ {
+ return string.substring( 1, string.length() - 1 );
+ }
+ }
+ return string;
+ }
}
diff --git a/community/shell/src/main/java/org/neo4j/shell/apps/extra/ScriptExecutor.java b/community/shell/src/main/java/org/neo4j/shell/apps/extra/ScriptExecutor.java
index 5a828516351a..9f396a761b3a 100644
--- a/community/shell/src/main/java/org/neo4j/shell/apps/extra/ScriptExecutor.java
+++ b/community/shell/src/main/java/org/neo4j/shell/apps/extra/ScriptExecutor.java
@@ -31,7 +31,7 @@
import org.neo4j.shell.Session;
import org.neo4j.shell.ShellException;
-import static org.neo4j.shell.TextUtil.tokenizeStringWithQuotes;
+import static org.neo4j.helpers.TextUtil.tokenizeStringWithQuotes;
/**
* Executes groovy scripts purely via reflection
diff --git a/community/shell/src/main/java/org/neo4j/shell/impl/AbstractAppServer.java b/community/shell/src/main/java/org/neo4j/shell/impl/AbstractAppServer.java
index d7405156fc1f..d97c6bb17b8b 100644
--- a/community/shell/src/main/java/org/neo4j/shell/impl/AbstractAppServer.java
+++ b/community/shell/src/main/java/org/neo4j/shell/impl/AbstractAppServer.java
@@ -30,6 +30,7 @@
import java.util.regex.Pattern;
import org.neo4j.helpers.Service;
+import org.neo4j.helpers.TextUtil;
import org.neo4j.shell.App;
import org.neo4j.shell.AppCommandParser;
import org.neo4j.shell.AppShellServer;
@@ -39,7 +40,6 @@
import org.neo4j.shell.Session;
import org.neo4j.shell.ShellException;
import org.neo4j.shell.TabCompletion;
-import org.neo4j.shell.TextUtil;
/**
* A common implementation of an {@link AppShellServer}. The server can be given
@@ -120,7 +120,7 @@ public Response interpretLine( Serializable clientId, String line, Output out )
Continuation commandResult = null;
for ( String command : line.split( Pattern.quote( "&&" ) ) )
{
- command = TextUtil.removeSpaces( command );
+ command = removeSpaces( command );
command = replaceAlias( command, session );
AppCommandParser parser = new AppCommandParser( this, command );
commandResult = parser.app().execute( parser, session, out );
@@ -133,6 +133,19 @@ public Response interpretLine( Serializable clientId, String line, Output out )
}
}
+ private static String removeSpaces( String command )
+ {
+ while ( command.length() > 0 && command.charAt( 0 ) == ' ' )
+ {
+ command = command.substring( 1 );
+ }
+ while ( command.length() > 0 && command.charAt( command.length() - 1 ) == ' ' )
+ {
+ command = command.substring( 0, command.length() - 1 );
+ }
+ return command;
+ }
+
protected String replaceAlias( String line, Session session )
{
boolean changed = true;
diff --git a/community/shell/src/main/java/org/neo4j/shell/kernel/apps/Cd.java b/community/shell/src/main/java/org/neo4j/shell/kernel/apps/Cd.java
index 3db7b0e884ad..796de8abdfa9 100644
--- a/community/shell/src/main/java/org/neo4j/shell/kernel/apps/Cd.java
+++ b/community/shell/src/main/java/org/neo4j/shell/kernel/apps/Cd.java
@@ -43,7 +43,7 @@
import org.neo4j.shell.ShellException;
import org.neo4j.shell.impl.RelationshipToNodeIterable;
-import static org.neo4j.shell.TextUtil.lastWordOrQuoteOf;
+import static org.neo4j.helpers.TextUtil.lastWordOrQuoteOf;
/**
* Mimics the POSIX application with the same name, i.e. traverses to a node.
diff --git a/community/shell/src/main/java/org/neo4j/shell/kernel/apps/TransactionProvidingApp.java b/community/shell/src/main/java/org/neo4j/shell/kernel/apps/TransactionProvidingApp.java
index 7691dd56d518..84ecc3af1eb6 100644
--- a/community/shell/src/main/java/org/neo4j/shell/kernel/apps/TransactionProvidingApp.java
+++ b/community/shell/src/main/java/org/neo4j/shell/kernel/apps/TransactionProvidingApp.java
@@ -43,6 +43,7 @@
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
+import org.neo4j.helpers.TextUtil;
import org.neo4j.helpers.collection.Pair;
import org.neo4j.shell.App;
import org.neo4j.shell.AppCommandParser;
@@ -52,7 +53,6 @@
import org.neo4j.shell.Output;
import org.neo4j.shell.Session;
import org.neo4j.shell.ShellException;
-import org.neo4j.shell.TextUtil;
import org.neo4j.shell.impl.AbstractApp;
import org.neo4j.shell.kernel.GraphDatabaseShellServer;
import org.neo4j.shell.util.json.JSONArray;