Algorithmen und Datenstrukturen Übung 1a: Baumansichten der Java Foundation Classes "Model-View-Controller"-Ansatz Die Oberflächentechnik "Swing" (Paket javax.swing) umfasst eine Vielzahl mächtiger Klassen, z.B. Baumansichten, Tabellen. Swing benutzt den "Model-View-Controller"Ansatz, d.h. eine konsequente Trennung der Daten und der Darstellung. Beim MVC-Ansatz unterscheidet man 3 Arten von Applikationselementen: - Model Datenmodell) Im Datenmodell werden Funktionalität und Zustand gespeichert. Es kann Anfragen von Views über seinen Zustand beantworten und Zustandsänderungen vom Controller oder anderen Objekten bearbeiten. - View (Darstellung: look) Die Ansicht weiß nichts oder nur sehr wenig (über das Datenmodell) und stellt lediglich die Daten dar - Controller (Steuerung: feel) Die Steuerung stellt ein Modell der realen welt dar und reagiert auf Benutzereingaben von Maus, Tastatur und anderen Eingabegeräten. Ein "Model" kann mehrere "Views" gleichzeitig haben. Damit Veränderungen des Modells in allen Views sichtbar werden, wird ein Benachrichtungsmechanismus implementiert, mit dem das Modell die verschiedenen Views über Änderungen informiert. Bei praktische Realisierung führt die MVC-Architektur zu Problemen bzgl. - der Entwicklung der Komponenten Meistens sind View und Controller eng verbunden, so dass es zusätzlichen Schnittstellenaufwand für die Implementierung gibt. - erhöhter Kommunikationsaufwand zwischen Objekten. Falls sich Ergebnisse in der Darstellung oder dem Modell ergeben, führt die Benachrichtigung immer über den Controller. Sinnvoll ist es daher View und Controller zu einer Komponente zu verschmelzen (Vereinfachung der komplexen Interaktion zwischen View und Controller). Das neue Modell wird anstatt MVC auch Model-View-Presenter (MVP-Pattern) genannt. In Java ist der View und Controller durch ComponentUI repräsentiert. Für alle Swing-Komponenten gibt es ein Component-Objekt, das Darstellung und Benutzeraktion übernimmt. Die Klasse JTree Die Baumdarstellung basiert auf einem hierarchischen Datenmodell. Jede Modellklasse zur Baumdarstellung muß sich auf das Interface TreeModel beziehen. JTree besitzt dafür Konstruktoren bzw. die Zugriffsmethoden getModel() und setModel(). Die einzelnen Baumknoten müssen die Interfaces TreeNode oder MutableTreeNode implementieren. Die Klasse DefaulMutableTreeNode enthält eine universelle Implementierung (inklusive Navigationsmethoden) für Baumknoten Erzeugen eines Baums. Liegt ein geeignetes Datenmodell vor, ist das Instanzieren eines JTree einfach. Die beiden wichtigsten Konstruktoren der Klasse JTree sind: public JTree(TreeModel neuesModell) // erwartet wird ein vordefiniertes TreeModel zur Darstellung der Baumelemente. Ein TreeModel // kapselt alle relevanten Informationen über die Baumstruktur. Es liefert auf Anfrage die Wurzel 1 Algorithmen und Datenstrukturen // des Baums, stellt Informationen über einen bestimmten Knoten bereit oder liefert dessen // Nachfolgeknoten public JTree(TreeNode wurzel) // Die Wurzel wird automatisch in ein geeignetes TreeModel eingebettet. Der JTree erfragt die zur // Darstellung der Navigation erforderlichen Daten immer beim TreeModel. Das über wurzel // Baumwurzel) instanzierte DefaultTreeModel kann diese Informationen aus den Knoten und den // darin gespeicherten Verweisen auf ihre Nachfolgeknoten entnehmen. Zugriffsmethoden auf das Datenmodell. public TreeModel getModel() public void setModel(TreeModel neuesModell) Anzeige bzw. Unterdrücken der Wurzel bei der Baumdarstellung. Eine wichtige Konfigurationsoption regelt, ob die Wurzel des Baums bei seiner Darstellung angezeigt oder unterdrückt werden soll. Auf sie kann mit den Methoden public void setRootVisible(boolean rootVisible) public boolean isRootVisible() zugegriffen werden. Zur Beschriftung eines Knotens bei der visuellen Darstellung verwendet ein JTree die Methode toString(). Abfragen zur Selektion von Baumknoten. JTree stellt eine Reihe von Methoden für Abfragen bereit, ob und welche Knoten selektiert sind, z.B.: public TreePath getSelectionPath() // ermittelt das selektierte Element. Bei aktivierter Mehrfachselektion liefert die Methode das erste aller // selektierten Elemente. Falls kein Knoten selektiert ist, wird null zurückgeliefert. public TreePath[] getSelectionPaths() // zurückgegeben wird ein Array mit allen selektierten Knoten public TreePath getLeadSelectionPath() // liefert das markierte Element Die angegebenen Methoden liefern Objekte der Klasse TreePath. Diese Klasse beschreibt einen Knoten im Baum über den Pfad, der von der Wurzel aus beschritten werden muß, um zu den Knoten zu gelangen. Mit public Object getLastSelectedPathComponent() der Klasse TreePath kann das letzte Element dieses Pfads bestimmt werden. Mit public Object[] getPath() der Klasse TreePath kann der vollständige Pfad ermittelt werden. An erster Stelle liegt die Wurzel des Baums, an letzter Stelle das selektierte Element. Solle ermittelt werden, ob und welche Elemente im Baum selektiert sind, können die Methoden public boolean isSelectionEmpty() public boolean isPathSelected(TreePath pfad) aufgerufen werden. Verändern der Selektion. Die Selektion kann programmgesteuert verändert werden: public void clearSelection() // Löschen der Selektion public void addSelectionPath(TreePath path) 2 Algorithmen und Datenstrukturen // Erweitern der Selektion um ein einzelnes Element public void addSelectionPaths(TreePath [] pfade) // Erweitern der Selektion um eine Menge Knoten public void setSelectionPath(TreePath path) // Selektion der als Argument übergebenen Knoten) public void setSelectionPaths(TreePath[] paths) // Selektion der als Argument übergebenen Knoten Zur unmittelbaren Reaktion auf Änderungen der Selektion durch den Anwender kann ein TreeSelectionListener instanziert werden (Registrierung mit addTreeSelectionListener() beim JTree). Bei jeder Selektionsänderung wird public void valueChanged(TreeSelectionEvent event) aufgerufen. "event" stellt public TreePath getOldLeadSelectionPath() public TreePath getNewLeadSelectionPath() zur Verfügung, um auf den vorherigen und aktuellen Selektionspfad zuzugreifen. Öffnen und Schließen der Knoten. Der Anwender kann die Knoten mit Maus- oder Tastaturkommandos öffnen oder schließen. Dadurch werden Nachfolgeknoten sichtbar oder versteckt. public boolean isExpanded(TreePath pfad) // liefert true, wenn der Knoten geöffnet ist public boolean isCollapsed() // liefert true, wenn der Knoten geschlossen ist public boolean hasBeenExpanded(TreePath path) // gibt an, ob der Knoten überhaupt schon einmal geöffnet wurde public boolean isVisible(TreePath pfad) // liefert true, wenn der Knoten sichtbar ist, d.h. all seine Elternknoten geöffnet sind public void makeVisible(TreePath pfad) // Sichtbar Machen eines Knoten public void expandPath(TreePath pfad) // Öffnen eines Knoten public void collapsePath(TreePath pfad) // Schließen eines Knoten Verändern der Baumstruktur. Es können neue Knoten eingefügt, bestehende entfernt oder vorhandene modifiziert werden. Interfaces zur Modellierung und Sicht auf einen Baum Drei Interfaces erlauben Modellierung und Sicht auf einen Baum: TreeModel, TreeSelectionModel, TreeCellRenderer. Ein viertes Interface TreeNode beschreibt, was in jedem Knoten des Baums dargestellt wird. TreeModel Dieses Interface beschreibt das einem JTree zugeordnete Datenmodell. Das TreeModel-Interface spezifiziert, wie ein Baum (über eine Datenstruktur) abgebildet wird. 3 Algorithmen und Datenstrukturen public Object getChild(Object parent, int index) public int getChildCount(Object parent) public int getIndexOfChild(Object parent,Object child) public Object getRoot() public Object isLeaf(Object node) Drei weitere Methoden public void addTreeModelListener(TreeModelListener l) public void removeTreeModelListener(TreeMoldelListener l) public void valueForPathChanged(TreePath pfad, Object neuerWert) behandeln Hinzufügen, Entfernen und die Kentnisnahme von Event-Listeners. Diese Listeners bemerken Änderungen im TreeModel nach Empfang von TreeModelEventNachrichten. Ein Objekt, das diese Methoden definiert, kann Modell für einen JTree sein. Die DefaultTreeModel-Klasse ist eine einfache Implementierung des TreeModel, das explizit TreeNode- und MutableTreeNode-Objekte benutzt. TreeNode JTree-Objekte werden aus TreeNode-Objekten gebildet (einfache Repräsentationen eines Baumknoten). Die wichtigsten Methoden von TreeNode sind: public int getChildCount() // ermittelt die Anzahl der Nachfolgeknoten. Sie werden von 0 an durchnummeriert. public TreeNode getChildAt(int childIndex) // liefert einen beliebigen Nachfolgeknoten public TreeNode getParent() // Ein Knoten kennt seinen Vaterknoten, der mit getParent() ermittelt werden kann public boolean isLeaf() // Abfrage, ob ein Knoten ein Blatt ist oder weitere Nachfolgeknoten enthält Die Klasse DefaultMutableTreeNode definiert Methoden zum Anschauen und Manipulieren von Baumknoten. Diese Klasse implementiert das Interface MutableTreeNode (Extension des Interface TreeNode). Anwendungsbezogene Informationen werden bereits dem Konstruktor übergeben: public DefaultMutableTreeNode(Object benutzerObjekt) DefaultMutableTreeNode hat Methoden zum Einfügen und Löschen von Knoten implementiert: public void add(MutableTreeNode neuesKind) // ein Kindknoten wird an das Ende der Liste der Nachfolgeknoten angefügt public void insert(MutableTreeNode neuesKind, int KindIndex) // Einfügen eines neuen Kindknoten an beliebiger Stelle public void remove(int kindIndex) // Entfernen eines beliebigen Knoten public void removeAllChildren() // Entfernen aller Kindknoten /* Zugriff auf Infornationen, die in einem benutzerObjekt gehalten werden */ public void setUserObject(Object benutzerObjekt) public Object getUserObject() // benutzerObjekt ist auch der Lieferant von Knotenbeschriftungen. Jeder Aufruf von toString() // wird an benutzerObjekt weitergeleitet 4 Algorithmen und Datenstrukturen TreeSelectionModel Diese Interface spezifiziert, wie ein Benutzer einen Pfad bestimmter Objekte auswählen kann. JTree benutzt dieses Modell zur Auswahl von Regeln. DefaultTreeSelectionModel ist eine Implementierung von TreeSelectionModel. TreeSelectionModel steuert das Selektieren von Knoten. Die Zugriffsmethoden auf TreeSelectionModel sind: public void setSelectionModel(TreeSelectionModel selectionModel) public TreeSelectionModel getSelectionModel() Standardmäßig erlaubt ein JTree das Selektieren mehrerer Knoten. Soll die Selektionsmöglichkeit auf einen Knoten beschränk werden, muß ein eigenes TreeSelectionModel an setSelectionModel() übergeben werden. Dazu kann eine Instanz der Klasse DefaultTreeSelectionModel erzeugt werden und durch Aufruf von public void setSelectionMode(int mode) und Übergabe einer der Konstanten SINGLE_TREE_SELECTION CONTIGUOUS_TREE_SELECTION DISCONTIGUOUS_TREE_SELECTION konfiguriert werden. TreeCellRenderer Dieses Interface wird zur visuellen Repräsentation von Baumknoten benutzt. Es umfasst nur eine Methode. Anwendungen 1. Ein Baum, der aus DefaultMutableTreeNodes aufgebaut ist1. import java.awt.*; import javax.swing.*; import javax.swing.tree.*; /** Example tree built out of DefaultMutableTreeNodes. * 1999 Marty Hall, http://www.apl.jhu.edu/~hall/java/ * Ueberarbeitet im Oktober 2002 Juergen S a u e r */ public class SimpleTree extends JFrame { public SimpleTree() { super("Creating a Simple JTree"); // WindowUtilities.setNativeLookAndFeel(); 1 vgl. pr56570 5 Algorithmen und Datenstrukturen /* try { UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName()); } catch(Exception e) { System.out.println("Error setting native L&F: " + e); } */ // addWindowListener(new ExitListener()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container content = getContentPane(); Object[] hierarchy = { "javax.swing", "javax.swing.border", "javax.swing.colorchooser", "javax.swing.event", "javax.swing.filechooser", new Object[] { "javax.swing.plaf", "javax.swing.plaf.basic", "javax.swing.plaf.metal", "javax.swing.plaf.multi" }, "javax.swing.table", new Object[] { "javax.swing.text", new Object[] { "javax.swing.text.html", "javax.swing.text.html.parser" }, "javax.swing.text.rtf" }, "javax.swing.tree", "javax.swing.undo" }; DefaultMutableTreeNode root = processHierarchy(hierarchy); JTree tree = new JTree(root); content.add(new JScrollPane(tree), BorderLayout.CENTER); setSize(275, 300); setVisible(true); } public static void main(String[] args) { new SimpleTree(); } /** Small routine that will make node out of the first entry * in the array, then make nodes out of subsequent entries * and make them child nodes of the first one. The process is * repeated recursively for entries that are arrays. */ private DefaultMutableTreeNode processHierarchy(Object[] hierarchy) { DefaultMutableTreeNode node = new DefaultMutableTreeNode(hierarchy[0]); DefaultMutableTreeNode child; for(int i=1; i<hierarchy.length; i++) { Object nodeSpecifier = hierarchy[i]; if (nodeSpecifier instanceof Object[]) // Knoten mit Nachfolgeknoten child = processHierarchy((Object[])nodeSpecifier); else child = new DefaultMutableTreeNode(nodeSpecifier); // Blatt node.add(child); } return(node); } } 6 Algorithmen und Datenstrukturen Swing besitzt einen Manager für Benutzerschnittstellen, der das "Look and Feel" (Java L&F) von Komponenten kontrolliert. Das Management des Look and Feel wird von der Klasse UIManager übernommen. Folgende Auswahlmöglichkeiten stehen in Abhängigkeit von der Java-Entwicklungsumgebung zur Verfügung: - das Windows-95- und Windows-NT-Look-and-Feel - das Motif-x-Windows-System-Look-and-Feel - Metal Die Auswahl des Look-and-Feel erfolgt mit der Methode getSystemLookAndFeelClassName() als Argument für die Methode setLookandFeel. Es werden unterschiedliche Ergebnisse auf unterschiedlichen Betriebssystemen erzeugt. 7 Algorithmen und Datenstrukturen Anlagen: Übersicht zur Klasse JTree und deren Modelle JComponent JTree ... protected TreeCellEditor cellEditor protected TreeCellRendrer cellRenderer … protected boolean rootVisible protected TreeSelectionModel selectionModel … protected TreeModel treeModel protected TreeModellListener treeModelListener … << Konstruktoren >> JTree() JTree(Hashtable wert) JTree(TreeModel neuesModel) JTree(TreeNode root) JTree(Vector wert) JTree(Object[] wert) … << Methoden >> … public void addSelectionPath(TreePath pfad) public void addSelectionPaths(TreePath[] pfade) public TreePath getSelectionPath() // ermittelt das ausgewählte Element public TreePath[] getSelectionPaths() public void setSelectionPath(TreePath pfad) public void setSelectionPaths(TreePath[] pfade) public TreePath getLeadSelectionPath() // liefert das markierte Element … public void setRootVisible(boolean rootVisible) public boolean isRootVisible() public TreeModel getModel() public void setModel(TreeModel neuesModel) public TreeCellRenderer getCellRenderer () public void setCellRenderer(TreeCellrenderer x) … public void setUI(TreeUI ui) public void updateUI() … public void setSelectionModel(TreeSelectionModel selectionModel) … Abb.: Die Klasse JTree 8 Algorithmen und Datenstrukturen << interface >> TreeModel public Object getRoot() public Object getChild(Object parent, int index) public boolean isLeaf(Object knoten) public void valueForPathChanged(TreePath pfad,Object neuerWert) public int getIndexOfChild(Object parent, Object child) public void addTreeModelListener(TreeModelListener l) public void removeTreeListener(TreeModelListener l) DefaultTreeModell … proteced TreeNode root protected EventListenerList listenerList << Konstruktoren >> public DefaultTreeModel() public DefaultTreeModel(TreeNode root,boolean asksAllowsChildren) << Metoden >> public void addTreeModelListener l) public void removeTreeModelListener(TreeModelListener l) … public Object getChild(Object parent, int index) public int getChildCount(Object parent) public int getIndexOfChild(Object parent,Object child) … public TreeNode[] getPathToRoot(TreeNode einKnoten) … public Object getRoot() …. public void insertInfo(MutableTreeNode neuesKind, MutableTreeNode parent,int index) public boolean isLeaf(Object knoten) public void nodeChanged(TreeNode knoten) … public void removeNodeFromParent(MutableTreeNode knoten) … public void setRoot(TreeNode root) public void valueForPathChanged(TreePath path,Object neuerWert) 9 Algorithmen und Datenstrukturen << interface >> TreeSelectionModel static int CONTIGUOUS_TREE_SELECTION static int DISCONTIGUOUS_TREE_SELECTION static int SINGLE_TREE_SELECTION public void setSelectionMode(int node) public int setSelectionPath(TreePath path) public void setSelectionPaths(TreePath [] paths) public void addSelectionPath(TreePath path) public void addSelectionPaths(TreePath [] paths) public void removeSelectionPath(TreePath path) public void removeSelectionPaths(TreePaths [] paths) public TreePath getSelectionPath() public TreePath[] getSelectionPaths() public int getSelectionCount() public boolean isPathSelected(TreePath path) public boolean isSelectionEmpty() public void clearSelection() public int [] getSelectionRows() public boolean isRowSelected(int row) public void resetRowSelection() public int getLeadSelecttionRow() public TreePath getSelectionPath() public void addPropertyChangeListener(PropertyChangeListener listener) DefaultTreeSelectionModel ... protected int leadIndex ... protected TreePath [] selection … static String SELECTION_MODE_PROPERTY protected int selectionMode … << Konstruktor >> public DefaultTreeSelectionModel() << Methoden >> public void addSelectionPath(TreePath path) public void addSelectionPaths(TreePath [] paths) public void removeSelectionPath(TreePath path) … public void addTreeSelectionListener(TreeSelectionListener x) public void removeTreeSelectionListener(TreeSelectionListener x) … public void updateLeadIndex() public void insureUniqueness public String toString() public Object clone() throws CloneNotSupportedException 10 Algorithmen und Datenstrukturen << interface >> TreeNode public Enumertion children() public boolean getAllowsChildren() public TreeNode getChildAt(int childIndex) public int getChildCount() public int getIndex(TreeNode node) public TreeNode getParent() public boolean isLeaf() << interface >> MutableTreeNode public void insert(MutableTreeNode child,int index) public void remove(int index) public void remove(MutableTreeNode node) public void removeFromParent() public void setParent(MutableTreeNode newParent) public void setUserObject(Object objekt) DefaultMutableTreeNode …. protected MutableTreeNode parent << Konstruktoren >> DefaultMutableTreeNode() DefaultMutableTreeNode(Object objekt) DefaultMutableTreeNode(Object benutzerObjekt) << Methoden >> public void add(MutableTreeNode neuesKind) public Enumeartion breadFirstEnumeration() … public Enumeration depthFirstEnumeration() … public TreeNode getChildAfter(TreeNode einKind) … public TreeNode getChildBefore(TreeNode einKind) … public int getDepth() … public int getLevel() … public TreeNode getPath() protected TreeNode[] getPathToRoot(TreeNode einKnoten, int tiefe) … public TreeNode getRoot() … public boolean isRoot() … public void removeAllChildren() … public String toString() 11 Algorithmen und Datenstrukturen << interface >> TreeCellEditor public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, Boolean expanded, boolean leaf, int row) DefaultTreeCellEditor protected Color borderSelectionColor protected booleab canEdit protected Component editingComponent protected Container editingContainer protected Icob editingIcon protected Font font protected TreePath lstPath … protected Timer timer protected JTree tree << Konstruktoren >> public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer,TreeCellEditor editor) << Methoden >> public void setBorderSelectionColor(Color newColor) public Color getBorderSelectionColor() public void setFont(Font font) public Font getFont() … 12 Algorithmen und Datenstrukturen << interface >> TreeCellRenderer public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded, bollean leaf, int row, boolean lostFocus) DefaultTreeCellRenderer protected Color backGroundSelectionColor protected Color borderSelectionColor protected Icon closedIcon protected boolean hasFocus protected Icon leafIcon protected Icon openIcon protected boolean selected protected Color textNonSelectionColor protected Color textSelectionColor << Konstruktor >> public TreeCellRenderer() << Methoden >> public void setFont(Font font) public void setLeafIcon(Icon newIcon) public void setOpenIcon(Icon newIcon) public void setTextNonSelectionColor(Color newColor) public void setTextSelectionColor(Color newColor) public void validate() 13