This repository has been archived on 2022-08-05. You can view files and clone it, but cannot push or open issues or pull requests.
school-projects/src/main/java/io/gitlab/jfronny/ImgJava/util/Table.java

872 lines
27 KiB
Java

package io.gitlab.jfronny.ImgJava.util;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* Die Klasse Table vereinfacht den Zugriff auf CSV-Dateien.
* Die Klassen Table und TableRow ermöglichen einen einfachen Zugriff auf tabellenbasierte
* Dokumente.
*
* @author Thomas Schaller
* @version 1.0 vom 01.02.2019
*/
public class Table {
// Standardtrennzeichen für Spalten
private static final char DEFAULT_SEPARATOR = ';';
// Standardmarkierung für Texte
private static final char DEFAULT_QUOTE = '"';
// Standardtrennzeichen für Dezimalzahlen
private static final char DEFAULT_COMMA = ',';
// mögliche Spaltentypen
private static final String UNKNOWN = "UNKOWN";
private static final String INT = "INTEGER";
private static final String DOUBLE = "DOUBLE";
private static final String FLOAT = "FLOAT";
// interne Verwaltung des Dokuments als JDOM-Document-Objekt
private Document doc;
// Verweis auf Element für Kopfzeile
private Element header;
// Ende Attribute
/**
* Erzeugt leeres Tabellen-Dokument.
*/
public Table() {
this.doc = new Document();
doc.setRootElement(new Element("CSV-Data"));
this.header = new Element("Header");
doc.getRootElement().addContent(header);
}
/**
* Erzeugt Tabellen-Dokument aus einer CSV-Datei.
* Liest den Inhalt einer Datei und erstellt ein Tabellenobjekt mit seinen Werten.
* Wenn die Datei eine Kopfzeile enthält, fügen Sie "header" in den Parameter options ein. Wenn die Datei keine Kopfzeile hat,
* dann lassen Sie einfach die Option "header" weg.
* @param file Dateiname der CSV-Datei.
* @param options Geben Sie hier "header" an, wenn die Datei eine Kopfzeile enthält.
* @param separator Trennzeichen für Spalten (meist ';' oder ',' oder '\t' für Tab)
* @param quote Kennung für Texte (meist '"').
*/
public Table(Path file, String options, char separator, char quote) throws IOException {
loadCSV(file, options, separator, quote);
}
/**
* Erzeugt Tabellen-Dokument aus einer CSV-Datei.
* Liest den Inhalt einer Datei und erstellt ein Tabellenobjekt mit seinen Werten (Separator = ';', Kennung für Text = '"').
* Wenn die Datei eine Kopfzeile enthält, fügen Sie "header" in den Parameter options ein. Wenn die Datei keine Kopfzeile hat,
* dann lassen Sie einfach die Option "header" weg.
* @param file Dateiname der CSV-Datei.
* @param options Geben Sie hier "header" an, wenn die Datei eine Kopfzeile enthält.
*/
public Table(Path file, String options) throws IOException {
loadCSV(file, options);
}
/**
* Erzeugt Tabellen-Dokument aus einer CSV-Datei.
* Liest den Inhalt einer Datei ohne Kopfzeile und erstellt ein Tabellenobjekt mit seinen Werten (Separator = ';', Kennung für Text = '"').
* @param file Dateiname der CSV-Datei.
*/
public Table(Path file) throws IOException {
loadCSV(file);
}
// Anfang Methoden
/**
* Liest den Inhalt einer CSV-Datei ohne Kopfzeile (Separator = ';', Kennung für Text = '"').
* @param file Dateiname der CSV-Datei.
*/
public void loadCSV(Path file) throws IOException {
loadCSV(file, "");
}
/**
* Liest den Inhalt einer CSV-Datei (Separator = ';', Kennung für Text = '"').
* Wenn die Datei eine Kopfzeile enthält, fügen Sie "header" in den Parameter options ein. Wenn die Datei keine Kopfzeile hat,
* dann lassen Sie einfach die Option "header" weg.
* @param file Dateiname der CSV-Datei.
* @param options Geben Sie hier "header" an, wenn die Datei eine Kopfzeile enthält.
*/
public void loadCSV(Path file, String options) throws IOException {
loadCSV(file, options, DEFAULT_SEPARATOR, DEFAULT_QUOTE);
}
/**
* Liest den Inhalt einer CSV-Datei.
* Wenn die Datei eine Kopfzeile enthält, fügen Sie "header" in den Parameter options ein. Wenn die Datei keine Kopfzeile hat,
* dann lassen Sie einfach die Option "header" weg.
* @param file Dateiname der CSV-Datei.
* @param options Geben Sie hier "header" an, wenn die Datei eine Kopfzeile enthält.
* @param separator Trennzeichen für Spalten (meist ';' oder ',' oder '\t' für Tab)
* @param quote Kennung für Texte (meist '"').
*/
public void loadCSV(Path file, String options, char separator, char quote) throws IOException {
doc = new Document();
doc.setRootElement(new Element("CSV-Data"));
header = new Element("Header");
doc.getRootElement().addContent(header);
Scanner scanner = new Scanner(file);
if (options.toLowerCase().contains("header") && scanner.hasNext()) {
List<String> entries = parseLine(scanner.nextLine(), separator, quote);
int i = 0;
for (String s : entries) {
Element entry = new Element("Column");
header.addContent(entry);
entry.setText(s);
entry.setAttribute("type", "unknown");
i++;
}
}
List<Element> cols = header.getChildren();
while (scanner.hasNext()) {
Element line = new Element("Row");
doc.getRootElement().addContent(line);
List<String> entries = parseLine(scanner.nextLine(), separator, quote);
int i = 0;
for (String s : entries) {
if (i == cols.size()) {
Element entry = new Element("Column");
entry.setAttribute("type", "unknown");
header.addContent(entry);
cols = header.getChildren();
}
Element entry = new Element("Entry");
entry.setText(s);
line.addContent(entry);
i++;
}
}
scanner.close();
}
/**
* Speichert das aktuelle Dokument als CSV-Datei ohne Kopfzeile (Separator = ';', Kennung für Text = '"').
* @param file Dateiname der CSV-Datei.
*/
public void saveCSV(Path file) throws IOException {
saveCSV(file, "");
}
/**
* Speichert das aktuelle Dokument als CSV-Datei (Separator = ';', Kennung für Text = '"').
* Wenn die Datei eine Kopfzeile enthalten, fügen Sie "header" in den Parameter options ein. Wenn die Datei keine Kopfzeile haben soll,
* dann lassen Sie einfach die Option "header" weg.
* @param options Geben Sie hier "header" an, wenn die Datei eine Kopfzeile haben soll.
* @param file Dateiname der CSV-Datei.
*/
public void saveCSV(Path file, String options) throws IOException {
saveCSV(file, options, DEFAULT_SEPARATOR, DEFAULT_QUOTE);
}
/**
* Speichert das aktuelle Dokument als CSV-Datei.
* Wenn die Datei eine Kopfzeile enthalten, fügen Sie "header" in den Parameter options ein. Wenn die Datei keine Kopfzeile haben soll,
* dann lassen Sie einfach die Option "header" weg.
* @param options Geben Sie hier "header" an, wenn die Datei eine Kopfzeile haben soll.
* @param file Dateiname der CSV-Datei.
* @param separator Trennzeichen für Spalten (meist ';' oder ',' oder '\t' für Tab)
* @param quote Kennung für Texte (meist '"').
*/
public void saveCSV(Path file, String options, char separator, char quote) throws IOException {
PrintStream outputFile = new PrintStream(Files.newOutputStream(file));
System.out.println("Speicher in : " + file.toAbsolutePath());
List<Element> columns = header.getChildren();
String sq = "" + quote;
String ss = "" + separator;
if (quote == '"') sq = "\"";
if (separator == '"') ss = "\"";
if (options.toLowerCase().contains("header")) {
String h = "";
for (Element c : columns) {
h += ss + sq + c.getText() + sq;
}
outputFile.println(h.substring(1));
}
for (int i = 0; i < getRowCount(); i++) {
String l = "";
for (String s : getStringRow(i)) {
if (s.contains("" + separator)) {
if (quote == '"' && s.contains("\"")) {
s = s.replace("\"", "\"\"");
}
l += ss + sq + s + sq;
} else {
l += ss + s;
}
}
outputFile.println(l.substring(1));
}
outputFile.close();
}
/** Speichert die Tabelle als XML-Dokument.
* @param file Dateiname des XML-Files
*/
public void saveXML(Path file) throws IOException {
// new XMLOutputter().output(doc, System.out);
XMLOutputter xmlOutput = new XMLOutputter();
// display nice nice
xmlOutput.setFormat(Format.getPrettyFormat());
OutputStream outputFile = Files.newOutputStream(file);
System.out.println("Speicher in : " + file.toAbsolutePath());
xmlOutput.output(doc, outputFile);
outputFile.close();
System.out.println("File Saved!");
}
//----------------------------------------------- Zeigerbewegungen --------------------------------------------------
/** HIlfsfunktion für die Analyse einer Dateizeile
* @param cvsLine Zeile aus der Datei
* @return Liste von String für die einzelnen Spalten
*/
private List<String> parseLine(String cvsLine) {
return parseLine(cvsLine, DEFAULT_SEPARATOR, DEFAULT_QUOTE);
}
/** HIlfsfunktion für die Analyse einer Dateizeile
* @param cvsLine Zeile aus der Datei
* @param separator Trennzeichen für die Spalten
* @return Liste von String für die einzelnen Spalten
*/
private List<String> parseLine(String cvsLine, char separator) {
return parseLine(cvsLine, separator, DEFAULT_QUOTE);
}
/** HIlfsfunktion für die Analyse einer Dateizeile
* @param cvsLine Zeile aus der Datei
* @param separator Trennzeichen für die Spalten
* @param customQuote Kennung für Strings
* @return Liste von String für die einzelnen Spalten
*/
private List<String> parseLine(String cvsLine, char separator, char customQuote) {
List<String> result = new ArrayList<>();
//if empty, return!
if (cvsLine == null && cvsLine.isEmpty()) {
return result;
}
//ggf. Default-Value laden
if (customQuote == ' ') {
customQuote = DEFAULT_QUOTE;
}
if (separator == ' ') {
separator = DEFAULT_SEPARATOR;
}
StringBuffer curVal = new StringBuffer();
boolean inQuotes = false;
boolean startCollectChar = false;
boolean doubleQuotesInColumn = false;
char[] chars = cvsLine.toCharArray();
for (char ch : chars) {
if (inQuotes) { // aktueller Text ist in Quotes eingeschlossen
startCollectChar = true;
if (ch == customQuote) { // Quotes werden beendet, aber Achtung bei "" => Metazeichen
inQuotes = false;
if (ch == '\"') {
doubleQuotesInColumn = true;
}
} else {
if (ch == '\"' && !doubleQuotesInColumn) {
doubleQuotesInColumn = true;
} else {
curVal.append(ch);
doubleQuotesInColumn = false;
}
}
} else {
if (ch == customQuote) {
inQuotes = true;
//Fixed : allow "" in empty quote enclosed
if (ch == '\"') {
if (doubleQuotesInColumn) {
curVal.append('"');
doubleQuotesInColumn = false;
} else doubleQuotesInColumn = true;
}
} else {
doubleQuotesInColumn = false;
if (ch == separator) {
result.add(curVal.toString());
curVal = new StringBuffer();
startCollectChar = false;
} else if (ch == '\r') {
//ignore LF characters
continue;
} else if (ch == '\n') {
//the end, break!
break;
} else {
curVal.append(ch);
}
}
}
}
result.add(curVal.toString());
return result;
}
/**
* Sucht die Nummer einer durch Namen gegebenen Spalte.
* @param name Name der Spalte
* @return Nummer der Spalte
*/
private int findColumnNumber(String name) {
List<Element> columns = header.getChildren();
int i = 0;
for (Element c : columns) {
if (c.getText().equalsIgnoreCase(name)) {
return i;
}
i++;
}
return -1;
}
/**
* Fügt eine neue Spalte am Ende der Tabelle an.
*/
public void addColumn() {
Element entry = new Element("Column");
entry.setAttribute("type", Table.UNKNOWN);
header.addContent(entry);
}
/**
* Fügt eine neue Spalte am Ende der Tabelle an und benennt sie.
* @param title Bezeichnung der Spalte
*/
public void addColumn(String title) {
addColumn();
Element nc = ((List<Element>) (header.getChildren())).get(header.getChildren().size() - 1);
nc.setText(title);
}
/**
* Fügt eine neue Spalte am Ende der Tabelle an und benennt und typisiert sie.
* @param title Bezeichnung der Spalte
* @param type Typ der Spalte (UNKNOWN, DOUBLE, INTEGER, FLOAT)
*/
public void addColumn(String title, String type) {
addColumn(title);
Element nc = ((List<Element>) (header.getChildren())).get(header.getChildren().size() - 1);
nc.setAttribute("type", type);
}
/**
* Löscht eine Spalte.
* @param i Nummer der Spalte.
*/
public void removeColumn(int i) {
List<Element> lines = doc.getRootElement().getChildren();
for (Element l : lines) {
if (l.getChildren().size() > i) l.removeContent(i);
}
}
/**
* Löscht eine Spalte
* @param name Name der Spalte
*/
public void removeColumn(String name) {
removeColumn(findColumnNumber(name));
}
/**
* Liefert die Anzahl der Spalten in der Tabelle
* @return Anzahl der Spalten
*/
public int getColumnCount() {
return header.getChildren().size();
}
/**
* Liefert die Anzahl der Zeilen in der Tabelle
* @return Anzahl der Zeilen
*/
public int getRowCount() {
return doc.getRootElement().getChildren().size() - 1;
}
/**
* Löscht alle Zeilen der Tabelle.
* Die Spaltenüberschriften und Typen bleiben erhalten.
*/
public void clearRows() {
doc.getRootElement().removeChildren("Row");
}
/**
* Fügt eine neue Zeile an das Ende der Tabelle an.
* @return ein TableRow-Objekt für diese neue Zeile
*/
public TableRow addRow() {
Element row = new Element("Row");
doc.getRootElement().addContent(row);
return new TableRow(doc, row);
}
/**
* Löscht eine Zeile
* @param i Nummer der Zeile
*/
public void removeRow(int i) {
if (i < getRowCount()) {
doc.getRootElement().removeContent(i);
}
}
/**
* Liefert eine Zeile der Tabelle
* @param i Nummer der Zeile
* @return TableRow-Objekt für diese Zeile
*/
public TableRow getRow(int i) {
if (i < getRowCount()) {
List<Element> rows = doc.getRootElement().getChildren();
return new TableRow(doc, rows.get(i + 1));
}
return null;
}
/**
* Liefert die ganze Tabelle als Array von TableRow-Objekten
* @return Array von TableRow-Objekten
*/
public TableRow[] rows() {
TableRow[] rows = new TableRow[getRowCount()];
for (int i = 0; i < getRowCount(); i++) {
rows[i] = getRow(i);
}
return rows;
}
/**
* Liefert den Wert einer Zelle als Integer-Zahl
* @param row Zeilennummer
* @param column Spaltennummer
* @return Wert der Zelle
*/
public int getInt(int row, int column) {
return getRow(row).getInt(column);
}
/**
* Liefert den Wert einer Zelle als Integer-Zahl
* @param row Zeilennummer
* @param name Name der Spalte
* @return Wert der Zelle
*/
public int getInt(int row, String name) {
return getRow(row).getInt(name);
}
/**
* Setzt den Wert einer Zelle als Integer-Zahl
* @param row Zeilennummer
* @param column Spaltennummer
* @param value neuer Wert der Zelle
*/
public void setInt(int row, int column, int value) {
getRow(row).setInt(column, value);
}
/**
* Setzt den Wert einer Zelle als Integer-Zahl
* @param row Zeilennummer
* @param name Name der Spalte
* @param value neuer Wert der Zelle
*/
public void setInt(int row, String name, int value) {
getRow(row).setInt(name, value);
}
/**
* Liefert alle Werte einer Zeile als Integer-Array.
* @param row Nummer der Zeile
* @return int-Array, dass alle Werte der Zeile enthält
*/
public int[] getIntRow(int row) {
try {
TableRow trow = getRow(row);
int anz = getColumnCount();
int[] r = new int[anz];
for (int i = 0; i < anz; i++) {
r[i] = trow.getInt(i);
}
return r;
} catch (Exception e) {
return null;
}
}
/**
* Liefert alle Werte einer Spalte als Integer-Array.
* @param column Nummer der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public int[] getIntColumn(int column) {
try {
int anz = getRowCount();
int[] r = new int[anz];
for (int i = 0; i < anz; i++) {
r[i] = getInt(i, column);
}
return r;
} catch (Exception e) {
return null;
}
}
/**
* Liefert alle Werte einer Spalte als Integer-Array.
* @param name Name der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public int[] getIntColumn(String name) {
return getIntColumn(findColumnNumber(name));
}
/**
* Liefert den Wert einer Zelle als Float-Zahl
* @param row Zeilennummer
* @param column Spaltennummer
* @return Wert der Zelle
*/
public float getFloat(int row, int column) {
return getRow(row).getFloat(column);
}
/**
* Liefert den Wert einer Zelle als Float-Zahl
* @param row Zeilennummer
* @param name Name der Spalte
* @return Wert der Zelle
*/
public float getFloat(int row, String name) {
return getRow(row).getFloat(name);
}
/**
* Setzt den Wert einer Zelle als Float-Zahl
* @param row Zeilennummer
* @param column Spaltennummer
* @param value neuer Wert der Zelle
*/
public void setFloat(int row, int column, float value) {
getRow(row).setFloat(column, value);
}
/**
* Setzt den Wert einer Zelle als Float-Zahl
* @param row Zeilennummer
* @param name Name der Spalte
* @param value neuer Wert der Zelle
*/
public void setFloat(int row, String name, float value) {
getRow(row).setFloat(name, value);
}
/**
* Liefert alle Werte einer Zeile als Float-Array.
* @param row Nummer der Zeile
* @return int-Array, dass alle Werte der Zeile enthält
*/
public float[] getFloatRow(int row) {
try {
TableRow trow = getRow(row);
int anz = getColumnCount();
float[] r = new float[anz];
for (int i = 0; i < anz; i++) {
r[i] = trow.getFloat(i);
}
return r;
} catch (Exception e) {
return null;
}
}
/**
* Liefert alle Werte einer Spalte als Float-Array.
* @param column Nummer der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public float[] getFloatColumn(int column) {
try {
int anz = getRowCount();
float[] r = new float[anz];
for (int i = 0; i < anz; i++) {
r[i] = getFloat(i, column);
}
return r;
} catch (Exception e) {
return null;
}
}
/**
* Liefert alle Werte einer Spalte als Float-Array.
* @param name Name der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public float[] getFloatColumn(String name) {
return getFloatColumn(findColumnNumber(name));
}
/**
* Liefert den Wert einer Zelle als Double-Zahl
* @param row Zeilennummer
* @param column Spaltennummer
* @return Wert der Zelle
*/
public double getDouble(int row, int column) {
return getRow(row).getDouble(column);
}
/**
* Liefert den Wert einer Zelle als Double-Zahl
* @param row Zeilennummer
* @param name Name der Spalte
* @return Wert der Zelle
*/
public double getDouble(int row, String name) {
return getRow(row).getDouble(name);
}
/**
* Setzt den Wert einer Zelle als Double-Zahl
* @param row Zeilennummer
* @param column Spaltennummer
* @param value neuer Wert der Zelle
*/
public void setDouble(int row, int column, double value) {
getRow(row).setDouble(column, value);
}
/**
* Setzt den Wert einer Zelle als Double-Zahl
* @param row Zeilennummer
* @param name Name der Spalte
* @param value neuer Wert der Zelle
*/
public void setDouble(int row, String name, double value) {
getRow(row).setDouble(name, value);
}
/**
* Liefert alle Werte einer Spalte als Double-Array.
* @param row Nummer der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public double[] getDoubleRow(int row) {
try {
TableRow trow = getRow(row);
int anz = getColumnCount();
double[] r = new double[anz];
for (int i = 0; i < anz; i++) {
r[i] = trow.getDouble(i);
}
return r;
} catch (Exception e) {
return null;
}
}
/**
* Liefert alle Werte einer Spalte als Double-Array.
* @param column Nummer der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public double[] getDoubleColumn(int column) {
try {
int anz = getRowCount();
double[] r = new double[anz];
for (int i = 0; i < anz; i++) {
r[i] = getDouble(i, column);
}
return r;
} catch (Exception e) {
return null;
}
}
/**
* Liefert alle Werte einer Spalte als Double-Array.
* @param name Name der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public double[] getDoubleColumn(String name) {
return getDoubleColumn(findColumnNumber(name));
}
/**
* Liefert den Wert einer Zelle als String
* @param row Zeilennummer
* @param column Spaltennummer
* @return Wert der Zelle
*/
public String getString(int row, int column) {
return getRow(row).getString(column);
}
/**
* Liefert den Wert einer Zelle als String
* @param row Zeilennummer
* @param name Name der Spalte
* @return Wert der Zelle
*/
public String getString(int row, String name) {
return getRow(row).getString(name);
}
/**
* Setzt den Wert einer Zelle als String
* @param row Zeilennummer
* @param column Spaltennummer
* @param text neuer Wert der Zelle
*/
public void setString(int row, int column, String text) {
getRow(row).setString(column, text);
}
/**
* Setzt den Wert einer Zelle als String
* @param row Zeilennummer
* @param name Name der Spalte
* @param text neuer Wert der Zelle
*/
public void setString(int row, String name, String text) {
getRow(row).setString(name, text);
}
/**
* Liefert alle Werte einer Spalte als String-Array.
* @param row Nummer der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public String[] getStringRow(int row) {
try {
TableRow trow = getRow(row);
int anz = getColumnCount();
String[] r = new String[anz];
for (int i = 0; i < anz; i++) {
r[i] = trow.getString(i);
}
return r;
} catch (Exception e) {
return null;
}
}
/**
* Liefert alle Werte einer Spalte als String-Array.
* @param column Nummer der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public String[] getStringColumn(int column) {
try {
int anz = getRowCount();
String[] r = new String[anz];
for (int i = 0; i < anz; i++) {
r[i] = getString(i, column);
}
return r;
} catch (Exception e) {
return null;
}
}
/**
* Liefert alle Werte einer Spalte als String-Array.
* @param name Name der Spalte
* @return int-Array, dass alle Werte der Spalte enthält
*/
public String[] getStringColumn(String name) {
return getStringColumn(findColumnNumber(name));
}
/**
* Sucht nach einem bestimmtem Wert in einer Zeile.
* @param value Wert der gesucht werden soll
* @param column Nummer der Spalte, die durchsucht werden soll
* @return TableRow-Objekt der Zeile, wenn der Wert gefunden wurde, sonst null
*/
public TableRow findRow(String value, int column) {
for (int i = 0; i < getRowCount(); i++) {
if (getString(i, column).equals(value)) {
return getRow(i);
}
}
return null;
}
/**
* Sucht nach einem bestimmtem Wert in einer Zeile.
* @param value Wert der gesucht werden soll
* @param name Name der Spalte, die durchsucht werden soll
* @return TableRow-Objekt der Zeile, wenn der Wert gefunden wurde, sonst null
*/
public TableRow findRow(String value, String name) {
return findRow(value, findColumnNumber(name));
}
/**
* Kürzt alle Einträge der Tabelle um unnötige Leerzeichen am Anfang oder Ende
*/
public void trim() {
for (int y = 0; y < getRowCount(); y++) {
for (int x = 0; x < getColumnCount(); x++) {
setString(y, x, getString(y, x).trim());
}
}
}
}