From caf4144f98ff834c8fd19ab5d0c5379fa05e32f7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Tue, 16 Jan 2018 11:33:04 +0100 Subject: [PATCH] SONAR-10288 move multivalue property parsing to sonar-core --- sonar-core/pom.xml | 4 + .../sonar/core/config/MultivalueProperty.java | 85 ++++++++++++++++++- .../core/config/MultivaluePropertyTest.java | 6 +- sonar-scanner-engine/pom.xml | 4 - .../scanner/config/DefaultConfiguration.java | 69 +-------------- .../scanner/scan/ProjectReactorBuilder.java | 5 +- .../ModuleFileSystemInitializer.java | 2 +- 7 files changed, 93 insertions(+), 82 deletions(-) rename sonar-scanner-engine/src/main/java/org/sonar/scanner/config/MultivaluePropertyCleaner.java => sonar-core/src/main/java/org/sonar/core/config/MultivalueProperty.java (57%) rename sonar-scanner-engine/src/test/java/org/sonar/scanner/config/MultivaluePropertyCleanerTest.java => sonar-core/src/test/java/org/sonar/core/config/MultivaluePropertyTest.java (98%) diff --git a/sonar-core/pom.xml b/sonar-core/pom.xml index be0e9836a67..a0e5e0af6ac 100644 --- a/sonar-core/pom.xml +++ b/sonar-core/pom.xml @@ -27,6 +27,10 @@ commons-codec commons-codec + + org.apache.commons + commons-csv + org.picocontainer picocontainer diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/config/MultivaluePropertyCleaner.java b/sonar-core/src/main/java/org/sonar/core/config/MultivalueProperty.java similarity index 57% rename from sonar-scanner-engine/src/main/java/org/sonar/scanner/config/MultivaluePropertyCleaner.java rename to sonar-core/src/main/java/org/sonar/core/config/MultivalueProperty.java index a5ae8d807e6..17451ee0cb6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/config/MultivaluePropertyCleaner.java +++ b/sonar-core/src/main/java/org/sonar/core/config/MultivalueProperty.java @@ -17,13 +17,89 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.scanner.config; +package org.sonar.core.config; -class MultivaluePropertyCleaner { - private MultivaluePropertyCleaner() { +import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.commons.lang.ArrayUtils; + +public class MultivalueProperty { + private MultivalueProperty() { // prevents instantiation } + public static String[] parseAsCsv(String key, String value) { + return parseAsCsv(key, value, Function.identity()); + } + + public static String[] parseAsCsv(String key, String value, Function valueProcessor) { + String cleanValue = MultivalueProperty.trimFieldsAndRemoveEmptyFields(value); + List result = new ArrayList<>(); + try (CSVParser csvParser = CSVFormat.RFC4180 + .withHeader((String) null) + .withIgnoreEmptyLines() + .withIgnoreSurroundingSpaces() + .parse(new StringReader(cleanValue))) { + List records = csvParser.getRecords(); + if (records.isEmpty()) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + processRecords(result, records, valueProcessor); + return result.toArray(new String[result.size()]); + } catch (IOException e) { + throw new IllegalStateException("Property: '" + key + "' doesn't contain a valid CSV value: '" + value + "'", e); + } + } + + /** + * In most cases we expect a single record.
Having multiple records means the input value was splitted over multiple lines (this is common in Maven). + * For example: + *
+   *   <sonar.exclusions>
+   *     src/foo,
+   *     src/bar,
+   *     src/biz
+   *   <sonar.exclusions>
+   * 
+ * In this case records will be merged to form a single list of items. Last item of a record is appended to first item of next record. + *

+ * This is a very curious case, but we try to preserve line break in the middle of an item: + *

+   *   <sonar.exclusions>
+   *     a
+   *     b,
+   *     c
+   *   <sonar.exclusions>
+   * 
+ * will produce ['a\nb', 'c'] + */ + private static void processRecords(List result, List records, Function valueProcessor) { + for (CSVRecord csvRecord : records) { + Iterator it = csvRecord.iterator(); + if (!result.isEmpty()) { + String next = it.next(); + if (!next.isEmpty()) { + int lastItemIdx = result.size() - 1; + String previous = result.get(lastItemIdx); + if (previous.isEmpty()) { + result.set(lastItemIdx, valueProcessor.apply(next)); + } else { + result.set(lastItemIdx, valueProcessor.apply(previous + "\n" + next)); + } + } + } + it.forEachRemaining(s -> result.add(valueProcessor.apply(s))); + } + } + /** * Removes the empty fields from the value of a multi-value property from empty fields, including trimming each field. *

@@ -48,7 +124,8 @@ class MultivaluePropertyCleaner { *

  • {@code "a,\" \",b" => "ab"]}
  • * */ - public static String trimFieldsAndRemoveEmptyFields(String str) { + @VisibleForTesting + static String trimFieldsAndRemoveEmptyFields(String str) { char[] chars = str.toCharArray(); char[] res = new char[chars.length]; /* diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/config/MultivaluePropertyCleanerTest.java b/sonar-core/src/test/java/org/sonar/core/config/MultivaluePropertyTest.java similarity index 98% rename from sonar-scanner-engine/src/test/java/org/sonar/scanner/config/MultivaluePropertyCleanerTest.java rename to sonar-core/src/test/java/org/sonar/core/config/MultivaluePropertyTest.java index 70be5c59b90..d9daba88c54 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/config/MultivaluePropertyCleanerTest.java +++ b/sonar-core/src/test/java/org/sonar/core/config/MultivaluePropertyTest.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.scanner.config; +package org.sonar.core.config; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; @@ -30,10 +30,10 @@ import org.junit.runner.RunWith; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.scanner.config.MultivaluePropertyCleaner.trimFieldsAndRemoveEmptyFields; +import static org.sonar.core.config.MultivalueProperty.trimFieldsAndRemoveEmptyFields; @RunWith(DataProviderRunner.class) -public class MultivaluePropertyCleanerTest { +public class MultivaluePropertyTest { @Rule public ExpectedException expectedException = ExpectedException.none(); diff --git a/sonar-scanner-engine/pom.xml b/sonar-scanner-engine/pom.xml index 5b2147d285f..622c760e457 100644 --- a/sonar-scanner-engine/pom.xml +++ b/sonar-scanner-engine/pom.xml @@ -76,10 +76,6 @@ com.google.code.gson gson
    - - org.apache.commons - commons-csv - org.freemarker diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/config/DefaultConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/config/DefaultConfiguration.java index 0b32c98a1d5..dfdfa2f9f1d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/config/DefaultConfiguration.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/config/DefaultConfiguration.java @@ -19,19 +19,11 @@ */ package org.sonar.scanner.config; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Optional; import javax.annotation.concurrent.Immutable; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; import org.apache.commons.lang.ArrayUtils; import org.sonar.api.config.Configuration; import org.sonar.api.config.Encryption; @@ -44,6 +36,7 @@ import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import static java.util.Objects.requireNonNull; import static org.apache.commons.lang.StringUtils.trim; +import static org.sonar.core.config.MultivalueProperty.parseAsCsv; @Immutable public abstract class DefaultConfiguration implements Configuration { @@ -119,66 +112,6 @@ public abstract class DefaultConfiguration implements Configuration { return ArrayUtils.EMPTY_STRING_ARRAY; } - public static String[] parseAsCsv(String key, String value) { - String cleanValue = MultivaluePropertyCleaner.trimFieldsAndRemoveEmptyFields(value); - List result = new ArrayList<>(); - try (CSVParser csvParser = CSVFormat.RFC4180 - .withHeader((String) null) - .withIgnoreEmptyLines() - .withIgnoreSurroundingSpaces() - .parse(new StringReader(cleanValue))) { - List records = csvParser.getRecords(); - if (records.isEmpty()) { - return ArrayUtils.EMPTY_STRING_ARRAY; - } - processRecords(result, records); - return result.toArray(new String[result.size()]); - } catch (IOException e) { - throw new IllegalStateException("Property: '" + key + "' doesn't contain a valid CSV value: '" + value + "'", e); - } - } - - /** - * In most cases we expect a single record.
    Having multiple records means the input value was splitted over multiple lines (this is common in Maven). - * For example: - *
    -   *   <sonar.exclusions>
    -   *     src/foo,
    -   *     src/bar,
    -   *     src/biz
    -   *   <sonar.exclusions>
    -   * 
    - * In this case records will be merged to form a single list of items. Last item of a record is appended to first item of next record. - *

    - * This is a very curious case, but we try to preserve line break in the middle of an item: - *

    -   *   <sonar.exclusions>
    -   *     a
    -   *     b,
    -   *     c
    -   *   <sonar.exclusions>
    -   * 
    - * will produce ['a\nb', 'c'] - */ - private static void processRecords(List result, List records) { - for (CSVRecord csvRecord : records) { - Iterator it = csvRecord.iterator(); - if (!result.isEmpty()) { - String next = it.next(); - if (!next.isEmpty()) { - int lastItemIdx = result.size() - 1; - String previous = result.get(lastItemIdx); - if (previous.isEmpty()) { - result.set(lastItemIdx, next); - } else { - result.set(lastItemIdx, previous + "\n" + next); - } - } - } - it.forEachRemaining(result::add); - } - } - private Optional getInternal(String key) { if (mode.isIssues() && key.endsWith(".secured") && !key.contains(".license")) { throw MessageException.of("Access to the secured property '" + key diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorBuilder.java index ac49740357b..9f5716c1a05 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorBuilder.java @@ -45,9 +45,10 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.scanner.analysis.AnalysisProperties; import org.sonar.scanner.bootstrap.DroppedPropertyChecker; -import org.sonar.scanner.config.DefaultConfiguration; import org.sonar.scanner.util.ScannerUtils; +import static org.sonar.core.config.MultivalueProperty.parseAsCsv; + /** * Class that creates a project definition based on a set of properties. */ @@ -408,7 +409,7 @@ public class ProjectReactorBuilder { static String[] getListFromProperty(Map properties, String key) { String propValue = properties.get(key); if (propValue != null) { - return DefaultConfiguration.parseAsCsv(key, propValue); + return parseAsCsv(key, propValue); } return new String[0]; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java index 57dde312c17..12872bb2e49 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java @@ -38,7 +38,7 @@ import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import static org.sonar.scanner.config.DefaultConfiguration.parseAsCsv; +import static org.sonar.core.config.MultivalueProperty.parseAsCsv; @ScannerSide @Immutable -- 2.39.5