JTree aus Collection von File-Objekten erstellen

Martin240

Grünschnabel
Hallo zusammen.

Ich habe eine kleine Methode geschrieben, die aus einer Collection von File-Objekten einen JTree aufbaut. Da ich per Google nichts dazu gefunden habe musste eine eigene Methode her und da wollte ich natürlich auch wissen was ihr davon haltet. Ihr könnt die Methode gerne benutzen wenn ihr sie braucht.

Jegliche Art von Kritik ist genehm. Hier ein Anzeigebeispiel (mit JTattoo-LookAndFeel) erstellt aus einer Liste von Bildern auf meiner Platte:

c2jt.jpg


Und hier der Code:

Code:
	/**
	 * Gibt einen JTree zurück, der die übergebene Collection aus Files
	 * als Baum darstellt. Der zweite Parameter regelt die Darstellung
	 * von Dateien. Sollen diese mit im JTree angezeigt werden so ist
	 * dieser Wert auf true zu setzen. 
	
	@author Martin J.
	 *

	@param  files  Eine Collection von Files
	 *  
	                          
	@param  displayFiles  Sollen die Dateien im JTree mit angezeigt werden?
	 *  
	                          
	@return      Ein fertiger JTree
	 *  
	                          
	@see         JTree
	 */
	private static JTree createTree(Collection<File> files, Boolean displayFiles){
		
		// Wird später für die Anzeige des Baumes mit oder ohne
		// die Dateien benötigt.
		int diff = 0;
		if(!displayFiles) diff = -1;
		
		// Hier erstellen wir einmal den Root-Knoten und einen
		// Zeiger auf den aktuellen Knoten
		DefaultMutableTreeNode top = new DefaultMutableTreeNode("Root");
		DefaultMutableTreeNode now = null;
		
		// Zwei Variablen die wir später brauchen um zu bestimmen
		// ob der Knoten schon existiert und an welcher Position
		Boolean found = false;
		int pos = 1;
		
		for(File s : files) {
			
			// Um das ganze unabhängig zu machen ersetzen wir erstmal die
			// Trennsymbole der Verzeichnisse auf den Linuxstandard und dann
			// zerlegen wir den Pfad in seine Verzeichnisse und die Datei
			// selber. Außerdem setzen wir das aktuelle Element auf das obere.
			String st = s.getAbsolutePath().replaceAll("\\\\", "/");
			String[] parts = st.split("/");
			now = top;
			
			// Jetzt müssen wir das gerade zerlegte Verzeichnis in den Baum
			// eintragen.
			for(int i=0;i<parts.length+diff;i++) {
				
				// Hier filtern wir noch die doppelten Backslashes raus die
				// Java in Windows nach dem Wurzelverzeichnis zurückgibt 
				if(!parts[i].equals("")) {
					
					// Dann gehen wir alle Kinder durch und schauen ob sie den
					// Namen unseres einzutragenden Knotens tragen. Sollte das
					// der Fall sein dann merken wir uns das und speichern auch
					// noch die passende Knotennummer.
					for(int j=0;j<now.getChildCount();j++) {
						if(now.getChildAt(j).toString().equals(parts[i])) {
							found = true;
							pos = j;
						}
					}
					
					// Haben wir nichts gefunden so können wir den neuen Knoten
					// hinzufügen und springen auch gleich zu diesem.
					if(!found) {
						now.add(new DefaultMutableTreeNode(parts[i]));
						now = (DefaultMutableTreeNode) now.getLastChild();
					}
					
					// Sonst springen wir zu dem den wir eben gefunden haben
					else {
						now = (DefaultMutableTreeNode) now.getChildAt(pos);
					}
					
					// Variable zurücksetzen und weiter gehts mit dem nächsten
					// Teil des File Strings.
					found = false;
				}
			}
			
		}
		
		// Noch ein paar Einstellungen für die Darstellung und ob die Dateien
		// mit angezeigt werden.
		JTree jt = new JTree(top);
		jt.setRootVisible(false);
		jt.setShowsRootHandles(true);
		
		if(!displayFiles) {
			DefaultTreeCellRenderer dtcr = new DefaultTreeCellRenderer();
			dtcr.setLeafIcon(dtcr.getClosedIcon());
			jt.setCellRenderer(dtcr);
		}
		
		return jt;
	}
 
Howdie.

Prinzipiell ist IMHO an dieser Vorgehensweise nichts auszusetzen, hab ich schon ähnlich gelöst.
Aber:
Ich persönlich finde Path-Auseinander-Gefriemel immer etwas unschön. Daher benutze ich solche Konstrukte nur, wenn ich wirklich explizite Dateien verwalten möchte. Wenn ich deinen Screenshot richtig interpretiere, hast du eine Art Explorer geschrieben, der alle Dateien anzeigen soll.
In diesem Fall wäre die eleganteste Lösung eine Rekursion, die von den File-Roots abwärts die Kindelemente ausliest und dem entsprechenden Eltern-Knoten hinzufügt. Dies bedeutet weniger Code (du musst beispielsweise keine Knoten suchen) und höhere Performance (ohne Umwege über Collections). Allerdings sollte man bei diesem Ansatz daran denken, die Kindelemente erst dann auszulesen, wenn der Knoten geöffnet wird, sonst wartet man beim Starten ne Weile ;)

Wie gesagt, deine Lösung passt ja. Ich bin halt ein riesen Fan von Rekursionen ;)

Gruß
miffi

P.S.: Mal noch eine Frage: Sind die String-Operationen auf den Dateipfad wirklich nötig? Ich dachte immer, dass Java intern sowieso '/' als Separator benutzt.
 
Zuletzt bearbeitet:
Leider wird der Pfad in Windows mit \ getrennt. Deswegen ist das ganze etwas Bastelei geworden.

Die Idee hinter der Methode ist aber nicht ganz wie du vermutest. Ich will damit nicht ganze Laufwerke oder Ordnerstrukturen abbilden, sondern ich habe eine Liste mit Dateien die ich vorher gesammelt habe aufgrund bestimmter Kriterien (in dem Fall die Endungen) und arbeite mit dieser Menge. Da ich gerne noch eine Übersicht wollte wo die Dateien überall verstreut sind habe ich mir überlegt mit einer Funktion einen JTree zu zeichnen, der nur die Ordner zeigt, die auch die Dateien auf die die Kriterien zutreffen enthält. In meinem Programm habe ich so einen schnellen Überblick wo Dateien liegen und kann mir auch per Klick auf ein Unterelement nur die Dateien aus diesem Ordner anzeigen lassen.

Rekursion ist da glaube ich etwas kniffliger, weil du ja nicht vorher sagen kannst ob in den Ordnern eine Datei ist, auf die das Suchkriterium zutrifft.

//Edit: mir fällt grad ein, ich hätte auch die Konstante File.Seperator nehmen können. Das wäre etwas einfacher gewesen :D
 
Zuletzt bearbeitet:
Ja, File.Separator hätte sich da angeboten ;)

Und Kriterien wie File-Extensions lassen sich für eine Rekursion schon verwenden. Es gibt beispielsweise die Funktion java.io.File#listFiles(java.io.FileFilter), welche dir nur Kind-Dateien auflistet, auf welche die Kriterien deiner FileFilter-Klasse zutreffen. In einer Rekursion kannst du den Suchvorgang mit dem Erstellen und Hinzufügen von Knoten verbinden und sparst Zeit/Code. Durch dein Vorgehen musst du erst Suchen und filtern, dann für jede Datei den Baum durchsuchen und den Pfad hinzufügen.

Die Art und Weise wie du den Pfad zusammenbaust und suchst funktioniert und ist ja auch okay. So hab ich das vor einiger Zeit wie gesagt auch mal gelöst. Es geht halt effizienter und eleganter. Mit gewachsenen Strukturen wie JTrees oder JTables kann man sich eh ewig beschäftigen, man entdeckt immer wieder Neues. Du könntest beispielsweise auch TreePaths basteln und diese mit dem Baum abgleichen bzw. diese hinzufügen. Aber Rekursionen bleiben für Such-Algorithmen/Operationen einfach die schönste Lösung.

Gruß
miffi

Und als kleiner Tipp am Rande:
Wenn du for-Schleifen verwendest, um einen Kindknoten zu identifizieren, mach nach found = true; ein break; rein. Fällt bei deiner Datenmenge vermutlich nicht ins Gewicht, aber ist nicht schlecht, wenn man sich sowas angewöhnt. Falls man mal mit 10k Kindern zu tun hat oder so ;)
 
Zurück