TreeTable exportieren/importieren

KlaRo

Mitglied
Schönen guten Tag an die Community,

ich würde gerne eine TreeTable (Swing) in eine Datei (XML, CSV, whatever) exportieren und selbige wieder importieren. Sinn des Ganzen ist ich lese viele Dateien und einige deren Infos aus und gebe alles in einer JTreeTable aus. Um dies nicht ständig aufs Neue tun zu müssen, was recht zeitintensiv ist, würde ich gerne die Daten aus der JTreeTable in eine Datei – Format spielt eigentlich keine Rolle – exportieren/importieren.

Bisher habe ich leider nur den Export/Import von JTrees oder JTables gefunden, allerdings leider nicht von der Kombination aus beiden. Hat ev. dazu jemand eine Lösung, Codebeispiel, Link? Der Export von dem Tree oder dem Table einzeln klappt einwandfrei.

Wäre super, wenn mir jemand helfen könnte!

Viele Grüße aus dem verschneiten Süden Deutschlands...
 
Wie meinst Du das genau?

Sagen wir mal, Du hättest eine CSV-Datei mit 1 Mio. Zeilen und 200 Spalten. Für die Anzeige brauchst Du davon aber nur 100.000 Zeilen und 5 Spalten. Dann transformierst Du die Datei halt vorab derart, so dass sie nur noch das enthält, was Du anzeigen willst, was dann zu einer neuen CSV-Datei mit eben 100.000 Zeilen und 5 Spalten führt. Diese neue Datei verwendest Du dann zum Einlesen. Dazu musst Du nicht die TreeTable exportieren.
 
Ja OK das Exportieren habe ich auf diese Weise bereits in eine CSV-Datei realisiert. Allerdings schaffe ich es leider nicht diese Daten wieder in die TreeTable zu importieren.

Meine Frage ist nun, wie kann ich diese Daten aus der CSV-Datei wieder in die TreeTable importieren?

Die Daten in der CSV-Datei sind wie folgt aufgebaut:
Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis1-4/Verzeichnis1-5/Dateiname1;Info 1;Info 2;Info 3;Info 4
Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis1-4/Verzeichnis1-5/Dateiname2;Info 1;Info 2;Info 3;Info 4
Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis1-4/Verzeichnis2-5/Dateiname1;Info 1;Info 2;Info 3;Info 4
Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis1-4/Verzeichnis2-5/Dateiname2;Info 1;Info 2;Info 3;Info 4
Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis2-4/Verzeichnis1-5/Dateiname1;Info 1;Info 2;Info 3;Info 4
was dann in der Treetable wie folgt nach dem Einlesen aussehen soll:
Verzeichnis1-3
-Verzeichnis1-4
--Verzeichnis1-5
---Dateiname1;Info 1;Info 2;Info 3;Info 4
---Dateiname2;Info 1;Info 2;Info 3;Info 4
--Verzeichnis2-5
---Dateiname1;Info 1;Info 2;Info 3;Info 4
---Dateiname2;Info 1;Info 2;Info 3;Info 4
-Verzeichnis2-4
--Verzeichnis1-5
---Dateiname1;Info 1;Info 2;Info 3;Info 4
---Dateiname2;Info 1;Info 2;Info 3;Info 4

Oder gibt es gar eine einfachere/elegantere Lösung z.B. mittels XML-Datei oder so?
 
Naja, Du liest halt die Daten ein und baust das Model auf. Die erste Spalte ist offensichtlich der Pfad zum Knoten. Mit XML/JSON kannst Du die Baumstruktur direkt in der Datei abbilden, das ändert aber am Vorgehen nicht wirklich was: dann liest Du halt das XML/JSON ein und baust das Model auf 🙂

EDIT: Was für eine JTreeTable-Implementierung verwendest Du eigentlich?
 
Ich steh wohl etwas auf dem Schlauch oder ich bin zu doof 😖 was heißt »Baumstruktur direkt in der Datei abbilden«? In welcher Datei?

Die JTreeTable-Implementierung wird mittels SWING realisiert.
 
Leider kenne ich mich viel zu wenig mit Swing aus, aber in JavaFX kann man die Werte Zelle für Zelle eines TreeTableView auslesen und in eine Datei schreiben, siehe https://stackoverflow.com/questions...table-treetable-view-content-to-pdf-and-excel.

Code habe ich rauskopiert. Jede Zeile vom Baum wird durchiteriert und Zelle für Zelle geholt.

Java:
int rowIndex = treeTableView.getRow(treeRowItem);
for(int i=0;i<treeTable.getColumns();i++){
    TreeTableColumn ttc = treeTable.getColumns().get(i);
    String value = ttc.getCellData(rowIndex).toString();
}

Ich denke das wird auch für Swing gehen nur etwas komlizierter 🙂
 
Leider kenne ich mich viel zu wenig mit Swing aus, aber in JavaFX kann man die Werte Zelle für Zelle eines TreeTableView auslesen und in eine Datei schreiben, siehe https://stackoverflow.com/questions...table-treetable-view-content-to-pdf-and-excel.

Code habe ich rauskopiert. Jede Zeile vom Baum wird durchiteriert und Zelle für Zelle geholt.

Java:
int rowIndex = treeTableView.getRow(treeRowItem);
for(int i=0;i<treeTable.getColumns();i++){
    TreeTableColumn ttc = treeTable.getColumns().get(i);
    String value = ttc.getCellData(rowIndex).toString();
}

Ich denke das wird auch für Swing gehen nur etwas komlizierter 🙂
Danke für Deine Antwort. Das Exportieren der TreeTable-Daten in die CSV-Datei ist bereits implementiert und funktioniert einwandfrei mit der oben geschilderten Struktur. Mein Problem ist es, nun die Daten aus dieser erstellten CSV-Datei wieder in die TreeTable zu importieren → das bekomme ich iwie nicht hin.
 
Danke für Deine Antwort. Das Exportieren der TreeTable-Daten in die CSV-Datei ist bereits implementiert und funktioniert einwandfrei mit der oben geschilderten Struktur. Mein Problem ist es, nun die Daten aus dieser erstellten CSV-Datei wieder in die TreeTable zu importieren → das bekomme ich iwie nicht hin.
Wie bildest du die das Modell als CSV? Hast du kein pedant zu dem Einlesen?
 
was heißt »Baumstruktur direkt in der Datei abbilden«? In welcher Datei?
Mit CSV schreibst Du ja eine Tabellenstruktur, mit XML oder JSON kannst Du Hierarchien abbilden. Beispielhaft:
XML:
<laufwerk>
  <Verzeichnis1-1>
    <Verzeichnis1-2>
      <Dateiname1 />
    </Verzeichnis1-2>
  </Verzeichnis1-1>
  <Verzeichnis2-1>
  ...
</laufwerk>
Das entspricht also im Wesentlichen Deinem Baum, nur ändert das am Problem des Einlesens nichts bzw. kaum etwas.

Die JTreeTable-Implementierung wird mittels SWING realisiert.
Das ist schon klar, aber welche Implementierung hast Du verwendet? JTreeTable ist ja keine Standardkomponente.
 
Wird mit folgender Klasse bewerkstelligt:
Java:
public class JTreeTable extends JTable {
    protected static TreeTableCellRenderer tree;

    public JTreeTable(TreeTableModel treeTableModel) {
        super();

        // Create the tree. It will be used as a renderer and editor.
        tree = new TreeTableCellRenderer(treeTableModel);

        // Install a tableModel representing the visible rows in the tree.
        super.setModel(new TreeTableModelAdapter(treeTableModel, getTree()));

        // Force the JTable and JTree to share their row selection models.
        getTree().setSelectionModel(new DefaultTreeSelectionModel() {
            private static final long serialVersionUID = -2566509625314303370L;

            // Extend the implementation of the constructor, as if:
            /* public this() */ {
                setSelectionModel(listSelectionModel);
            }
        });
        // Make the tree and table row heights the same.
        getTree().setRowHeight(getRowHeight());

        // Root aus- oder einblenden
        getTree().setRootVisible(true);

        // Install the tree editor renderer and editor.
        setDefaultRenderer(TreeTableModel.class, getTree());
        setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());

        setShowGrid(true);// Tabellenlinien anzeigen
        setIntercellSpacing(new Dimension(0, 0));
    }

    /*
     * Workaround for BasicTableUI anomaly. Make sure the UI never tries to paint
     * the editor. The UI currently uses different techniques to paint the renderers
     * and editors and overriding setBounds() below is not the right thing to do for
     * an editor. Returning -1 for the editing row in this case, ensures the editor
     * is never painted.
     */
    public int getEditingRow() {
        return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 : editingRow;
    }

    public static TreeTableCellRenderer getTree() {
        return tree;
    }

    // The renderer used to display the tree nodes, a JTree.
    public class TreeTableCellRenderer extends JTree implements TableCellRenderer {
        private static final long serialVersionUID = -5593454246676923763L;
        protected int visibleRow;

        public TreeTableCellRenderer(TreeModel model) {
            super(model);
        }

        public void setBounds(int x, int y, int w, int h) {
            super.setBounds(x, 0, w, JTreeTable.this.getHeight());
        }

        public void paint(Graphics g) {
            g.translate(0, -visibleRow * getRowHeight());
            super.paint(g);
        }

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
            if (isSelected) {
                setBackground(table.getSelectionBackground());
            } else {
                setBackground(table.getBackground());
            }

            setToolTipText("Doppelklick auf eine Datei zum Öffnen");

            visibleRow = row;
            return this;
        }
    }

    // The editor used to interact with tree nodes, a JTree.
    public class TreeTableCellEditor extends AbstractCellEditor implements TableCellEditor {
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int r, int c) {
            return getTree();
        }
    }
}
 
Wie bildest du die das Modell als CSV? Hast du kein pedant zu dem Einlesen?
Das Erstellen der CSV-Datei wird mit dieser Klasse erledigt:
Java:
public class WriteCSVfile {
    public static void writeCSVfile(JTreeTable dtm, String fileName, char customSeparator)
            throws IOException, ClassNotFoundException, SQLException {
        int nRow = dtm.getRowCount();
        int nCol = dtm.getColumnCount();

        int startWithCol = 0;

        int pfadCol = 0;

        Writer writer = null;

        try {
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), PROPERTIES_CHARSET));

            // Stand bei Erstellung der Datei
            LocalDate dateNow = LocalDate.now();
            String dateFormatted = dateNow.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            writer.write(dateFormatted + SYS_LINE_SEP);

            // ausgewählter Haupt-Pfad
            writer.write(MusicGui.lblHeader.getText().replace(TITLE_TEXT + " — ", "") + SYS_LINE_SEP);

            // ausgelesene Dateien
            writer.write(MusicGui.lblAnzahlDatsGes.getText() + CUSTOM_SEPARATOR + MusicGui.lblAnzDats.getText()
                    + SYS_LINE_SEP);

            // write header information
            StringBuffer bufferHeader = new StringBuffer();
            for (int j = startWithCol; j < nCol; j++) {
                // Tabellennummer mit Pfad ermitteln
                if (dtm.getColumnName(j).equals(strPfad)) {
                    pfadCol = j;
                }

                // Wert zu Ausgabestring hinzufügen
                bufferHeader.append(dtm.getColumnName(j));
                if (j < nCol - 1) {
                    bufferHeader.append(customSeparator);
                }
            }
            writer.write(bufferHeader.toString() + SYS_LINE_SEP);

            // write row information, ohne TreeView, da keine Daten davon
            // ausgegeben werden
            for (int i = 0; i < nRow; i++) {
                StringBuffer buffer = new StringBuffer();
                for (int j = startWithCol; j < nCol; j++) {
                    buffer.append(dtm.getValueAt(i, j));
                    if (j < nCol - 1) {
                        buffer.append(customSeparator);
                    }
                }

                // Ausgabe, wenn Wert in Spalte für Pfad nicht Null
                if (dtm.getValueAt(i, pfadCol) != null) {
                    writer.write(buffer.toString() + SYS_LINE_SEP);
                }
            }
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}
 
Wie schaut TreeTableModel aus?
TreeTableModel ist ein Interface und schaut folgendermaßen aus:
Java:
public interface TreeTableModel extends TreeModel {
    /**
     * Returns the number of available column.
     */
    public int getColumnCount();

    /**
     * Returns the name for column number <code>column</code>.
     */
    public String getColumnName(int column);

    /**
     * Returns the type for column number <code>column</code>.
     */
    @SuppressWarnings("rawtypes")
    public Class getColumnClass(int column);

    /**
     * Returns the value to be displayed for node <code>node</code>, at column
     * number <code>column</code>.
     */
    public Object getValueAt(Object node, int column);

    /**
     * Indicates whether the the value for node <code>node</code>, at column number
     * <code>column</code> is editable.
     */
    public boolean isCellEditable(Object node, int column);

    /**
     * Sets the value for node <code>node</code>, at column number
     * <code>column</code>.
     */
    public void setValueAt(Object aValue, Object node, int column);
}
 
Sorry, hatte vergessen. Du brauchst eine Klasse, die Deine Dateien bzw. Verzeichnisse beschreibt. Nennen wir sie mal FileDescriptor. Diese hat ein paar Attribute (Namen und was Du sonst in der Tabelle anzeigen willst) und Kinder, die wiederum FileDescriptor-Objekte sind. Damit baust Du einen Baum auf, wobei der erste FileDescriptor Dein root ist. Den Pfad durch den Baum entnimmst Du eben der ersten Spalte.
 
In etwa so:
Java:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.TreeSet;
import java.util.stream.Collectors;
public class FileDescriptor {
           
    public enum Type {
        FILE, DIRECTORY
    };
    private static final Comparator<FileDescriptor> byName = 
            Comparator.comparing(FileDescriptor::getName);
    
    private final String name;
    private final Type type;
    private final TreeSet<FileDescriptor> children = new TreeSet<>(byName);
    private final List<String> infos = new ArrayList<>();
    FileDescriptor() {
        this("", Type.DIRECTORY);
    }
    FileDescriptor(String name, Type type) {
        this.name = name;
        this.type = type;
    }
    String getName() {
        return name;
    }
    void add(FileDescriptor child) {
        children.add(child);
    }
    void addInfo(String info) {
        infos.add(info);
    }
    Set<FileDescriptor> getChildren() {
        return Collections.unmodifiableSet(children);
    }
    FileDescriptor findChildByName(String name) {
        FileDescriptor query = new FileDescriptor(name, Type.DIRECTORY);
        FileDescriptor candidate = children.ceiling(query);
        if (candidate != null && byName.compare(candidate, query) == 0) {
            return candidate;
        }
        return null;
    }
    FileDescriptor getOrCreateFile(String[] path) {
        FileDescriptor currentDir = this;
        for (int i = 0; i < path.length; i++) {
            FileDescriptor child = currentDir.findChildByName(path[i]);
            if (child == null) {
                Type type = i < path.length-1 ? Type.DIRECTORY : Type.FILE;
                child = new FileDescriptor(path[i], type);
                currentDir.add(child);
            }
            currentDir = child;
        }
        return currentDir;
    }
    @Override
    public String toString() {
        return String.join(": ", name, infos.stream().collect(Collectors.joining("|")));
    }
}

Zum Test:
Java:
import java.io.BufferedReader;
import java.io.StringReader;
public class Test {
    static final String CONTENT = "Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis1-4/Verzeichnis1-5/Dateiname1;Info 1;Info 2;Info 3;Info 4\n"
            + "Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis1-4/Verzeichnis1-5/Dateiname2;Info 1;Info 2;Info 3;Info 4\n"
            + "Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis1-4/Verzeichnis2-5/Dateiname1;Info 1;Info 2;Info 3;Info 4\n"
            + "Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis1-4/Verzeichnis2-5/Dateiname2;Info 1;Info 2;Info 3;Info 4\n"
            + "Laufwerk/Verzeichnis1-1/Verzeichnis1-2/Verzeichnis1-3/Verzeichnis2-4/Verzeichnis1-5/Dateiname1;Info 1;Info 2;Info 3;Info 4";
    public static void main(String[] args) throws Exception {
        FileDescriptor root = new FileDescriptor();
        new BufferedReader(new StringReader(CONTENT)).lines()
            .forEach(l -> readLine(root, l));
        print(root, 0);
    }
    private static void print(FileDescriptor root, int level) {
        if (root == null) {
            return;
        }
        System.out.printf("%" + (level+1) + "s%s\n", "", root.toString());
        for (FileDescriptor child : root.getChildren()) {
            print(child, level+1);
        }
    }
    private static void readLine(FileDescriptor root, String line) {
        String[] parts = line.split(";");
        String[] path = parts[0].split("/");
        FileDescriptor d = root.getOrCreateFile(path);
        for (int i = 1; i < parts.length; i++) {
            d.addInfo(parts[i]);
        }
    }
}
 

Zurück
Oben