/* ** This file is part of Filius, a network construction and simulation software. ** ** Originally created at the University of Siegen, Institute "Didactics of ** Informatics and E-Learning" by a students' project group: ** members (2006-2007): ** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding, ** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer ** supervisors: ** Stefan Freischlad (maintainer until 2009), Peer Stechert ** Project is maintained since 2010 by Christian Eibl <filius@c.fameibl.de> ** and Stefan Freischlad ** Filius is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 2 of the License, or ** (at your option) version 3. ** ** Filius is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ** PURPOSE. See the GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with Filius. If not, see <http://www.gnu.org/licenses/>. */ package filius.software.system; import filius.Main; import java.io.Serializable; import java.util.Enumeration; import java.util.LinkedList; import java.util.StringTokenizer; import javax.swing.tree.DefaultMutableTreeNode; /** * Die Klasse Dateisystem dient dazu, die Funktionalitaet des Dateisystems eines * Betriebssystems nachzubilden. Das Dateisystem ist als Baum strukturiert. * Daher werden die einzelnen Verzeichnisse und Dateien als Knoten gespeichert. * <br /> * Dateien werden als Objekte der Klasse Datei uebergeben, die als Inhalt sowohl * einfache Textdateien wie auch Base64-kodierte binaere Dateien ermoeglicht. * <br /> * Datei wird hier als generischer Begriff fuer eine Datei und ein Verzeichnis * verwendet. * * @see javax.swing.tree.DefaultMutableTree * @see filius.software.system.Datei */ public class Dateisystem implements Serializable { private static final long serialVersionUID = 1L; /** * Trennzeichen zwischen Verzeichnissen (und Datei) in einer Pfadangabe. */ public static final String FILE_SEPARATOR = "/"; /** * Das Attribut root stellt den Wurzelknoten dar. Im Betriebssystem Linux * entspricht es dem Einhaengepunkt (Mount point) "/". */ private DefaultMutableTreeNode root; /** das aktuelle Arbeitsverzeichnis */ private DefaultMutableTreeNode arbeitsVerzeichnis; /** * Diese Klasse muss fuer die persistente Speicherung einer * Filius-Projektdatei den Anforderungen einer JavaBean genuegen. Daher ist * der Paramterlose Konstruktor wichtig! */ public Dateisystem() { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (Dateisystem), constr: Dateisystem()"); root = new DefaultMutableTreeNode("root"); arbeitsVerzeichnis = root; } // print entire tree, starting from root node public void printTree() { printSubtree("",root); } private void printSubtree(String indent, DefaultMutableTreeNode tmpRoot) { DefaultMutableTreeNode node; Main.debug.print(indent+"--"); if(tmpRoot.getUserObject() instanceof Datei) { //Main.debug.println(tmpRoot.getUserObject().toString()); } else { Main.debug.println("["+tmpRoot.getUserObject().toString()+"]"); } indent=indent+" |"; for (Enumeration e = tmpRoot.children(); e.hasMoreElements();) { node = (DefaultMutableTreeNode) e.nextElement(); printSubtree(indent,node); } } /** * Diese Methode prueft, ob eine Datei (bzw. ein Verzeichnis) in einem * konkreten Verzeichnis bereits vorhanden ist. Unterverzeichnisse werden * <b> nicht </b> rekursiv durchsucht! * * @param verzeichnis * Das Verzeichnis, das auf einen Dateinamen geprueft wird (keine * Pfadangabe!). * @param dateiName * Der Name einer Datei oder eines Verzeichnisses, nach dem * gesucht wird. * @return Rueckgabewert ist, ob eine Datei oder ein Verzeichnis mit dem * Bezeichner "dateiName" im Verzeichnis "verzeichnis" vorhanden * ist. */ public boolean dateiVorhanden(DefaultMutableTreeNode verzeichnis, String dateiName) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (Dateisystem), dateiVorhanden("+verzeichnis+","+dateiName+")"); DefaultMutableTreeNode enode; if (verzeichnis == null) { return false; } else { for (Enumeration e = verzeichnis.children(); e.hasMoreElements();) { enode = (DefaultMutableTreeNode) e.nextElement(); if (enode.getUserObject().toString() .equalsIgnoreCase(dateiName)) { return true; } } } return false; } /** * Diese Methode prueft, ob eine Datei (bzw. ein Verzeichnis) in einem * konkreten Verzeichnis bereits vorhanden ist. Unterverzeichnisse werden * <b> nicht </b> rekursiv durchsucht! * * @param verzeichnis * der absolute Pfad zu dem Verzeichnis, das auf einen Dateinamen * geprueft wird. * @param dateiName * Der Name einer Datei oder eines Verzeichnisses, nach dem * gesucht wird. * @return Rueckgabewert ist, ob eine Datei oder ein Verzeichnis mit dem * Bezeichner "dateiName" im Verzeichnis "verzeichnis" vorhanden * ist. */ public boolean dateiVorhanden(String verzeichnis, String dateiName) { return dateiVorhanden(verzeichnisKnoten(verzeichnis), dateiName); } /** * Gibt einen String zurueck, der den Pfad zu des als Parameter angegebenen * Knotens darstellt (z.B. root/ordner/unterordner). * * @param node * Der Knoten im Verzeichnisbaum kann ein Verzeichnis oder eine * Datei repraesentieren * @return der Dateipfad als String. Verzeichnisse werden durch den * "File-Separator" getrennt. * * @see FILE_SEPARATOR */ public static String absoluterPfad(DefaultMutableTreeNode node) { Main.debug.println("INVOKED (static) filius.software.system.Dateisystem, absoluterPfad("+node+")"); StringBuffer pfad; Object[] pfadObjekte; pfadObjekte = node.getUserObjectPath(); pfad = new StringBuffer(); pfad.append(pfadObjekte[0].toString()); for (int i = 1; i < pfadObjekte.length; i++) { pfad.append(FILE_SEPARATOR + pfadObjekte[i].toString()); } //Main.debug.println("\tpfad='"+stripRoot(pfad.toString())+"'"); return stripRoot(pfad.toString()); } public String holeRootPfad() { return absoluterPfad(root); } /** * Gibt die Knoten des Verzeichnisbaums zurueck, der unter dem angegeben * Pfad zu finden ist * * @param pfad * der absolute (!) Verzeichnis- oder Dateipfad als String * @return der Knoten im Verzeichnisbaum, der durch durch den uebergebenen * Pfad bezeichnet wird * @see absoluterPfad(DefaultMutableTreeNode) */ public DefaultMutableTreeNode verzeichnisKnoten(String pfad) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", verzeichnisKnoten("+pfad+")"); pfad=stripRoot(evaluatePathString(pfad)); if(pfad.equals(FILE_SEPARATOR) || pfad.isEmpty()) { return root; } Enumeration enumeration; DefaultMutableTreeNode node; enumeration = root.preorderEnumeration(); while (enumeration.hasMoreElements()) { node = (DefaultMutableTreeNode) enumeration.nextElement(); //Main.debug.println("DEBUG: verzeichnisKnoten:\n\t'"+pfad+"' =?= '"+absoluterPfad(node)+"'"); if (pfad.equalsIgnoreCase(absoluterPfad(node))) { return node; } } return null; } /** * Gibt die Knoten des Verzeichnisbaums zurueck, der unter dem angegeben * Pfad zu finden ist * * @param verzeichnis Verzeichnisknoten, in dem nach einem Kinderknoten * gesucht werden soll * @param pfad * der absolute Verzeichnis- oder Dateipfad als String * @return der Knoten im Verzeichnisbaum, der durch durch den uebergebenen * Pfad bezeichnet wird * @see absoluterPfad(DefaultMutableTreeNode) */ public static DefaultMutableTreeNode verzeichnisKnoten(DefaultMutableTreeNode verzeichnis, String pfad) { Main.debug.println("INVOKED (static) filius.software.system.Dateisystem, verzeichnisKnoten("+verzeichnis+","+pfad+")"); Enumeration enumeration; DefaultMutableTreeNode node; String absolutePath; if(pfad.length()>0 && pfad.substring(0,1).equals(FILE_SEPARATOR)) { // 'pfad' is absolute path! absolutePath = evaluatePathString(pfad); } else { absolutePath = evaluatePathString(absoluterPfad(verzeichnis)+FILE_SEPARATOR+pfad); } enumeration = verzeichnis.preorderEnumeration(); while (enumeration.hasMoreElements()) { node = (DefaultMutableTreeNode) enumeration.nextElement(); if (absolutePath.equalsIgnoreCase(absoluterPfad(node))) { return node; } } return null; } /** * delete file from filesystem * */ public boolean deleteFile(String absolutePath) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (Dateisystem), deleteFile("+absolutePath+")"); DefaultMutableTreeNode node = verzeichnisKnoten(absolutePath); if (node != null) { node.removeFromParent(); return true; } else { return false; } } /** * Mit dieser Methode wird eine Referenz auf eine Datei geholt. * * @param dateiPfad * der absolute Pfad zu der Datei * @return die Datei wenn vorhanden, sonst "null" */ public Datei holeDatei(String dateiPfad) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (Dateisystem), holeDatei("+dateiPfad+")"); DefaultMutableTreeNode node; node = verzeichnisKnoten(dateiPfad); if (node != null && (node.getUserObject() instanceof Datei)) { // Main.debug.println("DEBUG ("+this.hashCode()+") "+getClass()+", holeDatei: return='"+(Datei) node.getUserObject()+"'"); return (Datei) node.getUserObject(); } else { // Main.debug.println("DEBUG ("+this.hashCode()+") "+getClass()+", holeDatei: return=<null>"); return null; } } /** * Mit dieser Methode wird eine Referenz auf eine Datei geholt. * * @param verzeichnis * ein Oberverzeichnis der zu holenden Datei * @param dateiPfad * der relative Pfad der Datei im Bezug zu verzeichnis * @return die Datei wenn vorhanden, sonst "null" */ public Datei holeDatei(DefaultMutableTreeNode verzeichnis, String dateiPfad) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (Dateisystem), holeDatei("+verzeichnis+","+dateiPfad+")"); String absoluterDateiPfad; absoluterDateiPfad = absoluterPfad(verzeichnis) + FILE_SEPARATOR + dateiPfad; return holeDatei(absoluterDateiPfad); } /** * Mit dieser Methode wird eine Referenz auf eine Datei geholt. * * @param verzeichnisPfad * der absolute Pfad zu einem Oberverzeichnis der zu holenden * Datei * @param dateiPfad * der relative Pfad der Datei im Bezug zu verzeichnisPfad * @return die Datei wenn vorhanden, sonst "null" */ public Datei holeDatei(String verzeichnisPfad, String dateiPfad) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (Dateisystem), holeDatei("+verzeichnisPfad+","+dateiPfad+")"); String absoluterDateiPfad; absoluterDateiPfad = verzeichnisPfad + FILE_SEPARATOR + dateiPfad; return holeDatei(absoluterDateiPfad); } /** * Sucht in einem Verzeichnis nach Dateien die dem Suchbegriff aehneln. * * * @param suchVerzeichnis * Verzeichni, in dem nach Dateien gesucht wird, die dem * Suchstring entsprechen. * @param suchString * ein String nach dem in Dateinnamen gesucht wird * @return eine Liste von Dateien, in deren Dateiname der Suchstring * enthalten ist */ public LinkedList<Datei> dateiSuche(String suchVerzeichnis, String suchString) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+" (Dateisystem), dateiSuche("+suchVerzeichnis+","+suchString+")"); LinkedList<Datei> dateien = new LinkedList<Datei>(); DefaultMutableTreeNode verzeichnisNode, node; Datei tmpDatei; verzeichnisNode = verzeichnisKnoten(suchVerzeichnis); for (Enumeration e = verzeichnisNode.children(); e.hasMoreElements();) { node = (DefaultMutableTreeNode) e.nextElement(); if (node.getUserObject() instanceof Datei) { tmpDatei = (Datei) node.getUserObject(); if (tmpDatei.getName().toLowerCase().matches( "(.+)?" + suchString.toLowerCase() + "(.+)?")) { dateien.addLast(tmpDatei); } } } return dateien; } /** * Methode zum speichern einer Datei. Existiert die Datei in dem angegebenen * Verzeichnis bereits, wird sie ueberschrieben! Existiert der Knoten im * Verzeichnisbaum noch nicht, wird er angelegt. * * @param verzeichnisPfad * absoluter Pfad des Verzeichnisses, in dem die Datei * gespeichert werden soll * @param datei * der Dateiname der zu speichernden Datei * @return ob das Speichern erfolgreich war */ public boolean speicherDatei(String verzeichnisPfad, Datei datei) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", speicherDatei("+verzeichnisPfad+","+datei+")"); DefaultMutableTreeNode node = null; node = verzeichnisKnoten(verzeichnisPfad); if (node != null) { if (!dateiVorhanden(node, datei.getName())) { DefaultMutableTreeNode dateiNode = new DefaultMutableTreeNode(datei); node.add(dateiNode); } else { node = verzeichnisKnoten(verzeichnisPfad + FILE_SEPARATOR + datei.getName()); node.setUserObject(datei); } return true; } else { Main.debug.println("ERROR ("+this.hashCode()+"): Datei " + datei + " konnte nicht gespeichert werden, " + "weil Verzeichnis " + verzeichnisPfad + " nicht existiert."); return false; } } /** * Methode zum speichern einer Datei. Existiert die Datei in dem angegebenen * Verzeichnis bereits, wird sie ueberschrieben! Existiert der Knoten im * Verzeichnisbaum noch nicht, wird er angelegt. <br /> * Diese Methode verwendet speicherDatei(String, String). * * @param verzeichnis * Verzeichnis, in dem die Datei gespeichert werden soll * @param datei * der Dateiname der zu speichernden Datei * @return ob das Speichern erfolgreich war */ public boolean speicherDatei(DefaultMutableTreeNode verzeichnis, Datei datei) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", speicherDatei("+verzeichnis+","+datei+")"); return speicherDatei(absoluterPfad(verzeichnis), datei); } /** * Diese Methode legt ein neues Verzeichnis an. Wenn das Verzeichnis bereits * existiert, passiert nichts. * * @param verzeichnisPfad * absoluter Pfad zu dem Verzeichnis, in dem das neue Verzeichnis * erstellt werden soll * @param neuesVerzeichnis * der Name des neu zu erstellenden Verzeichnisses * @return ob das Verzeichnis nach Ausfuehrung dieser Methode existiert. D. * h. wenn es bereits vorher vorhanden war, wird auch "true" zurueck * gegeben. */ public boolean erstelleVerzeichnis(String verzeichnisPfad, String neuesVerzeichnis) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", erstelleVerzeichnis("+verzeichnisPfad+","+neuesVerzeichnis+")"); DefaultMutableTreeNode node; DefaultMutableTreeNode neuerNode = null; String absPath; if(neuesVerzeichnis.length()>0 && neuesVerzeichnis.substring(0,1).equals(FILE_SEPARATOR)) { // 'pfad' is absolute path! absPath = evaluatePathString(neuesVerzeichnis); } else { absPath = evaluatePathString(verzeichnisPfad+FILE_SEPARATOR+neuesVerzeichnis); } verzeichnisPfad = getDirectory(absPath); neuesVerzeichnis = getBasename(absPath); node = verzeichnisKnoten(verzeichnisPfad); if (node != null) { if (dateiVorhanden(node, neuesVerzeichnis)) { Main.debug.println("WARNING ("+this.hashCode()+"): Verzeichnis " + neuesVerzeichnis + " wurde nicht erzeugt, " + "weil es im Verzeichnis " + verzeichnisPfad + " bereits existiert."); } else { neuerNode = new DefaultMutableTreeNode(neuesVerzeichnis); node.add(neuerNode); // Main.debug.println("DEBUG ("+this.hashCode()+"): Verzeichnis " // + neuesVerzeichnis + " wurde erstellt."); } return true; } else { return false; } } /** * Diese Methode legt ein neues Verzeichnis an. Wenn das Verzeichnis bereits * existiert, passiert nichts. * * @param verzeichnis * Verzeichnis, in dem das neue Verzeichnis erstellt werden soll * @param neuesVerzeichnis * der Name des neu zu erstellenden Verzeichnisses * @return ob das Verzeichnis nach Ausfuehrung dieser Methode existiert. D. * h. wenn es bereits vorher vorhanden war, wird auch "true" zurueck * gegeben. */ public boolean erstelleVerzeichnis(DefaultMutableTreeNode verzeichnis, String neuesVerzeichnis) { return erstelleVerzeichnis(absoluterPfad(verzeichnis), neuesVerzeichnis); } /** * Methode zum abrufen einer Liste aller Dateien in einem Verzeichnis. * * @param node * das Verzeichnis, in dem die Dateien gespeichert sind * @return eine Liste der Dateien */ public LinkedList<Datei> holeDateien(DefaultMutableTreeNode node) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", holeDateien("+node+")"); LinkedList<Datei> liste = new LinkedList<Datei>(); if (node == null) { return null; } else { for (Enumeration e = node.children(); e.hasMoreElements();) { DefaultMutableTreeNode n = (DefaultMutableTreeNode) e .nextElement(); if (n.getUserObject() instanceof Datei) { Datei dat = (Datei) n.getUserObject(); liste.add(dat); } } return liste; } } /** * Methode, um den Inhalt eines Verzeichnisses zu erhalten. * * @param verzeichnis * das Verzeichnis, dessen Inhalt zurueckgegeben werden soll * @return wenn das Verzeichnis existiert, wird eine Liste mit allen * Objekten der Kinderknoten zurueckgeliefert. Die Liste enthaelt * Datei-Objekte fuer Dateien und Strings fuer Verzeichnisse. Wenn * das Verzeichnis nicht existiert wird null zurueckgeliefert. */ public LinkedList<Object> listeVerzeichnis(DefaultMutableTreeNode verzeichnis) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", listeVerzeichnis("+verzeichnis+")"); LinkedList<Object> liste = new LinkedList<Object>(); Enumeration enumeration; DefaultMutableTreeNode tmpNode; if (verzeichnis == null) { return null; } else { enumeration = verzeichnis.children(); while (enumeration.hasMoreElements()) { tmpNode = (DefaultMutableTreeNode) enumeration.nextElement(); liste.addLast(tmpNode.getUserObject()); } return liste; } } public DefaultMutableTreeNode getRoot() { return root; } public void setRoot(DefaultMutableTreeNode root) { this.root = root; } public DefaultMutableTreeNode getArbeitsVerzeichnis() { return arbeitsVerzeichnis; } public void setArbeitsVerzeichnis(DefaultMutableTreeNode arbeitsVerzeichnis) { this.arbeitsVerzeichnis = arbeitsVerzeichnis; } // change current working directory public DefaultMutableTreeNode changeDirectory(String absPath) { return verzeichnisKnoten(absPath); } public DefaultMutableTreeNode changeDirectory(String currDir,String relPath) { return verzeichnisKnoten(currDir+Dateisystem.FILE_SEPARATOR+relPath); } /** * bunch of path calculation and assignment functions for nodes in the tree */ // evaluate '.' and '..' as special directories public static String evaluatePathString(String path) { Main.debug.println("INVOKED (static) filius.software.system.Dateisystem, evaluatePathString("+path+")"); String result=""; StringTokenizer tk = new StringTokenizer(path, Dateisystem.FILE_SEPARATOR); String[] pathElements = new String[tk.countTokens()]; int currIndex=-1; String currString; while(tk.hasMoreTokens()) { currString = tk.nextToken(); if (currString.equals(".")) {} // current directory is referenced again by "."; ignore this path element else if (currString.equals("..")) { currIndex--; } else if (!currString.equals("")) { currIndex++; pathElements[currIndex] = currString; } } for (int i=0; i<=currIndex; i++) { // NOTE: if currIndex<0, e.g. because of multiple '..' elements, then empty path will be returned! result+=pathElements[i]; if(i<currIndex) result+=Dateisystem.FILE_SEPARATOR; } if(currIndex>=0 && path.substring(0,1).equals(FILE_SEPARATOR)) result=FILE_SEPARATOR+result; // add leading slash if it was present before //Main.debug.println(" \tevaluatePathString, result="+result); return result; } // strip root node denomination, e.g. 'root', from actual (absolute!) path String representation private static String stripRoot(String path) { Main.debug.println("INVOKED (static) filius.software.system.Dateisystem, stripRoot("+path+")"); if(path.indexOf(Dateisystem.FILE_SEPARATOR)>=0) return path.substring(path.indexOf(Dateisystem.FILE_SEPARATOR)); else return "/"; } // add root node denomination again to path representation (if not existing already) private String addRoot(String path) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", addRoot("+path+")"); if(path.substring(0,1).equals(Dateisystem.FILE_SEPARATOR)) { return root.toString()+path; } else return path; } private DefaultMutableTreeNode pathToNode(String path) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", pathToNode("+path+")"); return null; } private String nodeToPath(DefaultMutableTreeNode node) { Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", nodeToPath("+node+")"); return ""; } // get directory part in absolute file pathname public static String getDirectory (String path) { Main.debug.println("INVOKED (static) filius.software.system.Dateisystem, getDirectory("+path+")"); if(path.lastIndexOf(Dateisystem.FILE_SEPARATOR) >= 0) return path.substring(0, path.lastIndexOf(Dateisystem.FILE_SEPARATOR)); else return ""; } // get filename part in absolute file pathname public static String getBasename (String path) { Main.debug.println("INVOKED (static) filius.software.system.Dateisystem, getBasename("+path+")"); if(path.lastIndexOf(Dateisystem.FILE_SEPARATOR) >= 0) return path.substring(path.lastIndexOf(Dateisystem.FILE_SEPARATOR)+1); else return path; } }