CSV.java
Home
/
src /
main /
java /
br /
ufrgs /
inf /
prosoft /
cache /
tools /
CSV.java
package br.ufrgs.inf.prosoft.cache.tools;
import br.ufrgs.inf.prosoft.cache.tools.CSV.Row;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
public class CSV implements Iterable<Row> {
private class Reference {
String value;
public Reference() {
}
public Reference(String value) {
this.value = value;
}
@Override
public int hashCode() {
int hash = 7;
hash = 11 * hash + Objects.hashCode(this.value);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this.value == null) {
return obj == null;
}
if (obj instanceof Reference) {
return this.value.equals(((Reference) obj).value);
}
if (obj instanceof String) {
return this.value.equals((String) obj);
}
return false;
}
@Override
public String toString() {
return value;
}
}
private class Header extends Reference {
public Header(String value) {
super(value);
}
}
private class Value {
private final Header header;
private Reference reference;
private final Row row;
private final Column column;
public Value(Header header, Row row, Column column) {
this(header, null, row, column);
}
public Value(Header header, Reference reference, Row row, Column column) {
this.header = header;
this.reference = reference;
this.row = row;
this.column = column;
}
public String getProperty() {
return header.value;
}
public String getValue() {
return reference.value;
}
private void setValue(Reference reference) {
this.reference = reference;
}
public Row getRow() {
return row;
}
public Column getColumn() {
return column;
}
@Override
public String toString() {
return getProperty() + ": " + getValue();
}
}
protected class Row implements Iterable<Value> {
private final List<Value> values;
public Row(List<Value> values) {
if (values == null) {
throw new RuntimeException("Tried to create a null row");
}
this.values = values;
}
public String get(String property) {
for (Value value : this) {
if (value.getProperty().equals(property)) {
return value.getValue();
}
}
throw new RuntimeException("data does not contain property " + property);
}
@Override
public Iterator<Value> iterator() {
return this.values.iterator();
}
@Override
public String toString() {
return values.toString();
}
}
private class Column extends Row {
public Column() {
this(new ArrayList<>());
}
public Column(List<Value> values) {
super(values);
}
}
private List<Header> headers;
private final List<Row> rows;
private final List<Column> columns;
private final Map<Header, Map<Reference, Value>> indexes;
public CSV(String... headers) {
this(headers, new String[]{});
}
public CSV(String[] headers, String[] indexes) {
this.headers = new ArrayList<>();
this.indexes = new HashMap<>();
this.columns = new ArrayList<>();
this.rows = new ArrayList<>();
ArrayList<String> indexesList = new ArrayList(Arrays.asList(indexes));
for (String strHeader : headers) {
Header header = new Header(strHeader);
this.headers.add(header);
if (indexesList.contains(header.toString())) {
indexesList.remove(header.toString());
this.indexes.put(header, new HashMap<>());
}
this.columns.add(new Column());
}
if (!indexesList.isEmpty()) {
throw new RuntimeException("indexes not present on headers: " + indexesList);
}
}
public CSV append(String... values) {
if (values.length != this.headers.size()) {
throw new RuntimeException("Values not matching headers length");
}
List<Value> list = new ArrayList<>();
Row row = new Row(list);
Iterator<Column> columnsIterator = this.columns.iterator();
Iterator<Header> headersIterator = this.headers.iterator();
for (String strValue : values) {
Header header = headersIterator.next();
Reference reference = new Reference(strValue);
Value value = new Value(header, reference, row, columnsIterator.next());
list.add(value);
if (this.indexes.containsKey(header)) {
if (this.indexes.get(header).get(reference) != null) {
throw new RuntimeException("duplicate index " + reference + " for " + header);
}
this.indexes.get(header).put(reference, value);
}
}
this.rows.add(row);
return this;
}
private static String[] process(String line) {
if (line.contains("'") || line.contains("\"")) {
throw new UnsupportedOperationException("Scaped values not supported yet");
}
return line.split(",");
}
public static CSV read(String path) {
return read(path, new String[]{});
}
public static CSV read(String path, String... indexes) {
try {
List<String> lines = Files.readAllLines(Paths.get(path));
if (lines.isEmpty()) {
throw new RuntimeException("Empty file");
}
String headers = lines.remove(0);
CSV csv = new CSV(process(headers), indexes);
lines.forEach(line -> {
csv.append(process(line));
});
return csv;
} catch (IOException ex) {
throw new RuntimeException("Cannot read file");
}
}
public Optional<Row> selectFirst(String property, String value) {
Header mockProperty = new Header(property);
Reference mockValue = new Reference(value);
if (!this.headers.contains(mockProperty)) {
throw new RuntimeException("header not present");
}
Map<Reference, Value> values = this.indexes.get(mockProperty);
if (values != null) {
return Optional.ofNullable(values.get(mockValue).getRow());
}
for (Row row : this) {
if (row.get(property).equals(value)) {
return Optional.ofNullable(row);
}
}
return Optional.empty();
}
public List<Row> select(String property, String value) {
Header mockProperty = new Header(property);
Reference mockValue = new Reference(value);
if (!this.headers.contains(mockProperty)) {
throw new RuntimeException("header not present");
}
List<Row> select = new ArrayList<>();
Map<Reference, Value> values = this.indexes.get(mockProperty);
if (values != null) {
select.add(values.get(mockValue).getRow());
return select;
}
for (Row row : this) {
if (row.get(property).equals(value)) {
select.add(row);
}
}
return select;
}
@Override
public Iterator<Row> iterator() {
return this.rows.iterator();
}
}