/*
* Copyright 2007 - 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.jailer.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* Parser for CSV-files.
*
* @author Ralf Wisser
*/
public class CsvFile {
/**
* A line in a CSV-file.
*/
public static class Line {
/**
* Describes the position of the line in a file.
*/
public final String location;
/**
* The cells.
*/
public final List<String> cells;
/**
* Length of the line.
*/
public int length;
/**
* Constructor.
*
* @param location describes the position of the line in a file
* @param cells the cells
*/
public Line(String location, List<String> cells) {
this.location = location;
this.cells = new ArrayList<String>(cells);
int num = 0;
int l = 0;
for (String s : cells) {
++num;
if (s != null && s.trim().length() > 0) {
l = num;
}
}
for (int i = 0; i < 10; ++i) {
this.cells.add("");
}
this.length = l;
}
/**
* Line as String.
*/
public String toString() {
int num = 0;
int l = 0;
for (String s : cells) {
++num;
if (s != null && s.trim().length() > 0) {
l = num;
}
}
StringBuffer sb = new StringBuffer();
if (l >= cells.size()) {
l = cells.size() - 1;
}
for (int i = 0; i <= l; ++i) {
sb.append(encodeCell(cells.get(i)) + "; ");
}
return sb.toString();
}
};
public static interface LineFilter {
boolean accept(Line line);
}
/**
* List of lines.
*/
private List<Line> rows = new ArrayList<Line>();
/**
* Indicates start of block inside a CSV file.
*/
public static String BLOCK_INDICATOR = "#! block ";
/**
* Constructor.
*
* @param csvFile the csv file
*/
public CsvFile(File csvFile) throws IOException {
this(csvFile, null, null);
}
/**
* Constructor.
*
* @param csvFile the csv file
*/
public CsvFile(File csvFile, LineFilter filter) throws IOException {
this(csvFile, null, filter);
}
/**
* Constructor.
*
* @param csvFile the csv file
* @param block the block to read, <code>null</code> to read default block
*/
public CsvFile(File csvFile, String block) throws IOException {
this(csvFile, block, null);
}
/**
* Constructor.
*
* @param csvFile the csv file
* @param block the block to read, <code>null</code> to read default block
*/
public CsvFile(File csvFile, String block, LineFilter filter) throws IOException {
if (csvFile.exists()) {
BufferedReader reader = new BufferedReader(new FileReader(csvFile));
String line = null;
int lineNr = 0;
boolean inBlock = block == null;
while ((line = reader.readLine()) != null) {
++lineNr;
if (line.trim().length() == 0) {
continue;
}
if (line.trim().startsWith(BLOCK_INDICATOR)) {
if (inBlock) {
break;
}
String blockName = line.trim().substring(BLOCK_INDICATOR.length()).trim();
inBlock = block.equals(blockName);
continue;
}
if (line.trim().startsWith("#")) {
continue;
}
if (!inBlock) {
continue;
}
List<String> row = new ArrayList<String>();
String[] col = decodeLine(line);
for (int i = 0; i < col.length; ++i) {
String s = col[i];
row.add(s.trim());
}
while (row.size() < 100) {
row.add("");
}
Line cvsLine = new Line("line " + lineNr + ", " + csvFile.getName(), row);
if (filter == null || filter.accept(cvsLine)) {
rows.add(cvsLine);
}
}
reader.close();
}
}
/**
* Constructor.
*
* @param in to read from
* @param block the block to read, <code>null</code> to read default block
*/
public CsvFile(InputStream in, String block, String location, LineFilter filter) throws IOException {
if (in != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
int lineNr = 0;
boolean inBlock = block == null;
while ((line = reader.readLine()) != null) {
++lineNr;
if (line.trim().length() == 0) {
continue;
}
if (line.trim().startsWith(BLOCK_INDICATOR)) {
if (inBlock) {
break;
}
String blockName = line.trim().substring(BLOCK_INDICATOR.length()).trim();
inBlock = block.equals(blockName);
continue;
}
if (line.trim().startsWith("#")) {
continue;
}
if (!inBlock) {
continue;
}
List<String> row = new ArrayList<String>();
String[] col = decodeLine(line);
for (int i = 0; i < col.length; ++i) {
String s = col[i];
row.add(s.trim());
}
while (row.size() < 100) {
row.add("");
}
Line cvsLine = new Line("line " + lineNr + ", " + location, row);
if (filter == null || filter.accept(cvsLine)) {
rows.add(cvsLine);
}
}
in.close();
}
}
/**
* Decodes and splits csv-line.
*
* @param line the line to decode
* @return decoded and splitted line
*/
public static String[] decodeLine(String line) {
List<String> cells = new ArrayList<String>();
StringBuilder sb = new StringBuilder();
boolean esc = false;
for (int i = 0; i < line.length(); ++i) {
char c = line.charAt(i);
if (c == '\\') {
if (esc) {
esc = false;
} else {
esc = true;
continue;
}
}
if (!esc && c == ';') {
cells.add(sb.toString());
sb.setLength(0);
} else {
if (esc && c == 'n') {
c = '\n';
} else if (esc && c == 'r') {
c = '\r';
}
sb.append(c);
}
esc = false;
}
cells.add(sb.toString());
return (String[]) cells.toArray(new String[cells.size()]);
}
/**
* Encodes and csv-cell.
*
* @param cell the cell to encode
* @return encoded cell
*/
public static String encodeCell(String cell) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < cell.length(); ++i) {
char c = cell.charAt(i);
if (c == ';') {
sb.append("\\;");
} else if (c == '\\') {
sb.append("\\\\");
} else if (c == '\n') {
sb.append("\\n");
} else if (c == '\r') {
sb.append("\\r");
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Gets the list of lines.
*
* @return list of lists of cell-contents
*/
public List<Line> getLines() {
return rows;
}
/**
* Checks if a certain line can be found in this file.
*
* @param the line
* @return <code>true</code> iff this file contains the line
*/
public boolean contains(String[] line) {
for (Line l: getLines()) {
boolean differenceFound = false;
int i = 0;
for (String cell: line) {
if (cell != null && !cell.equals(l.cells.get(i))) {
differenceFound = true;
break;
}
++i;
}
if (!differenceFound) {
return true;
}
}
return false;
}
}