|
Buscador
Secciones
Registro
¡Colabora!
Ganamos
Servicios
|
|
|
Nota:
Las clases que siguen están definidas como clases internas. Si estamos codificando para la plataforma 1.1, necesitaremos definir estas clases como clases externas. |
Definir la Clase AdapterNodeEmpezamos importanto, el tree, el event, y las clases de utilidades que vamos a necesitar para hacer este trabajo.
// For creating a TreeModel
import javax.swing.tree.*;
import javax.swing.event.*;
import java.util.*;
public class DomEcho02 extends JPanel
{
Nos movemos a la parte final del programa, y definimos un conjunto de strings para los tipos de elementos de los nodos:
...
} // makeFrame
// An array of names for DOM node-types
static final String[] typeName = {
"none",
"Element",
"Attr",
"Text",
"CDATA",
"EntityRef",
"Entity",
"ProcInstr",
"Comment",
"Document",
"DocType",
"DocFragment",
"Notation",
};
} // DomEcho
Estas son las cadenas que se mostrarán en el JTree. Las especificaciones de estos tipos de nodos puede encontrarse en los comentarios de la clase org.w3c.dom.Node
Luego, definimos la envoltura AdapterNode para los nodos DOM:
static final String[] typeName = {
...
};
public class AdapterNode
{
org.w3c.dom.Node domNode;
// Construct an Adapter node from a DOM node
public AdapterNode(org.w3c.dom.Node node) {
domNode = node;
}
// Return a string that identifies this node in the tree
// *** Refer to table at top of org.w3c.dom.Node ***
public String toString() {
String s = typeName[domNode.getNodeType()];
String nodeName = domNode.getNodeName();
if (! nodeName.startsWith("#")) {
s += ": " + nodeName;
}
if (domNode.getNodeValue() != null) {
if (s.startsWith("ProcInstr"))
s += ", ";
else
s += ": ";
// Trim the value to get rid of NL's at the front
String t = domNode.getNodeValue().trim();
int x = t.indexOf("\n");
if (x >= 0) t = t.substring(0, x);
s += t;
}
return s;
}
} // AdapterNode
} // DomEcho
Esta clase declara una variable que contiene el nodo DOM, y requiere ser especificada como un argumento del constructor. Luego define la operación toString, lo que devuelve el tipo de nodo desde el array de Strings, y luego lo añade a esta información adicional sobre el nodo, para identificarlo posteriormente.
Como podemos ver en la tabla de tipos de nodos de org.w3c.dom.Node, cada nodo tiene un tipo, un nombre y un valor, que podría o no estar vacío. En aquellos casos en que el nombre del nodo empieza con "#", este campo duplica el tipo de nodo, por eso está a punto de incluir. Esto explica las líneas que dicen.
if (! nodeName.startsWith("#")) {
s += ": " + nodeName;
}
El resto del método toString merece un par de notas. Por ejemplo, estas líneas.
if (s.startsWith("ProcInstr"))
s += ", ";
else
s += ": ";
sólo proporcionan un poco de "azucar sintáctica". El campo tipo de una Instrucción de Procesamiento termina con dos puntos (:), por eso estos código se mantienen para doblar los dos puntos.
Las otras líneas interesantes son:
String t = domNode.getNodeValue().trim();
int x = t.indexOf("\n");
if (x >= 0) t = t.substring(0, x);
s += t;
Estas línea recortan el campo valor al primer caracter de nueva línea en el campo. Si dejamos fuera estas líneas, veremos algunos caracteres muy divertidos en el JTree.
|
Nota:
Recuerda que XML estipula que todos los finales de línea están normalizados a nuevas líneas sin importar el sistema de donde vienen los datos. Esto hace la programación un poco más sencilla. |
Envolver un DomNode y devolver el string deseado son las principales funciones del AdapterNode. Pero como el adaptador TreeModel necesita responder a cuestiones como "¿Cuántos hijos tiene este nodo?", y satisfacer comandos como "Dame el número de hijos de este nodo", nos ayudaría definir unos cuantos métodos de utilidad. (El adaptador podría siempre acceder al nodo DOM y obtener esta información por sí mismo, pero de esta forma, las cosas están más encapsuladas).
Añadimos el código en negrita de abajo para devolver el índice de un hijo especificado. El hijo que corresponde a un índice dado, y el contador de nodos hijos.
public class AdapterNode
{
...
public String toString() {
...
}
public int index(AdapterNode child) {
//System.err.println("Looking for index of " + child);
int count = childCount();
for (int i=0; i<count; i++) {
AdapterNode n = this.child(i);
if (child == n) return i;
}
return -1; // Should never get here.
}
public AdapterNode child(int searchIndex) {
//Note: JTree index is zero-based.
org.w3c.dom.Node node =
domNode.getChildNodes().item(searchIndex);
return new AdapterNode(node);
}
public int childCount() {
return domNode.getChildNodes().getLength();
}
} // AdapterNode
} // DomEcho
Definir el Adaptador TreeModelAhora, por último, estamos preparados para escribir el adaptador TreeModel. Una de las cosas bonitas del modelo JTree es lo relativamente fácil que es convertir un árbol existente para mostrarlo. Una de las razones para esto es la clara separación entre la vista mostrable que usa el JTree, y la vista modificable, que usa la aplicación. El punto importante es que para satisfacer el interface TreeModel sólo necesitamos (a) proporcionar métodos para acceder e informar sobre los hijos y (b) registrar el oyente JTree apropiado, para que sepa actualizar su vista cuando el modelo cambie.
Añadimos el código en negrita de abajo para crear el adaptador TreeModel y especificar los métodos de procesamiento de hijos.
...
} // AdapterNode
// This adapter converts the current Document (a DOM) into
// a JTree model.
public class DomToTreeModelAdapter implements
javax.swing.tree.TreeModel
{
// Basic TreeModel operations
public Object getRoot() {
//System.err.println("Returning root: " +document);
return new AdapterNode(document);
}
public boolean isLeaf(Object aNode) {
// Determines whether the icon shows up to the left.
// Return true for any node with no children
AdapterNode node = (AdapterNode) aNode;
if (node.childCount() > 0) return false;
return true;
}
public int getChildCount(Object parent) {
AdapterNode node = (AdapterNode) parent;
return node.childCount();
}
public Object getChild(Object parent, int index) {
AdapterNode node = (AdapterNode) parent;
return node.child(index);
}
public int getIndexOfChild(Object parent, Object child) {
AdapterNode node = (AdapterNode) parent;
return node.index((AdapterNode) child);
}
public void valueForPathChanged(TreePath path, Object newValue) {
// Null. We won't be making changes in the GUI
// If we did, we would ensure the new value was really new
// and then fire a TreeNodesChanged event.
}
} // DomToTreeModelAdapter
} // DomEcho
En este código, el método getRoot devuelve el nodo raíz del DOM, lo envuelve como un objeto AdapterNode. A partir de aquí, todos los nodos devueltos por el adaptador serán AdapterNodes que envuelven nodos DOM. Se hara lo mismo siempre que el JTree pida los hijos de un padre dado, el número de hijos que tiene un padre, etc., al JTree le pasaremos un AdapterNode. Sabemos que, como controlamos todos lo nodos que ve el JTree, empieza con el nodo raíz.
JTree usa el método isLeaf para determinar si mostrar o no un icono de expandible/contraible a la izquierda del nodo, por eso este método devuelve true sólo si el nodo tiene hijos. En este método, vemos el forzado del objeto genérico que JTree nos envía al objeto AdapterNode que nosotros conocemos. *Nosotros* sabemos que nos está enviando un objeto adaptador, pero el interface, por ser general, define objetos, por eso tenemos que hacer el forzado.
Los siguientes tres métodos devuelven el número de hijos de un nodo dado, el hijo que vive en un índice dado, y el índice de un hijo dado, respectivamente.
El último método se invoca cuando el usuario cambia un valor almacenado en el JTree. En esta aplicación, no lo soportamos. Pero si fuera así, la aplicación tendría que hacer el cambio en el modelo oculto y luego informar a los oyentes de que el cambio ha ocurrido.
Para informar a los oyentes de que ha ocurrido un cambio, necesitaremos la habilidad de registrarlos. Esto nos trae los dos últimos métodos necesarios del interface TreeModel. Añadimos el código en negrita de abajo para definirlos.
public class DomToTreeModelAdapter ...
{
...
public void valueForPathChanged(TreePath path, Object newValue) {
...
}
private Vector listenerList = new Vector();
public void addTreeModelListener( TreeModelListener listener ) {
if ( listener != null && ! listenerList.contains( listener ) ) {
listenerList.addElement( listener );
}
}
public void removeTreeModelListener( TreeModelListener listener ) {
if ( listener != null ) {
listenerList.removeElement( listener );
}
}
} // DomToTreeModelAdapter
Como esta aplicación no hará cambios en el árbol, esto métodos no se usarán, por ahora. Sin embargo, lo haremos en el futuro, cuando los necesitemos.
|
Nota:
Este ejemplo usa Vector por eso todavía funciona en aplicaciones 1.1. Si estamos codificando en 1.2 o posteriores, podríamos usar las collections en su lugar. private LinkedList listenerList = new LinkedList(); |
Las operaciones de lista son add y remove. Para iterar sobre la lista, como en las siguientes operaciones, usamos.
Iterator it = listenerList.iterator();
while ( it.hasNext() ) {
TreeModelListener listener = (TreeModelListener)it.next();
...
}
Aquí también tenemos algunos métodos opcionales que no usaremos en esta aplicación. En este punto, hemos construido una plantilla razonable para un adaptador TreeModel.
public void removeTreeModelListener( TreeModelListener listener ) {
...
}
public void fireTreeNodesChanged( TreeModelEvent e ) {
Enumeration listeners = listenerList.elements();
while ( listeners.hasMoreElements() ) {
TreeModelListener listener =
(TreeModelListener)listeners.nextElement();
listener.treeNodesChanged( e );
}
}
public void fireTreeNodesInserted( TreeModelEvent e ) {
Enumeration listeners = listenerList.elements();
while ( listeners.hasMoreElements() ) {
TreeModelListener listener =
(TreeModelListener)listeners.nextElement();
listener.treeNodesInserted( e );
}
}
public void fireTreeNodesRemoved( TreeModelEvent e ) {
Enumeration listeners = listenerList.elements();
while ( listeners.hasMoreElements() ) {
TreeModelListener listener =
(TreeModelListener)listeners.nextElement();
listener.treeNodesRemoved( e );
}
}
public void fireTreeStructureChanged( TreeModelEvent e ) {
Enumeration listeners = listenerList.elements();
while ( listeners.hasMoreElements() ) {
TreeModelListener listener =
(TreeModelListener)listeners.nextElement();
listener.treeStructureChanged( e );
}
}
} // DomToTreeModelAdapter
|
Note:
Estos métodos se han tomado de la clase TreeModelSupport descrita en Entender el TreeModel. Esta arquitectura fue producida por Tom Santos y Steve Wilson, y es mucho más elegante que la que hemos creado aquí. |
FinalizarloEn este momento, está básicamente finalizado. Todo lo que necesitamos es saltar de nuevo al constructor y añadir el código para construir un adaptador y entregarselo al JTree como el TreeModel.
// Set up the tree JTree tree = new JTree(new DomToTreeModelAdapter());
Ahora podemos compilar y ejecutar el código sobre un fichero XML. En la siguiente página, exploraremos lo que veremos al hacer esto.
| Leer comentarios (22) | |
| Escribir comentario | |
| Puntuación: |
|
| Votar | |
| Recomendar este tutorial | |
| Estadísticas |
Copyright © 1999-2006
Programación en castellano.
Todos los derechos reservados.
Formulario de Contacto -
Datos legales -
Publicidad
Hospedaje web y servidores dedicados linux por
Ferca Network