Windows: Collections.sort und JTree wirft NPE - ist long zu klein?

nullpointerexception

#1

Hallo Freunde,

wenn ich das mit Documents (Windows-Benutzer-Ordner) mit 30 Gigabyte aufrufe, dann funktioniert alles… Wenn ich es aber für den Benutzer-Ordner aufrufe mit ca. 300 Gigabyte, dann raff ich’s nicht und kriege eine NPE… Schaut euch das mal an.

Als UserObject für DefaultMutableTreeNode die Klasse Datei:

class Datei implements Comparable<Datei>, Serializable {
    File f;
    long size;

    @Override
    public int compareTo(Datei d) {
        return Long.compare(d.size, size);
    }

    @Override
    public String toString() {
        return String.format(Locale.GERMAN, "%1$,.2f", (double) size / (1024 * 1024)) + " - " + f.getName();
    }
}

Und so setze ich den root DefaultMutableTreeNode zusammen:

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;

public class BU2 {

    public static void main(String[] args) throws IOException {
        ArrayList<DefaultMutableTreeNode> dateien = new ArrayList<>();

        JFileChooser jfc = new JFileChooser();
        jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        jfc.setMultiSelectionEnabled(true);
        while (jfc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
            File[] fs1 = jfc.getSelectedFiles();
            for (File f : fs1) {
                DefaultMutableTreeNode d = new DefaultMutableTreeNode();
                addDatei(d, f);
                dateien.add(d);
                JOptionPane.showMessageDialog(null, f + " mit " + d.getUserObject());
            }
        }

        // ...
    }

Ich kriege eine NPE bei Collections.sort in addDatei und ich raffs nicht:

    static long addDatei(DefaultMutableTreeNode parent, File file) {
        if (file != null && file.canRead()) {
            Datei d = new Datei();
            d.f = file;
            parent.setUserObject(d);

            if (file.isFile()) {
                d.size = file.length();
            }
            File[] fs = file.listFiles();
            if (fs != null && fs.length > 0) {
                for (File f : fs) {
                    DefaultMutableTreeNode c = new DefaultMutableTreeNode();
                    d.size += addDatei(c, f);
                    parent.add(c);
                }
                ArrayList<DefaultMutableTreeNode> cs1 = new ArrayList<>();
                Enumeration cs2 = parent.children();
                while (cs2.hasMoreElements()) {
                    cs1.add((DefaultMutableTreeNode) cs2.nextElement());
                }
                Collections.sort(cs1, (DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) -> ((Datei) o1.getUserObject()).compareTo((Datei) o2.getUserObject()));
                parent.removeAllChildren();
                for (int i = 0; i < cs1.size(); i++) {
                    parent.add(cs1.get(i));
                }
            }
            return d.size;
        }
        return 0;
    }

Später ist dann angedacht, ein JTree anzuzeigen:

        JFrame jf = new JFrame();
        DefaultTreeModel model = new DefaultTreeModel(dateien.get(0));
        JTree jt = new JTree(model);
        jf.add(new JScrollPane(jt));
        jf.setSize(800, 800);
        jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        jf.setVisible(true);
        jt.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    TreePath tpath = jt.getPathForLocation(e.getX(), e.getY());
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) tpath.getLastPathComponent();
                    Datei d = (Datei) node.getUserObject();
                    d.f = null;
                    node.setUserObject(null);
                    model.removeNodeFromParent(node);
                }
            }
        });

Danke. :slight_smile:


#2

Ich hab das schon… Wenn man es mit dem Benutzer-Ordner aufruft, so ist AppData dabei. :roll_eyes:

Dort herrscht Windows schwarze Magie… d. h., einige Dateien kann er nicht öffnen:

    static long addDatei(DefaultMutableTreeNode parent, File file) {
        Datei d = new Datei();
        d.f = file;
        parent.setUserObject(d);

        if (file.isFile()) {
            d.size = file.length();
        }
        File[] fs = file.listFiles();
        if (fs != null && fs.length > 0) {
            for (File f : fs) {
                if (f != null && f.canRead()) {
                    DefaultMutableTreeNode c = new DefaultMutableTreeNode();
                    d.size += addDatei(c, f);
                    parent.add(c);
                }
            }
            ArrayList<DefaultMutableTreeNode> cs1 = new ArrayList<>();
            Enumeration cs2 = parent.children();
            while (cs2.hasMoreElements()) {
                cs1.add((DefaultMutableTreeNode) cs2.nextElement());
            }
            Collections.sort(cs1, (DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) -> ((Datei) o1.getUserObject()).compareTo((Datei) o2.getUserObject()));
            parent.removeAllChildren();
            for (int i = 0; i < cs1.size(); i++) {
                parent.add(cs1.get(i));
            }
        }
        return d.size;
    }

Steht Collections.sort eigentlich dort richtig? Es kann ja auch sein das parent.children() leer iss…

Danke. :wink:


#3

Wohl eher nicht.

Das kannst du doch ganz einfach aus der Fehlermeldung entnehmen oder mal ein einfaches
System.out.println(parent.children()); aufrufen und in der Ausgabe kontrollieren.

Wenn du schon Fehlermeldungen erwähnst, kannst du sie gleich mitposten. Du hast externe Bibliotheken eingebunden, die lade ich mir jetzt nicht runter, um dann deinen Code nach Fehlermeldungen abzugrasen.

Jedenfalls deutet eine NullPointerException darauf hin, dass eine Variable nicht initialisiert wurde und darauf zugegriffen wurde. Und das hat erst mal nichts mit Wertebereichen von Variablen zu tun.
Vielleicht gibt es noch weitere Fehlermeldungen, die vorher auflaufen und in der NullPointerException enden.

Vielleicht gibt es im JTree-Tutorial im Wiki noch weitere Hinweise, die dir nutzen.


#4

Ne, Lectrone, ich bin ja noch nich so lange hier und wusste nich dass es ein byte-welt Wiki gibt. :scream:
Der Feher war einfach, dass ab

addDatei(c, f);

eine gültige Rückgabe erwartet wird diese jedoch nicht erfolgt, wenn einige Dateien nicht gelesen werden können.

Ist das neuerdings verboten? Willst mich einsperren? :scream:

Hier haste se:

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-compress -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.18</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.tukaani/xz -->
        <dependency>
            <groupId>org.tukaani</groupId>
            <artifactId>xz</artifactId>
            <version>1.8</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jasypt/jasypt -->
        <dependency>
            <groupId>org.jasypt</groupId>
            <artifactId>jasypt</artifactId>
            <version>1.9.2</version>
        </dependency>

    </dependencies>

jasypt wird für den erfragten Teil nich benötigt.

Die Anwendung funktioniert ich frickle nur noch etwas an der Usability und user-friendlness…


#5

Auf keinen Fall. :rofl: Externe Libs zu benutzen ist doch besser, als das Rad neu erfinden zu wollen.

Zum Testen kann ich trotzdem nicht kompilieren, da fehlt Einiges an Code.

Ja, das wird viel zu wenig beworben. Wenn du magst, kannst du eigene Artikel, unter Verwendung deiner Zugangsdaten vom Forum, verfassen oder andere erweitern.

Liegt vermutlich an fehlenden Rechten.


#6

Hallo Lectrone,
habe es nochmal umgeschrieben. Vorher: composition, Nachher: inheritance - obwohl dadurch ja ein Anti-Pattern verletzt ist…
Alles fußt nur noch auf UserObject :

class UserObject extends DefaultMutableTreeNode implements Comparable<UserObject> {

    public File f;
    public long size;

    public UserObject(File file, ArrayList<File> toRemove) {
        if (file == null || !file.canRead()) {
            throw new IllegalArgumentException();
        }
        this.f = file;
        addChilds(toRemove);
    }

    private void addChilds(ArrayList<File> toRemove) {
        File[] fs = f.listFiles();
        if (fs != null && fs.length > 0) {
            for (File f1 : fs) {
                if (f1 != null && f1.canRead() && !toRemove.contains(f1)) {
                    this.add(new UserObject(f1, toRemove));
                }
            }
        }
    }

    public void writeToSof(SevenZOutputFile sof) throws IOException {
        if (f != null && f.canRead()) {
            SevenZArchiveEntry sae = sof.createArchiveEntry(f, f.getPath());
            sof.putArchiveEntry(sae);
            if (f.isFile()) {
                BU2.printlnToWriter("write: " + f);
                try (InputStream i = new FileInputStream(f)) {
                    byte[] ba = new byte[10 * 1024 * 1024];
                    int len;
                    while ((len = i.read(ba)) > 0) {
                        sof.write(ba, 0, len);
                    }
                }
            }
            sof.closeArchiveEntry();
            if (!this.isLeaf()) {
                for (Object object : children) {
                    ((UserObject) object).writeToSof(sof);
                }
            }
        }
    }

    public long refreshSize() {
        if (f != null && f.canRead()) {
            size = f.length();
            if (!this.isLeaf()) {
                for (Object object : children) {
                    size += ((UserObject) object).refreshSize();
                }
            }
        } else {
            size = 0;
        }
        return size;
    }

    public void sort() {
        if (!this.isLeaf()) {
            for (Object object : children) {
                ((UserObject) object).sort();
            }
            Collections.sort(children);
        }
    }

    @Override
    public int compareTo(UserObject u) {
        return Long.compare(u.size, size);
    }

    @Override
    public String toString() {
        return String.format(Locale.GERMAN, "%1$,.2f", (double) size / (1024 * 1024)) + " - " + f.getName();
    }
}

Top oder flop? :smile:


#7

Nicht ganz top…

Gleich zu Beginn, öffentliche Instanzvariablen.
Und ein UserObject, das von DefaultMutableTreeNode erbt… Hmm. Schreib dir doch ein TreeModel. Oder füge entsprechende DefaultMutableTreeNodes in den Baum ein.

Ist eine Weile her, dass ich was mit JTree gemacht habe.

Hast du dir mal das JTree-Tutorial angesehen? Ist da was für dich dabei?