From e4b47545b9c1e4ef1ab6c04b7a42c7ddcbfb9392 Mon Sep 17 00:00:00 2001 From: "@fbiville" Date: Mon, 3 Apr 2017 01:08:07 +0200 Subject: [PATCH] [#50] Control whether fields can be quoted or not --- README.md | 3 ++- .../export/DsvConfiguration.java | 10 +++++++- .../export/io/DsvFieldSerializers.java | 21 ++++++++--------- .../export/io/DsvFileWriter.java | 14 ++++++++--- .../export/io/DsvSerializer.java | 2 +- .../export/DsvProcessorTest.java | 23 +++++++++++++++++++ .../export/io/DsvFileWriterTest.java | 17 +++++++++++++- 7 files changed, 72 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 6576cca..3cb3012 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,10 @@ To make it work, you need to specify the following options (by passing `-A${CONF - `GeneratedDocumentationPath`: mandatory - the folder path in which the files are going to be generated in. If not specified, the export won't be done. - `Documentation.FieldDelimiter`: optional (default: ,) - the delimiter of values within a row - `Documentation.DelimitedFirstField`: optional (default: false) - the first column of each row (headers included) is prefixed with the configured separator + - `Documentation.QuotedFields`: optional (default: true) - whether column values are quoted or not - `Documentation.ExportGrouping`: optional (default: SINGLE) - comma-separated values of grouping strategy. The data is exported to a single place (SINGLE), to a single place per enclosing package (PACKAGE), to a single place per enclosing class (CLASS). For now, "place" may mean one or two files, depending on `Documentation.ExportSplit`. - `Documentation.ExportSplit`: optional (default: NONE) - whether to split data by kind (KIND, e.g. procedure vs. function) or not (NONE). - - `Documentation.ExportedHeaders`: optional (default: *) - delimiter-separated values which define custom ordering and filtering of the available headers. They are separated by the configured delimiter (see `Documentation.FieldDelimiter`) and the first header must not be prefixed, even if `Documentation.DelimitedFirstField` is enabled. The available headers are (in default order): + - `Documentation.ExportedHeaders`: optional (default: *) - delimiter-separated values which define custom ordering and filtering of the available headers. They are not to be quoted (regardless of `Documentation.QuotedFields` setting), have to be separated by the configured delimiter (see `Documentation.FieldDelimiter`) and the first header must not be prefixed, even if `Documentation.DelimitedFirstField` is enabled. The available headers are (in default order): - type: `'procedure'` or `'function'` for now - name: procedure/function logical name diff --git a/processor/src/main/java/net/biville/florent/sproccompiler/export/DsvConfiguration.java b/processor/src/main/java/net/biville/florent/sproccompiler/export/DsvConfiguration.java index f352f2a..e6ffcc0 100644 --- a/processor/src/main/java/net/biville/florent/sproccompiler/export/DsvConfiguration.java +++ b/processor/src/main/java/net/biville/florent/sproccompiler/export/DsvConfiguration.java @@ -34,6 +34,7 @@ public class DsvConfiguration public static final String DOCUMENTATION_ROOT_PATH = "GeneratedDocumentationPath"; private static final String DOCUMENTATION_FIELD_DELIMITER = "Documentation.FieldDelimiter"; private static final String DOCUMENTATION_DELIMITED_FIRST_FIELD = "Documentation.DelimitedFirstField"; + private static final String DOCUMENTATION_QUOTED_FIELDS = "Documentation.QuotedFields"; private static final String DOCUMENTATION_EXPORTED_HEADERS = "Documentation.ExportedHeaders"; private static final String DOCUMENTATION_EXPORT_GROUPING = "Documentation.ExportGrouping"; private static final String DOCUMENTATION_EXPORT_SPLIT = "Documentation.ExportSplit"; @@ -44,12 +45,14 @@ public class DsvConfiguration private final EnumSet groupingStrategy; private final DsvSplitStrategy splitStrategy; private final boolean delimitedFirstField; + private final boolean quotedFields; public DsvConfiguration( Map actualOptions ) { rootPath = Optional.ofNullable( actualOptions.getOrDefault( DOCUMENTATION_ROOT_PATH, null ) ).map( Paths::get ); fieldDelimiter = actualOptions.getOrDefault( DOCUMENTATION_FIELD_DELIMITER, "," ); delimitedFirstField = parseBoolean(actualOptions.getOrDefault( DOCUMENTATION_DELIMITED_FIRST_FIELD, "false" )); + quotedFields = parseBoolean(actualOptions.getOrDefault( DOCUMENTATION_QUOTED_FIELDS, "true" )); rawHeaders = actualOptions.getOrDefault( DOCUMENTATION_EXPORTED_HEADERS, "*" ); groupingStrategy = parseGroupingStrategy( actualOptions.getOrDefault( DOCUMENTATION_EXPORT_GROUPING, "SINGLE" ).toUpperCase( Locale.ENGLISH ), @@ -60,10 +63,11 @@ public DsvConfiguration( Map actualOptions ) public static Set getSupportedOptions() { - Set options = new HashSet<>( 6 ); + Set options = new HashSet<>( 7 ); options.add( DOCUMENTATION_ROOT_PATH ); options.add( DOCUMENTATION_EXPORTED_HEADERS ); options.add( DOCUMENTATION_FIELD_DELIMITER ); + options.add( DOCUMENTATION_QUOTED_FIELDS ); options.add( DOCUMENTATION_DELIMITED_FIRST_FIELD ); options.add( DOCUMENTATION_EXPORT_GROUPING ); options.add( DOCUMENTATION_EXPORT_SPLIT ); @@ -91,6 +95,10 @@ public boolean isFirstFieldDelimited() { return delimitedFirstField; } + public boolean areFieldsQuoted() { + return quotedFields; + } + public String getRawHeaders() { return rawHeaders; diff --git a/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvFieldSerializers.java b/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvFieldSerializers.java index 9f305cd..39d8be1 100644 --- a/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvFieldSerializers.java +++ b/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvFieldSerializers.java @@ -19,7 +19,17 @@ import net.biville.florent.sproccompiler.UserFunctionProcessor; import net.biville.florent.sproccompiler.export.Either; import net.biville.florent.sproccompiler.export.messages.DsvExportError; +import org.neo4j.procedure.Description; +import org.neo4j.procedure.PerformsWrites; +import org.neo4j.procedure.Procedure; +import org.neo4j.procedure.UserFunction; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.util.Elements; +import javax.lang.model.util.SimpleTypeVisitor8; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -27,17 +37,6 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.PrimitiveType; -import javax.lang.model.util.Elements; -import javax.lang.model.util.SimpleTypeVisitor8; - -import org.neo4j.procedure.Description; -import org.neo4j.procedure.PerformsWrites; -import org.neo4j.procedure.Procedure; -import org.neo4j.procedure.UserFunction; /** * All possible DSV header values: this declaration order is the default one diff --git a/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvFileWriter.java b/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvFileWriter.java index a3de900..c1ea9cc 100644 --- a/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvFileWriter.java +++ b/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvFileWriter.java @@ -34,18 +34,20 @@ public class DsvFileWriter implements AutoCloseable private final Writer writer; private final String separator; private final boolean delimitFirstField; + private final boolean quoteFields; public DsvFileWriter( Collection header, Writer writer ) { - this( header, writer, "," , false); + this( header, writer, "," , false, true); } - public DsvFileWriter( Collection header, Writer writer, String separator, boolean delimitFirstField ) + public DsvFileWriter( Collection header, Writer writer, String separator, boolean delimitFirstField, boolean quoteFields ) { this.header = header; this.writer = writer; this.separator = separator; this.delimitFirstField = delimitFirstField; + this.quoteFields = quoteFields; } public void write( Stream records, Function>> rowFunction, @@ -77,7 +79,13 @@ public void close() private String joinFields( Stream fields ) { - String result = fields.map(field -> "\"" + field.replace("\"", "\"\"") + "\"").collect(joining(separator)); + String result = fields.map(field -> { + if (quoteFields) { + return "\"" + field.replace("\"", "\"\"") + "\""; + } + return field; + + }).collect(joining(separator)); if (delimitFirstField) { return separator + result; } diff --git a/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvSerializer.java b/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvSerializer.java index 7bd0677..f16b3fd 100644 --- a/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvSerializer.java +++ b/processor/src/main/java/net/biville/florent/sproccompiler/export/io/DsvSerializer.java @@ -110,7 +110,7 @@ private void serializeWithHeaders( File file, DsvConfiguration configuration, Co Collection headers ) { try ( FileWriter resource = new FileWriter( file ); - DsvFileWriter writer = new DsvFileWriter( headers, resource, configuration.getFieldDelimiter(), configuration.isFirstFieldDelimited() ) ) + DsvFileWriter writer = new DsvFileWriter( headers, resource, configuration.getFieldDelimiter(), configuration.isFirstFieldDelimited(), configuration.areFieldsQuoted() ) ) { writer.write( methods.stream(), ( method ) -> fieldExporter.exportFields( method, headers ), messagePrinter::print ); diff --git a/processor/src/test/java/net/biville/florent/sproccompiler/export/DsvProcessorTest.java b/processor/src/test/java/net/biville/florent/sproccompiler/export/DsvProcessorTest.java index 5e49ece..ebef94f 100644 --- a/processor/src/test/java/net/biville/florent/sproccompiler/export/DsvProcessorTest.java +++ b/processor/src/test/java/net/biville/florent/sproccompiler/export/DsvProcessorTest.java @@ -97,6 +97,29 @@ public void dumps_procedure_definition_to_dsv_with_custom_delimiter_and_package_ "|\"function\"|\"" + namespace + ".sum\"|\"long sum(int a,int b)\"|\"Performs super complex maths\"|\"\"|\"" + namespace + ".SimpleUserFunctions\"|\"\""); } + @Test + public void dumps_procedure_definition_to_dsv_with_custom_delimiter_and_package_grouping_and_delimited_first_field_and_unquoted() throws IOException + { + Iterable sources = + asList( JavaFileObjectUtils.INSTANCE.procedureSource( "valid/SimpleProcedures.java" ), + JavaFileObjectUtils.INSTANCE.procedureSource( "valid/SimpleUserFunctions.java" ) ); + + assert_().about( javaSources() ).that( sources ) + .withCompilerOptions( "-AGeneratedDocumentationPath=" + folder.getAbsolutePath(), + "-ADocumentation.FieldDelimiter=|", "-ADocumentation.ExportGrouping=PACKAGE", + "-ADocumentation.DelimitedFirstField=true", "-ADocumentation.QuotedFields=false" ) + .processedWith( processor ).compilesWithoutError(); + + String namespace = "net.biville.florent.sproccompiler.procedures.valid"; + String generatedCsv = readContents(Paths.get(folder.getAbsolutePath(), namespace + ".csv")); + assertThat(generatedCsv).isEqualTo( + "|type|qualified name|signature|description|execution mode|location|deprecated by\n" + + "|procedure|" + namespace + ".doSomething|void doSomething(int foo)||PERFORMS_WRITE|" + namespace + ".SimpleProcedures|doSomething2\n" + + "|procedure|" + namespace + ".doSomething2|void doSomething2(long bar)|Much better than the former version|SCHEMA|" + namespace + ".SimpleProcedures|\n" + + "|procedure|" + namespace + ".doSomething3|void doSomething3(LongWrapper bar)|Much better with records|SCHEMA|" + namespace + ".SimpleProcedures|\n" + + "|function|" + namespace + ".sum|long sum(int a,int b)|Performs super complex maths||" + namespace + ".SimpleUserFunctions|"); + } + @Test public void dumps_only_exported_fields_with_default_delimiter_and_package_grouping() throws IOException { diff --git a/processor/src/test/java/net/biville/florent/sproccompiler/export/io/DsvFileWriterTest.java b/processor/src/test/java/net/biville/florent/sproccompiler/export/io/DsvFileWriterTest.java index 4f4619d..3913c67 100644 --- a/processor/src/test/java/net/biville/florent/sproccompiler/export/io/DsvFileWriterTest.java +++ b/processor/src/test/java/net/biville/florent/sproccompiler/export/io/DsvFileWriterTest.java @@ -49,7 +49,7 @@ public void writes_csv_records_with_custom_separator_prefixing_first_field() { StringWriter writer = new StringWriter(); try ( DsvFileWriter dsvFileWriter = new DsvFileWriter( Arrays.asList( "first header", "second header" ), - writer, "$", true ) ) + writer, "$", true, true ) ) { dsvFileWriter.write( Stream.of( "haha_this is", "so much_fun" ), this::parseRow, ( error ) -> fail( "Unexpected export error: " + error ) ); @@ -59,6 +59,21 @@ public void writes_csv_records_with_custom_separator_prefixing_first_field() } } + @Test + public void writes_csv_records_with_custom_separator_prefixing_first_field_without_quoting_fields() + { + StringWriter writer = new StringWriter(); + try ( DsvFileWriter dsvFileWriter = new DsvFileWriter( Arrays.asList( "first header", "second header" ), + writer, "$", true, false ) ) + { + dsvFileWriter.write( Stream.of( "haha_this is", "so much_fun" ), this::parseRow, + ( error ) -> fail( "Unexpected export error: " + error ) ); + String result = writer.toString(); + assertThat( result ).isEqualTo( + "$first header$second header\n" + "$haha$this is\n" + "$so much$fun\n" ); + } + } + private Stream> parseRow( String input ) { return Arrays.stream( input.split( "_" ) ).map( Either::right );