/*
* Copyright (c) 2017 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* 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.obiba.magma.datasource.csv;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;
import org.obiba.magma.MagmaRuntimeException;
import org.obiba.magma.Value;
import org.obiba.magma.ValueSet;
import org.obiba.magma.ValueTableWriter;
import org.obiba.magma.Variable;
import org.obiba.magma.VariableEntity;
import org.obiba.magma.datasource.csv.converter.VariableConverter;
import org.obiba.magma.support.DatasourceParsingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import au.com.bytecode.opencsv.CSVWriter;
public class CsvValueTableWriter implements ValueTableWriter {
private static final Logger log = LoggerFactory.getLogger(CsvValueSet.class);
private final CsvValueTable valueTable;
public CsvValueTableWriter(CsvValueTable valueTable) {
this.valueTable = valueTable;
}
@NotNull
@Override
public ValueSetWriter writeValueSet(@NotNull VariableEntity entity) {
return new CsvValueSetWriter(entity);
}
@Override
public VariableWriter writeVariables() {
return new CsvVariableWriter();
}
@Override
public void close() {
}
private class CsvVariableWriter implements VariableWriter {
@Override
public void writeVariable(@NotNull Variable variable) {
try {
VariableConverter variableConverter = valueTable.getVariableConverter();
if(valueTable.isVariablesFileEmpty()) {
// Write Header
writeVariableToCsv(variableConverter.getHeader());
valueTable.setVariablesFileEmpty(false);
}
String[] line = variableConverter.marshal(variable);
writeVariableToCsv(line);
} catch(IOException e) {
throw new MagmaRuntimeException(e);
}
}
@Override
public void removeVariable(@NotNull Variable variable) {
throw new UnsupportedOperationException("Variable cannot be removed from a CSV file");
}
private void writeVariableToCsv(String... strings) throws IOException {
try(CSVWriter writer = valueTable.getVariableWriter()) {
if(writer == null) {
throw new DatasourceParsingException(
"Cannot create variable writer. Table " + valueTable.getName() + " does not have variable file.",
"CsvCannotCreateWriter", valueTable.getName());
}
log.trace("write '{}'", Arrays.toString(strings));
writer.writeNext(strings);
}
}
@Override
public void close() {}
}
private class CsvValueSetWriter implements ValueSetWriter {
@NotNull
private final VariableEntity entity;
private final CsvLine csvLine;
private CsvValueSetWriter(@NotNull VariableEntity entity) {
this.entity = entity;
if(valueTable.getParentFile() == null) {
throw new IllegalArgumentException("valueTable.getParentFile() cannot be null");
}
//noinspection ConstantConditions
csvLine = new CsvLine(entity, valueTable.getParentFile(), valueTable.isMultilines());
// Populate with existing values, if available
if(valueTable.hasValueSet(entity)) {
ValueSet valueSet = valueTable.getValueSet(entity);
for(Variable variable : valueTable.getVariables()) {
writeValue(variable, valueTable.getValue(variable, valueSet));
}
}
}
@Override
public void writeValue(@NotNull Variable variable, Value value) {
csvLine.setValue(variable, value);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void close() {
try {
if(valueTable.isDataFileEmpty()) {
writeTableWithoutData();
} else {
writeTableWithData();
}
// Writer Value set. Throw exception if doesn't match header
csvLine.getLines().forEach(line -> {
try {
writeValueToCsv(line);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
// Update entities index
((CsvVariableEntityProvider)valueTable.getVariableEntityProvider()).add(entity);
} catch(IOException e) {
throw new RuntimeException(e);
}
}
private void writeTableWithoutData() throws IOException {
// Write Header
if(valueTable.getDataHeaderMap().isEmpty()) {
Map<String, Integer> generatedHeader = generateDataHeaderMapFromVariables();
if(generatedHeader.size() > 0) {
valueTable.setDataHeaderMap(generateDataHeaderMapFromVariables());
csvLine.setHeaderMap(valueTable.getDataHeaderMap());
} else {
valueTable.setDataHeaderMap(csvLine.getHeaderMap());
}
}
writeValueToCsv(valueTable.getDataHeaderAsArray());
getExistingHeaderMap();
valueTable.setDataHeaderMap(csvLine.getHeaderMap());
valueTable.setDataFileEmpty(false);
}
private void writeTableWithData() throws IOException {// Test header is a subset
List<String> extraHeaders = getExtraHeadersFromNewValueSet(getExistingHeaderMap(), csvLine.getHeaderMap());
if(extraHeaders.size() != 0) {
StringBuilder sb = new StringBuilder();
for(String header : extraHeaders) {
sb.append(header).append(" ");
}
throw new MagmaRuntimeException("Cannot update the CSV ValueTable [" + valueTable.getName() +
"]. The new ValueSet (record) included the following unexpected Variables (fields): " + sb.toString());
}
if(valueTable.hasValueSet(entity)) {
// Delete existing value set.
//valueTable.clearEntity(entity);
}
// Set existing header
csvLine.setHeaderMap(getExistingHeaderMap());
}
private void writeValueToCsv(String... strings) throws IOException {
try(CSVWriter writer = valueTable.getValueWriter()) {
if(writer == null) {
throw new DatasourceParsingException(
"Cannot create data writer. Table " + valueTable.getName() + " does not have data file.",
"CsvCannotCreateWriter", valueTable.getName());
}
log.trace("write '{}'", Arrays.toString(strings));
writer.writeNext(strings);
}
}
private Map<String, Integer> getExistingHeaderMap() {
return valueTable.getDataHeaderMap();
}
private List<String> getExtraHeadersFromNewValueSet(Map<String, Integer> existingHeaderMap,
Map<String, Integer> valueSetHeaderMap) {
List<String> result = new ArrayList<>();
for(Map.Entry<String, Integer> entry : valueSetHeaderMap.entrySet()) {
if(!existingHeaderMap.containsKey(entry.getKey())) {
result.add(entry.getKey());
}
}
return result;
}
public Map<String, Integer> generateDataHeaderMapFromVariables() {
Map<String, Integer> headerMap = new HashMap<>();
int count = 1;
for(Variable variable : valueTable.getVariables()) {
headerMap.put(variable.getName(), count++);
}
return headerMap;
}
}
}