El API JAXP

Ahora que sabemos el aspecto que tiene un DOM internamente, estaremos mejor preparados para modificar o construir un DOM desde cero. Antes de ir a ese punto, esta secci�n presenta algunas modificaciones para el JTreeModel que nos permitan producir una versi�n m�s amigable del JTree para usarlo en un GUI.

.�Comprimir la vista del �rbol

Mostrar el DOM en forma de �rbol est� muy bien para experimentar y aprender como funciona un DOM. Pero no es la clase de imagen amigable en la que muchos usuarios quieren ver un JTree. Sin embargo, se necesitan muy pocas modificaciones en el adaptador TreeModel para convertirlo en algo m�s presentable. En esta secci�n haremos estos cambios.

Nota:

El c�digo descrito en esta secci�n est� en DomEcho03.java. El fichero sobre el que opera es slideSample01.xml.

.�Hacer Seleccionable la Operaci�n

Cuando modifiquemos el analizador, vamos a comprimir la vista del DOM, eliminando todo excepto los nodos que realmente queremos mostrar. Empezamos definiendo una variable booleana que controla si queremos o no comprimir la vista del DOM.

public class DomEcho extends JPanel
{
    static Document document; 

    boolean compress = true;

    static final int windowHeight = 460;
    ...

.�Identificar los Nodos del �rbol

El siguiente paso es identificar los nodos que queremos mostrar en el �rbol. Para hacer esto, vamos al �rea donde definimos los nombres de todos los tipos de elementos (en el array typeName), y a�adimos el c�digo en negrita de abajo:

public class DomEcho extends JPanel
{
    ...
    public static void makeFrame() {
        ...
    }

    // An array of names for DOM node-types
    static String[] typeName = {
        ...
    };
    final int ELEMENT_TYPE =   1;

    // The list of elements to display in the tree
    static String[] treeElementNames = {
        "slideshow",
        "slide",
        "title",         // For slideshow #1
        "slide-title",   // For slideshow #10
        "item",
    };
    boolean treeElement(String elementName) {
      for (int i=0; i<treeElementNames.length; i++) {
        if ( elementName.equals(treeElementNames[i]) ) return true;
      }
      return false;
    } 

Con este c�digo, configuramos una constante que podemos usar para identificar el tipo de nodo ELEMENT, declarado los nombres de los elementos que queremos en el �rbol, y creado un m�todo que dice el nombre de un elemento dato es o no un "elemento �rbol".

Nota:

El mecanismo que hemos creado aqu� depende del echo de que las estructuras de nodos como slideshow y slide nunca contienen texto, mientras que el texto normalmente aparece en contenidos de nodos como item.

.�Controlar la Visibilidad de los Nodos

El siguiente paso es modificar la funci�n childCount del AdapterNode para que s�lo cuente los nodos "tree element" -- nodos que se han designado como mostrables en el JTree. Hacemos las modificaciones en negrita de abajo para hacer esto:

public class DomEcho extends JPanel
{
    ...
    public class AdapterNode 
    { 
      ...
      public AdapterNode child(int searchIndex) {
        ...
      }

      public int childCount() {
        if (!compress) {
          // Indent this
          return domNode.getChildNodes().getLength();  
        } 
        int count = 0;
        for (int i=0; i<domNode.getChildNodes().getLength(); i++) {
           org.w3c.dom.Node node = domNode.getChildNodes().item(i); 
           if (node.getNodeType() == ELEMENT_TYPE
           && treeElement( node.getNodeName() )) 
           {
             ++count;
           }
        }
        return count;
      }
    } // AdapterNode

La �nica parte delicada de este c�digo es aseguranos de que el nodo es un nodo elemento antes de compararlo. El nodo DocType es necesario porque tiene el mismo nombre "slideshow", que el elemento slideshow.

.�Controlar el Acceso a los Hijos

Finalmente, necesitamos modificar la funci�n child del AdapterNode para devolver el n�mero de �tems en la lista de nodos mostrables, en vez del n�mero de �tems en la lista de todos los nodos. A�adimos el c�digo en negrita de abajo para hacer esto:

 
public class DomEcho extends JPanel
{
    ...
    public class AdapterNode 
    { 
      ...
      public int index(AdapterNode child) {
        ...
      }

      public AdapterNode child(int searchIndex) {
        //Note: JTree index is zero-based.
        org.w3c.dom.Node node = domNode.getChildNodes().item(searchIndex);
        if (compress) {
          // Return Nth displayable node
          int elementNodeIndex = 0;
          for (int i=0; i<domNode.getChildNodes().getLength(); i++) {
            node = domNode.getChildNodes().item(i);
            if (node.getNodeType() == ELEMENT_TYPE 
            && treeElement( node.getNodeName() )
            && elementNodeIndex++ == searchIndex) {
               break; 
            }
          }
        }
        return new AdapterNode(node); 
      } // child

  }  // AdapterNode

No hay nada especial aqu�. Es un versi�n ligeramente modificada de la misma l�gica que usamos para devolver el contador de hijos.

.�Comprobar los Resultados

Cuando compilemos y ejecutemos esta aplicaci�n sobre slideSample01.xml, y expandamos los nodos del �rbol, veremos el resultado de la figura 1. Los �nicos nodos que quedan en el �rbol son los nodos de la estructura de alto nivel.

Nota:

Todas las im�genes de esta pagina se han reducido para que quepan en ella, si las quieres ver a su tama�o natural, pulsa sobre ellas.

.�Cr�dito Extra

En la forma en que la aplicaci�n est� ahora, la informaci�n que le dice a la aplicaci�n c�mo comprimir el �rbol para mostrarlo est� codificada en duro. Aqu� tenemos algunas formas en las que podr�amos considerar extender la aplicaci�n.

Usar un Argumento de la L�nea de Comandos
Si comprimimos o no el �rbol podr�a estar determinado por un argumento de la l�nea de ocmandos, en vez de una variable booleanda codificada.

Leer las lista treeElement desde un fichero
Si leemos la lista de elementos a incluir en el �rbol desde un fichero externo, podr�amos construir una aplicaci�n totalmente gobernada por comandos.

.�Actuar sobre las Selecciones del �rbol

Ahora que el �rbol se muestra de la forma apropiada, el siguiente paso es concatenar sub�rboles bajo nodos seleccionados para mostrarlos en el htmlPane. Cuando lo tengamos, usaremos el texto concatenado para poner de vuelta la informaci�n de indentificaci�n del nodo en el JTree.

Nota:

El c�digo de est� secci�n es DomEcho04.java.

.�Identificar los Tipos de Nodos

Cuando concatenamos los sub-nodos bajo un elemento, el procesamiento que estamos haciendo depende del tipo del nodo. Por eso lo primero es definir las constantes para el resto de los tipos de nodos. A�adimos el c�digo en negrita de abajo para hacer esto.

public class DomEcho extends JPanel
{
    ...
    // An array of names for DOM node-types
    static String[] typeName = {
        ...
    };
    static final int ELEMENT_TYPE =   1;
    static final int ATTR_TYPE =      2;
    static final int TEXT_TYPE =      3;
    static final int CDATA_TYPE =     4;
    static final int ENTITYREF_TYPE = 5;
    static final int ENTITY_TYPE =    6;
    static final int PROCINSTR_TYPE = 7;
    static final int COMMENT_TYPE =   8;
    static final int DOCUMENT_TYPE =  9;
    static final int DOCTYPE_TYPE =  10;
    static final int DOCFRAG_TYPE =  11;
    static final int NOTATION_TYPE = 12; 

.�Concatenar Subnodos para Definir Contenidos de Elementos

Luego, necesitamos definir el m�todo que concatena el texto y los subnodos para un elemento y devuelve el contenido de este elemento. Para definir el m�todo content, necesitaremos a�adir el c�digo en negrita de abajo:

public class DomEcho extends JPanel
{
    ...
    public class AdapterNode 
    { 
      ...
      public String toString() {
        ...
      }

      public String content() {
        String s = "";
        org.w3c.dom.NodeList nodeList = domNode.getChildNodes();
        for (int i=0; i<nodeList.getLength(); i++) {
          org.w3c.dom.Node node = nodeList.item(i);
          int type = node.getNodeType();
          AdapterNode adpNode = new AdapterNode(node);
          if (type == ELEMENT_TYPE) {   
            if ( treeElement(node.getNodeName()) ) continue;
            s += "<" + node.getNodeName() + ">";
            s += adpNode.content();
            s += "</" + node.getNodeName() + ">";
          } else if (type == TEXT_TYPE) {
            s += node.getNodeValue();
          } else if (type == ENTITYREF_TYPE) {
            // The content is in the TEXT node under it
            s += adpNode.content();
          } else if (type == CDATA_TYPE) {          
            StringBuffer sb = new StringBuffer( node.getNodeValue() );
            for (int j=0; j<sb.length(); j++) {
              if (sb.charAt(j) == '<') {
                sb.setCharAt(j, '&');
                sb.insert(j+1, "lt;");
                j += 3;
              } else if (sb.charAt(j) == '&') {
                sb.setCharAt(j, '&');
                sb.insert(j+1, "amp;");
                j += 4;
              }
            }
            s += "<pre>" + sb + "\n</pre>";
          }
        }
        return s;
      }
      ...
    } // AdapterNode

Este no es el c�digo m�s eficiente que se pueda escribir, pero funciona y nos va bi�n para nuestros prop�sitos. En este c�digo, hemos reconocido y tratado con los siguientes tipos de datos.

Element
Para elementos con nombres como el nodo "em" de XHTML, devolvemos el contenido del nodo entre las etiquetas <em> y </em> apropiadas. Sin embargo, cuando procesamos el contenido de un elemento slideshow, por ejemplo, no incluimos las etiquetas para los elementos slide que contiene, cuando devolvemos el contenido de un nodo, nos saltamos cualquier elemento que sea propiamente mostrado en el �rbol.

Text
No hay sorpresa. Para un nodo texto, s�lo devolvemos el valor del nodo.

Entity Reference
Al contrario que los nodos CDATA, las entidades de referencia pueden contener m�ltiples subelementos. Por eso la estrategia es devolver la concatenaci�n de dichos subelementos.

CDATA
Al igual que en un nodo texto, devolvemos el valor del nodo. Sin embargo, como el texto en este caso podr�a contener �ngulos y ampersands, necesitamos convertirlos a una forma en que se muestren de la forma adecuada en el panel HTML.

Por otro lado, hay unos pocos tipos de nodos que no hemos precesado en el c�digo anterior. Merece la pena gastar un momento para examinarlos y entender porqu�.

Attribute
Estos nodos no aparecen en el DOM, pero se pueden obtener llamando a getAttributes sobre los nodos elementos

Entity
Estos nodos tampoco aparecen en el DOM. Se obtienen llamando a getEntities sobre nodos DocType

Processing Instruction
Estos nodos no contienen datos mostrables.

Comment
Nada que queremos mostrar.

Document
Este es el nodo ra�z del DOM. No hay datos que mostrar de �l.

DocType
El nodo DocType contiene la especificaci�n DTD, con o sin punteros externos. S�lo aparece bajo nodo ra�z, no tiene nada que mostrar en el �rbol.

Document Fragment
Este nodo es equivalente a un nodo documento. Es un nodo ra�z que la especificaci�n DOM entiende para contener resultados intermedios durante operaciones de cortar/pegar, por ejemplo.

Notation
Podemos ignorarlos. Estos nodos se usan para incluir datos binarios en el DOM.

.�Mostrar el Contenido en el JTree

Con la concatenaci�n de contenidos creada, s�lo nos quedan unos peque�os pasos de programaci�n. El primero es modificar toString para que use el contenido del nodo como informaci�n identificactiva. A�adimos el c�digo en negrita de abajo para hacer esto:

public class DomEcho extends JPanel
{
    ...
    public class AdapterNode 
    { 
      ...
      public String toString() {
        ...
        if (! nodeName.startsWith("#")) {
           s += ": " + nodeName;
        }
        if (compress) {
           String t = content().trim();
           int x = t.indexOf("\n");
           if (x >= 0) t = t.substring(0, x);
           s += " " + t;
           return s;
        }
        if (domNode.getNodeValue() != null) {
           ...
        }
        return s;
      } 

.�Conectar el JTree con el JEditorPane

Volviendo al constructor de la aplicaci�n, creamos el oyente de treeSelection y los usamos para conectar el JTree con el JEditorPane:

public class DomEcho extends JPanel
{
    ...
    public DomEcho()
    {
       ...

       // Build right-side view
       JEditorPane htmlPane = new JEditorPane("text/html","");
       htmlPane.setEditable(false);
       JScrollPane htmlView = new JScrollPane(htmlPane);
       htmlView.setPreferredSize( 
           new Dimension( rightWidth, windowHeight ));

       tree.addTreeSelectionListener(
         new TreeSelectionListener() {
           public void valueChanged(TreeSelectionEvent e) {
             TreePath p = e.getNewLeadSelectionPath();
             if (p != null) {
               AdapterNode adpNode = 
                  (AdapterNode) p.getLastPathComponent();
               htmlPane.setText(adpNode.content());
             }
           }
         }
       ); 

Ahora, cuando se selecciona un nodo en el JTree, su contenido es enviado al htmlPane.

Nota:

El TreeSelectionListener de este ejemplo se crea usando una adaptador de clase interna an�nimo. Si est�mos programando en la versi�n 1.1 de la plataforma Java, necesitaremos definir una clase externa para este prop�sito.

Si compilamos esta versi�n de la aplicaci�n, descubriremos inmediatament que el htmlPane necesita ser especificado como final para ser referenciado en la clase interna, por eso a�adimos la palabra clave en negrita de abajo:

public DomEcho04()
{
   ...

   // Build right-side view
   final JEditorPane htmlPane = new JEditorPane("text/html","");
   htmlPane.setEditable(false);
   JScrollPane htmlView = new JScrollPane(htmlPane);
   htmlView.setPreferredSize( 
       new Dimension( rightWidth, windowHeight ));

.�Ejecutar la Aplicaci�n

Cuando compilamos la aplicaci�n y la ejecutamos sobre slideSample10.xml, obtenemos algo parecido a la figura 2. Si expandimos el �rbol el JTree incluye ahora textos identificativos para todos los nodos que sea posible.

Seleccionar un �tem que incluye subelementos XHTML produce una salida como la de la figura 3.

Seleccionar un nodo que contiene una referencia de entidad hace que el texto de la entidad sea incluido, como se ve en la figura 4.

Finalmente, seleccionado un nodo que incluye una secci�n CDATA produce como resultado la figura 5.

.�Finalizando

Ahora entendemos mucho mejor todo lo que hay sobre la estructura de un DOM, y sabemos como adaptar un DOM para crear una pantalla amigable en un JTree. Nos ha costado un poco de codificaci�n, pero en recompensa hemos obtenido una herramienta valiosa para exponer la estructura de un DOM y una plantilla para aplicaciones GUI. En la siguiente p�gina, haremos un par de peque�as modificaciones al c�digo para convertir la aplicaci�n en un veh�culo de experimentaci�n, y luego experimentar constuyendo y manipulando un DOM.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO