Clase de arbori generali din pachetul javax.swing

În pachetul javax.swing există două clase de arbori generali afișabili în interfața grafică. Una din ele, JFileChooser,  este specializată, fiind folosită pentru afișarea arborelui de fișiere și selectarea unui fișier din acest arbore. Cealaltă clasă, JTree, are ca instanțe arbori generali creați de utilizator și afișabili pe ecran.
 

Clasa JFileChooser

Clasa javax.swing.JFileChooser oferă o interfață grafică pentru navigarea în sistemul de fișiere. Instanța acestei clase poate fi pusă într-un container sau, recomandabil, într-o fereastră de dialog.

Clasa JFileChooser are un număr mare de  constructori si metode. La crearea instanțelor acestei clase se poate indica sub diverse forme directorul care va servi drept rădăcină a arborelui. În lipsă, se consideră ca rădăcina este directorul principal (home) al utilizatorului aplicației. Metodele permit, printre altele, să se deschidă o fereastră de dialog pentru selectarea fișierelor (în mod Open sau Save) și să se determine calea și numele fișierului selectat.

Când este selectat un fișier, instanța respectivă a clasei JFileChooser generează un eveniment de acțiune (ActionEvent).
 
Dintre constructorii clasei JFileChooser menționăm aici:
   public JFileChooser() - creează un JFileChooser care pornește de la directorul principal al utilizatorului (user home directory);
   public JFileChooser(String currentDirectoryPath) - creează un JFileChooser care are ca rădăcină directorul a cărui cale este dată ca argument;
   public JFileChooser(File currentDirectory) - creează un JFileChooser care are ca rădăcină directorul a cărui cale este dată ca argument sub forma de instanță a clasei File; 

Dintre metodele clasei JFileChooser menționăm aici:
   public File getSelectedFile() - întoarce calea și numele fișierului selectat;
   public void setSelectedFile(File file) - setează fișierul selectat;
   public File[] getSelectedFiles() - întoarce tabloul fișierelor selectate (când este permisă selectarea multiplă);
   public void setSelectedFiles(File[] selectedFiles) - setează fișierele selectate (când este permisă selectarea multiplă);
   public File getCurrentDirectory() - întoarce directorul curent;
   public void setCurrentDirectory(File dir) - setează directorul curent;
   public void changeToParentDirectory() - se trece la directorul părinte al celui curent;
   public void rescanCurrentDirectory() - rescanează directorul curent (actualizează informația despre conținutul acestuia);
   public void ensureFileIsVisible(File f) - dacă fișierul f este ascuns, îl face vizibil;
   public int showOpenDialog(Component parent) - deschide o fereastră de dialog cu titlul Open, care conține un JFileChooser. Fereastra conține, de asemenea două butoane: Open si Cancel. La apăsarea unuia din aceste butoane fereastra se închide, iar valoarea întoarsa este una din urmatoarele:
     JFileChooser.CANCEL_OPTION 
     JFileChooser.APPROVE_OPTION 
     JFileCHooser.ERROR_OPTION 
Ultima valoare intoarsă apare dacă s-a produs o eroare.
   public int showSaveDialog(Component parent) - similar cu metoda precedentă, dar titlul ferestrei este Save, iar cele două butoane sunt Save si Cancel;
   public void setMultiSelectionEnabled(boolean b) - setează modul de selecție: dacă argumentul este true, se setează selecția multiplă, altfel cea simplă;
   public String getName(File f) - întoarce numele fișierului f;
   public Icon getIcon(File f) - întoarce pictograma fișierului f;
   public void addActionListener(ActionListener l) - adaugă ascultătorul de evenimente de acțiune.

Exemplu: În fișierul TestJFileChooser.java este dat un exemplu de aplicație, în care se testează funcționarea unui JFileChooser. Aplicația conține o interfața grafică (clasa GUI) care prezintă trei butoane:
   Open file - deschide o fereastră de dialog pentru deschidere de fișiere;
   Save file - deschide o fereastra de dialog pentru salvare de fișiere;
   Clear - șterge aria de text de afișare a mesajelor.
Dacă se apasă pe unul din butoanele "Open file" sau "Save file" se deschide fereastra de dialog corespunzătoare, iar după ce se face selecția și se apasă unul din butoanele Open/Save sau Cancel se închide fereastra și se afișează în aria de text din interfața grafică mesajul privind selecția făcută.

Clasa JTree

Clasa javax.swing.JTree are ca instanțe componente de control care vizualizează un arbore general și permite efectuarea de operații asupra acestui arbore: selectarea unui nod, adăugarea sau eliminarea de fii ai nodului selectat etc. Arborele propriuzis este alcătuit din noduri care aparțin unor clase care implementează interfața javax.swing.tree.TreeNode.

Forma sub care arborele este afișat pe ecran este asemănătoare cu cea sub care apare arborele sistemului de fișiere într-un JFileChooser. Deosebirea este că, acum, nodurile arborelui nu mai conțin directoare sau fișiere, ci orice alte informații, depinzând de aplicație. Principalul avantaj al clasei JTree este că dă posibilitatea să se lucreze cu arborele în mod interactiv, selectând diferite noduri ale acestuia prin intermediul interfeței grafice.

Ca exemplu, în fișierul TestJTree1.java este dată o aplicație simplă cu interfață grafică, în care apare o instanță a clasei JTree, creată prin instrucțiunea
    JTree tree=new JTree();
Acest constructor creează un JTree care conține deja în mod implicit un exemplu de arbore. Aplicația vizualizează acest arbore pe ecran și afișează la partea de jos calea catre ultimul nod selectat. Se poate urmări modul în care se expandează și se colapsează nodurile arborelui și se afisează efectul selectării unui nod.
 
Dintre constructorii clasei JTree menționăm aici:
   public JTree() - creează un JTree care conține un exemplu de arbore; 
   public JTree(TreeNode root) - creează un JTree care are ca radacină nodul root, primit ca argument;
   public JTree(TreeModel newModel) - creează un JTree care conține un arbore care respectă modelul newModel.

Dintre metodele clasei JTree menționăm aici:
   public TreeModel getModel() - întoarce modelul arborelui (arborele propriu-zis, care conține datele);
   public void setModel(TreeModel newModel) - setează modelul arborelui;
   public TreePath getSelectionPath() - întoarce calea către nodul selectat;
   public void addTreeSelectionListener(TreeSelectionListener tsl) - adaugă la JTree un ascultător de evenimente de selecție;
   public void addTreeExpansionListener(TreeExpansionListener tel) - adaugă la JTree un ascultător de evenimente de expandare;
   public void addTreeWillExpandListener(TreeWillExpandListener tel) - adaugă un ascultator de evenimente care indică faptul că urmeaza o expandare;

   public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf,  int row,  boolean hasFocus) - convertește valoarea din nod (obiectul de informație atașat nodului respectiv) într-un text afișabil pe ecran. Această metodă este apelată de programul de vizualizare a arborelui. Pentru a se vizualiza nodul sub altă formă decât cea dată de metoda toString()  a obiectului value din nodul respectiv,  este necesar să definim propria noastră clasă de arbore, care extinde clasa JTree și în care să fie redefinită metoda convertValueToText.

Precizăm că instanțele clasei JTree nu conțin arborele propriu-zis, ci numai reprezentarea grafică a arborelui. Aceasta reprezentare se creează la executarea unui constructor al clasei JTree. De exemplu, dacă se execută expresia new JTree(radacina), în care rădăcina este referința la un TreeNode, atunci se construiește reprezentarea grafică a arborelui care are ca rădăcină nodul radacina. Dacă acest arbore iși modifică ulterior structura, este necesar să se construiască o altă instanță a clasei JTree, care reflectă noua structură.

Interfețele TreeNode și MutableTreeNode

Interfața javax.swing.tree.TreeNode definește specificațiile pe care trebuie să le respecte o clasă pentru a fi nod nemodificabil al unui JTree.
 
Conform acestei specificații, orice clasă care are ca instanțe noduri ale unui JTree trebuie să conțina următoarele metode:

   public int getChildCount() - întoarce numărul de fii;
   public Enumeration children() - întoarce o enumerație a fiilor nodului;
   public TreeNode getChildAt(int childIndex) - întoarce fiul cu indicele dat;
   public TreeNode getParent() - întoarce părintele (tatăl) nodului;
   public int getIndex(TreeNode node) - întoarce indicele fiului node; dacă nu există un astfel de fiu, intoarce -1;
   public boolean getAllowsChildren() - întoarce true dacă nodul admite fii;
   public boolean isLeaf() - întoarce true dacă nodul este frunză;

Interfața javax.swing.tree.MutableTreeNode extinde interfața TreeNode pentru cazul în care nodul permite să fie modificat. În consecință, ea oferă și metodele necesare pentru modificarea nodului.
 
În plus față de interfața TreeNode, interfața MutableTreeNode impune următoarele metode:
   public void insert(MutableTreeNode child, int index) - înserează noul fiu child pe poziția index;
   public void remove(int index) - elimină fiul de pe poziția index;
   public void remove(MutableTreeNode node) - elimină fiul node;
   public void setUserObject(Object object) - înlocuiește obiectul de informație din nod prin object;
   public void removeFromParent() - elimină nodul din lista fiilor părintelui acestuia;
   public void setParent(MutableTreeNode newParent) - pune nodul ca fiu al lui newParent.

Clasa DefaultMutableTreeNode

Clasa javax.swing.tree.DefaultMutableTreeNode implementează interfața MutableTreeNode și poate fi, deci, folosită pentru realizarea de noduri modificabile ale arborilor din clasa JTree. Clasa implementează toate metodele interfețelor TreeNode și MutableTreeNode și - în plus - oferă metode pentru toate tipurile cunoscute de parcurgere și traversare a arborilor. Nodul conține atât referințe catre fii, cât și către părinte. Nodul rădăcină are referința la părinte nulă. Remarcăm, deci, că această clasă poate servi pentru realizarea unei mari varietăți de arbori.
 
Clasa are următorii constructori:
   public DefaultMutableTreeNode() - creează un nod fără conținut, dar modificabil;
   public DefaultMutableTreeNode(Object userObject) - creează un nod care conține obiectul userObject, nu are fii și este modificabil;
   public DefaultMutableTreeNode(Object userObject, boolean allowsChildren) - creează un nod care conține obiectul userObject, nu are fii, iar proprietatea de a fi modificabil este dată de al doilea argument;

Clasa DefaultMutableTreeNode implementează toate metodele interfețelor TreeNode și MutableTreeNode. Dintre metodele pe care le conține în plus față de interfețele menționate, remarcăm aici:
   public void setAllowsChildren(boolean allows) - setează proprietatea nodului de a admite copii;
   public Object getUserObject() - întoarce obiectul de informație din nod;
   public void removeAllChildren() - elimină toți fiii;
   public void add(MutableTreeNode newChild) - elimină nodul newChild din lista fiilor fostului său părinte și il adaugă la sfârșitul listei fiilor  nodului pentru care este invocată metoda;
   public boolean isNodeAncestor(TreeNode anotherNode) - întoarce true dacă nodul anotherNode este un "strămoș" (ancestor)  al acestui nod;
   public boolean isNodeDescendant(DefaultMutableTreeNode anotherNode) - întoarce true dacă nodul anotherNode este un descendent al acestui nod;
   public TreeNode getSharedAncestor(DefaultMutableTreeNode aNode) - întoarce cel mai apropiat strămoș (ancestor) comun între acest nod și aNode;
   public boolean isNodeRelated(DefaultMutableTreeNode aNode) - întoarce true dacă nodul aNode se găsește în același arbore cu acest nod;
   public int getDepth() - întoarce adâncimea (înălțimea) subarborelui care are acest nod ca răcăcină;
   public int getLevel() - întoarce nivelul nodului;
   public TreeNode[] getPath() - întoarce tabloul nodurilor de pe calea de la rădăcină până la acest nod;
   public Object[] getUserObjectPath() - întoarce tabloul obiectelor de informație conținute în nodurile de pe calea de la rădacină pînă la acest nod;
   public TreeNode getRoot() - întoarce rădăcina arborelui în care se găsește acest nod;
   public boolean isRoot() - întoarce true dacă acest nod este rădăcina arborelui (are părinte null);
   public DefaultMutableTreeNode getNextNode() - întoarce nodul următor la traversarea arborelui în preordine;
   public DefaultMutableTreeNode getPreviousNode() - întoarce nodul precedent la traversarea arborelui în preordine;
   public Enumeration preorderEnumeration() - întoarce o enumerație pentru traversarea în preordine a subarborelui cu rădăcina în acest nod;
   public Enumeration postorderEnumeration() - întoarce o enumerație pentru traversarea în postordine a subarborelui cu rădăcina în acest nod;
   public Enumeration breadthFirstEnumeration() - întoarce o enumerație pentru traversarea în lățime (pe niveluri) a subarborelui cu rădăcina în acest nod;
   public boolean isNodeChild(TreeNode aNode) - întoarce true dacă nodul aNode este fiu al acestui nod;
   public TreeNode getFirstChild() - întoarce primul fiu al acestui nod;
   public TreeNode getLastChild() - întoarce ultimul fiu al acestui nod;
   public TreeNode getChildAfter(TreeNode aChild) - întoarce fiul care urmează după fiul aChild;
   public TreeNode getChildBefore(TreeNode aChild) - întoarce fiul care precede fiul aChild;
   public DefaultMutableTreeNode getFirstLeaf() - întoarce prima frunză descendentă a acestui nod;
   public DefaultMutableTreeNode getLastLeaf() - întoarce ultima frunză descendentă a acestui nod;
   public int getLeafCount() - întoarce numărul de frunze descendente ale acestui nod.

Exemplu: În fișierul TestJTree2.java este dat un exemplu de aplicație, în care se testează crearea și actualizarea unui arbore, ale cărui informații din noduri conțin șiruri de caractere. Nodurile arborelui sunt instanțe ale clasei DefaultMutableTreeNode. Reprezentarea grafică a arborelui se face printr-o instanță a clasei JTree, la construirea căreia se dă ca argument nodul rădăcină al arborelui. Interfața grafică conține la partea superioară un șir de butoane pentru adăugarea sau eliminarea unui nod și pentru traversarea arborelui în preordine, în postordine și in lățime. Partea centrală a interfeței grafice conține un panou împărțit în două (JSplitPane), care are în partea stangă reprezentarea grafică a arborelui, iar în partea dreaptă o arie de text pentru afișarea rezultatelor traversării. La partea de jos este o etichetă pentru mesaje. Inițial, arborele conține numai nodul rădăcină. Dacă se selectează un nod și se apasă pe butonul "Pune fiu", apare o fereastră de dialog, în care se cere să se introducă numele fiului. După introducerea acestuia, se pune nodului selectat anterior un nou fiu cu numele dat de utilizator. Dacă se selectează un nod și se apasă pe butonul "Elimina nod", se elimină nodul selectat. Dacă se apasă pe unul din butoanele "In preordine", "In postordine" sau "In lățime", se afișează în aria de text rezultatul traversării de tipul corespunzător. În fine, dacă se apasă butonul "Stergere text", se șterge conținutul ariei de text.

La fiecare adăugare sau eliminare de nod, se creează o noua instanță a clasei JTree și se pune în locul celei anterioare.
 
Considerăm că programul clasei TestJTree2 este ușor de urmărit, atât datorită modularizării specifice programării orientate pe obiecte, cât și datorită comentariilor pe care le conține. Vom face, deci, aici numai câteva precizări suplimentare.

Pentru crearea arborelui s-au folosit instrucțiunile
   radacina=new DefaultMutableTreeNode("Radacina arborelui");
   tree=new JTree(radacina);
S-a creat astfel un  nod, referit prin variabila radacina, apoi s-a creeat o instanța JTree folosind arborele cu această rădăcină. Dacă arborele nu ar fi fost modificabil, era necesar să se construiască mai întâi întregul arbore prin adaugarea la rădăcină a tuturor subarborilor acesteia și abia la sfârșit să se creeze clasa JTree, care conține reprezentarea grafică a arborelui.

Era posibil, de asemenea, să se creeze arborele prin instrucțiunile următoare:
   radacina=new DefaultMutableTreeNode("Radacina arborelui");
   modelArbore=new DefaultTreeModel(radacina);
   tree=new JTree(modelArbore);
Unde modelArbore este o referință la DefaultTreeModel. Aceasta ar fi permis o mai mare flexibilitate, deoarece era posibil să se asculte și evenimentele din clasa TreeModelEvent.

Pentru ascultarea evenimentelor de selecție s-a creat clasa AscultSelectie. Metoda valueChanged a acestei clase determină și memorează în variabila path calea către ultimul nod selectat.

Modificarea arborilor se face prin metodele claselor ascultătoare PuneFiu și EliminaNod, care ascultă evenimentele de acțiune generate de butoanele cu aceleași nume.

În metoda actionPerformed a clasei PuneFiu se cere utilizatorului, printr-o fereastră de dialog, să se introducă numele fiului adăugat, după care se determină nodul selectat ca fiind ultimul din calea selectată și i se pune un fiu cu conținutul dat de utilizator, după care se reconstruiește instanța clasei JTree pentru noul arbore și se pune în interfața grafică în locul celei vechi.

În metoda actionPerformed a clasei EliminaNod se determină nodul de eliminat, ca fiind ultimul nod din calea selectată, și i se aplică acestui nod metoda removeFromParent(), făcându-se astfel efectiv eliminarea lui din arbore. Dacă acest nod este rădăcina unui subarbore, se elimină întregul subarbore. După eliminare, se reconstruiește instanta clasei JTree și se pune în locul celei vechi.

Pentru traversarea arborelui în preordine, în postordine și în lățime s-a creat clasa ascultatoare Traversari, care invocă metodele de traversare corespunzătoare ale clasei DefaultMutableTreeNode. Aceste metode întorc o enumerație (un iterator) care respectă interfața java.util.Enumeration. Această enumerație este apoi folosită pentru afișarea în aria de text a succesiunii nodurilor în traversarea respectivă.

În acest exemplu s-au folosit numai o mică parte din facilitățile oferite de clasele JTree, DefaultMutableTreeModel și clasele conexe. Pentru aprofundare, recomandăm consultarea capitolului How to Use Trees din secțiunea Creating a GUI with JFC/Swing, subsecțiunea Using Swing Components a tutorialului Java (The Java Tutorial, al firmei Sun) .



© Copyright 2001 - Severin BUMBARU, Universitatea "Dunărea de Jos" din Galați