fix result mapping with double quotes string (#22)

Co-authored-by: ggmaleva <ggmaleva@yandex.ru>
This commit is contained in:
Gregory 2024-01-26 13:59:00 +03:00 committed by GitHub
parent 25f467e6f0
commit 8adf294551
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 68 additions and 54 deletions

View File

@ -1,6 +1,6 @@
package dev.vality.gambit.factory;
import dev.vality.gambit.util.Constants;
import dev.vality.gambit.util.CsvUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -18,8 +18,8 @@ public class DataMapFactory {
log.error("Headers or values cannot be empty. headers: {}, values {}", headers, values);
throw new IllegalArgumentException();
}
String[] splitHeaders = headers.split(Constants.SEPARATOR);
String[] splitValues = values.split(Constants.SEPARATOR);
String[] splitHeaders = headers.split(CsvUtils.SEPARATOR);
String[] splitValues = CsvUtils.trimAndSplitValueLine(values).toArray(new String[0]);
if (splitValues.length != splitHeaders.length) {
log.error("Error during split. headers: {}, values {}", splitHeaders, splitValues);
throw new IllegalArgumentException();

View File

@ -3,7 +3,7 @@ package dev.vality.gambit.service.impl;
import dev.vality.gambit.exception.FileProcessingException;
import dev.vality.gambit.model.DataEntries;
import dev.vality.gambit.service.FileService;
import dev.vality.gambit.util.Constants;
import dev.vality.gambit.util.CsvUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -12,8 +12,6 @@ import org.springframework.util.StringUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@ -46,7 +44,7 @@ public class CsvFileServiceImpl implements FileService {
log.error("Empty file");
throw new IllegalArgumentException();
}
List<String> inputHeaders = trimAndSplitLine(headerLine);
List<String> inputHeaders = CsvUtils.trimAndSplitLine(headerLine);
if (!CollectionUtils.isEmpty(existingHeaders)) {
validateByExistingHeaders(inputHeaders, existingHeaders);
}
@ -65,12 +63,12 @@ public class CsvFileServiceImpl implements FileService {
}
private String validateAndTrimValues(String line, int headersCount) {
List<String> trimmedValues = trimAndSplitValueLine(line);
List<String> trimmedValues = CsvUtils.trimAndSplitValueLine(line);
if (headersCount != trimmedValues.size()) {
log.error("Line '{}' doesn't match headers count {}", line, headersCount);
throw new IllegalArgumentException();
}
return String.join(Constants.SEPARATOR, trimmedValues);
return String.join(CsvUtils.SEPARATOR, trimmedValues);
}
private void validateByExistingHeaders(List<String> inputHeaders, List<String> existingHeaders) {
@ -86,31 +84,4 @@ public class CsvFileServiceImpl implements FileService {
}
}
private List<String> trimAndSplitLine(String line) {
return Arrays.stream(line.split(Constants.SEPARATOR))
.map(String::trim)
.collect(Collectors.toList());
}
private List<String> trimAndSplitValueLine(String line) {
List<String> values = new ArrayList<>();
int startPosition = 0;
boolean isInsideQuotes = false;
for (int currentPosition = 0; currentPosition < line.length(); currentPosition++) {
if (line.charAt(currentPosition) == Constants.DOUBLE_QUOTES) {
isInsideQuotes = !isInsideQuotes;
} else if (line.charAt(currentPosition) == Constants.SEPARATOR.charAt(0) && !isInsideQuotes) {
values.add(line.substring(startPosition, currentPosition).trim());
startPosition = currentPosition + 1;
}
}
String lastValue = line.substring(startPosition).trim();
if (lastValue.equals(Constants.SEPARATOR)) {
values.add("");
} else {
values.add(lastValue);
}
return values;
}
}

View File

@ -5,11 +5,11 @@ import dev.vality.gambit.domain.tables.pojos.DataSetInfo;
import dev.vality.gambit.exception.DataSetInfoAlreadyExistException;
import dev.vality.gambit.factory.DataFactory;
import dev.vality.gambit.model.DataEntries;
import dev.vality.gambit.service.FileService;
import dev.vality.gambit.service.DataService;
import dev.vality.gambit.service.DataSetInfoService;
import dev.vality.gambit.service.DataSetService;
import dev.vality.gambit.util.Constants;
import dev.vality.gambit.service.FileService;
import dev.vality.gambit.util.CsvUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -39,7 +39,7 @@ public class DataSetServiceImpl implements DataSetService {
validateDataSetNameRequest(dataSetName);
DataEntries dataEntries = fileService.process(bufferedReader);
Integer dataSetInfoId = dataSetInfoService.createDataSetInfo(
new DataSetInfo(null, dataSetName, String.join(Constants.SEPARATOR, dataEntries.getHeaders())));
new DataSetInfo(null, dataSetName, String.join(CsvUtils.SEPARATOR, dataEntries.getHeaders())));
dataService.saveDataBatch(dataEntries.getValues().stream()
.map(value -> DataFactory.create(dataSetInfoId, value))
.collect(Collectors.toList()));
@ -50,7 +50,7 @@ public class DataSetServiceImpl implements DataSetService {
public void updateDataSet(String dataSetName, BufferedReader bufferedReader) throws DataSetNotFound {
DataSetInfo dataSetInfo = dataSetInfoService.getDataSetInfoByName(dataSetName)
.orElseThrow(DataSetNotFound::new);
List<String> existingHeaders = Arrays.asList(dataSetInfo.getHeaders().split(Constants.SEPARATOR));
List<String> existingHeaders = Arrays.asList(dataSetInfo.getHeaders().split(CsvUtils.SEPARATOR));
DataEntries dataEntries = fileService.process(bufferedReader, existingHeaders);
dataService.saveDataBatch(dataEntries.getValues().stream()
.map(value -> DataFactory.create(dataSetInfo.getId(), value))

View File

@ -1,13 +0,0 @@
package dev.vality.gambit.util;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Constants {
public static final String SEPARATOR = ",";
public static final char DOUBLE_QUOTES = '\"';
}

View File

@ -0,0 +1,43 @@
package dev.vality.gambit.util;
import lombok.experimental.UtilityClass;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@UtilityClass
public class CsvUtils {
public static final String SEPARATOR = ",";
public static final char DOUBLE_QUOTES = '\"';
public static List<String> trimAndSplitLine(String line) {
return Arrays.stream(line.split(SEPARATOR))
.map(String::trim)
.collect(Collectors.toList());
}
public static List<String> trimAndSplitValueLine(String line) {
List<String> values = new ArrayList<>();
int startPosition = 0;
boolean isInsideQuotes = false;
for (int currentPosition = 0; currentPosition < line.length(); currentPosition++) {
if (line.charAt(currentPosition) == DOUBLE_QUOTES) {
isInsideQuotes = !isInsideQuotes;
} else if (line.charAt(currentPosition) == SEPARATOR.charAt(0) && !isInsideQuotes) {
values.add(line.substring(startPosition, currentPosition).trim());
startPosition = currentPosition + 1;
}
}
String lastValue = line.substring(startPosition).trim();
if (lastValue.equals(SEPARATOR)) {
values.add("");
} else {
values.add(lastValue);
}
return values;
}
}

View File

@ -4,7 +4,8 @@ import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class DataMapFactoryTest {
@ -24,6 +25,18 @@ class DataMapFactoryTest {
assertThrows(IllegalArgumentException.class, () -> DataMapFactory.createDataMap("one,two", "tres"));
}
@Test
void createDataMapWithDoubleQuotes() {
String headers = "one,two";
String values = "uno,\"dos,tres\"";
Map<String, String> expected = Map.of(
"one", "uno",
"two", "\"dos,tres\""
);
Map<String, String> actual = DataMapFactory.createDataMap(headers, values);
assertEquals(expected, actual);
}
@Test
void createDataMap() {
Map<String, String> expected = Map.of(