viernes, 7 de octubre de 2011

HOW TO: Llenar Swing Jtree con archivo XML

Hola a todos :). espero la estén pasando genial; y si no es así, almenos que no la estén pasando mal x).
Ahora pienso compartir algo que a mi parecer es muy útil (almenos en su debido momento me sirvió mucho) y es llenar un un Jtree desde un archivo xml. bueno, iniciemos entonces :D

Para empezar vamos a crear una sencilla clase que se llamará XMLTreeNode.java, dicha clase solo poseerá una propiedad Element (dicho tipo (interface que extiende de Node) representa un elemento un poco obvio no? xD en un documento HTML o XML, para nuestro caso un xml), un método getElement() que nos devuelve la propiedad, el método toString() (obviamente sobre escrito) y un método getKey(), entenderemos su uso a medida avancemos con la lectura. Quedando el código de la siguiente manera:



package CustomJtreeXML;

import org.w3c.dom.Element;

/**
 *
 * @author McCubo
 * @version 0.9
 */
public class XMLTreeNode {

    private Element element;

    public XMLTreeNode(Element element) {
        this.element = element;
    }

    @Override
    public String toString() {
        return element.getAttribute("NodeLabel");
    }

    public Element getElement() {
        return element;
    }

    public String getKey() {
        return element.getAttribute("key");
    }
}
Ahora crearemos otra clase, la cual llamaremos XMLTreeModel.java, la cual implementará la interface TreeModel, una vez hecho esto, implementamos los métodos de dicha interface y nuestras clase se deberia ver algo asi:

package CustomJtreeXML;

import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

/**
 *
 * @author McCubo
 */
public class XMLTreeModel implements TreeModel{

    public Object getRoot() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public Object getChild(Object parent, int index) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public int getChildCount(Object parent) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public boolean isLeaf(Object node) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void valueForPathChanged(TreePath path, Object newValue) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public int getIndexOfChild(Object parent, Object child) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void addTreeModelListener(TreeModelListener l) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void removeTreeModelListener(TreeModelListener l) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

}
Por el momento nada del otro mundo todo ese código, simplemente la implementación de los métodos que proporciona la interface.

Implementamos los métodos la interface (y claro esta) agregamos métodos propios para darle mayor funcionamiento a la clase, y nuestra clase debería de verse de la siguiente manera:

package CustomJtreeXML;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 *
 * @author McCubo
 */
public class XMLTreeModel implements TreeModel {

    private Document document;
    private List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();

    /**
     *
     * @param xmlDoc nombre del documento xml a leer
     */
    public void readDocument(String xmlDoc) {
        Document doc = null;
        try {
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = dbFactory.newDocumentBuilder();
            doc = docBuilder.parse(getClass().getResourceAsStream(xmlDoc));
            doc.normalize();
            this.document = doc;
            TreeModelEvent evt = new TreeModelEvent(this, new TreePath(getRoot()));
            for (TreeModelListener listener : listeners) {
                listener.treeStructureChanged(evt);
            }
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(XMLTreeMain.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SAXException ex) {
            Logger.getLogger(XMLTreeMain.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(XMLTreeMain.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


    public void addTreeModelListener(TreeModelListener listener) {
        if (!listeners.contains(listener)) {
            listeners.add(listener);
        }
    }

    public void removeTreeModelListener(TreeModelListener listener) {
        listeners.remove(listener);
    }

    public Object getChild(Object parent, int index) {
        if (parent instanceof XMLTreeNode) {
            List<Element> elements = getChildElements(((XMLTreeNode) parent).getElement());
            return new XMLTreeNode(elements.get(index));
        } else {
            return null;
        }
    }

    public int getChildCount(Object parent) {
        if (parent instanceof XMLTreeNode) {
            List<Element> elements = getChildElements(((XMLTreeNode) parent).getElement());
            return elements.size();
        }
        return 0;
    }

    public int getIndexOfChild(Object parent, Object child) {
        if (parent instanceof XMLTreeNode && child instanceof XMLTreeNode) {
            Element pElement = ((XMLTreeNode) parent).getElement();
            Element cElement = ((XMLTreeNode) child).getElement();
            if (cElement.getParentNode() != pElement) {
                return -1;
            }
            List<Element> elements = getChildElements(pElement);
            return elements.indexOf(cElement);
        }
        return -1;
    }

    public Object getRoot() {
        if (document == null) {
            return null;
        }
        List<Element> elements = getChildElements(document);
        if (elements.size() > 0) {
            return new XMLTreeNode(elements.get(0));
        } else {
            return null;
        }
    }

    public boolean isLeaf(Object node) {
        if (node instanceof XMLTreeNode) {
            Element element = ((XMLTreeNode) node).getElement();
            List<Element> elements = getChildElements(element);
            return elements.isEmpty();
        } else {
            return true;
        }
    }

    public void valueForPathChanged(TreePath path, Object newValue) {
        //not used
    }

    /**
     *
     * @param node Elemento del cual necesitamos obtener sus elementos hijos
     * @return Lista de elementos hijos del nodo especificado
     */
    private List<Element> getChildElements(Node node) {
        List<Element> elements = new ArrayList<Element>();
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {
                elements.add((Element) list.item(i));
            }
        }
        return elements;
    }
}
Por ultimo solo tenemos que crear una clase que extienda de javax.swing.Jtree. Esa clase la llamaremos XMLTree y su codigo seria algo asi:
package CustomJtreeXML;

/**
 *
 * @author McCubo
 */
public class XMLTree extends javax.swing.JTree {

    private XMLTreeModel xMLTreeModel;

    public XMLTree() {
        super();
        xMLTreeModel = new XMLTreeModel();
        setModel(xMLTreeModel);
        setEditable(false);
    }

    public XMLTreeNode getSelectedNode() {
        if (getSelectionPath() != null) {
            return (XMLTreeNode) getSelectionPath().getLastPathComponent();
        }
        return null;
    }

    /**
     *
     * @return Lista de Nodos los cuales tienen el foco
     */
    public List<XMLTreeNode> getSelectedNodes() {
        List<XMLTreeNode> selectedNodes = new ArrayList<XMLTreeNode>();
        TreePath[] selectionPaths = getSelectionPaths();
        for (TreePath treePath : selectionPaths) {
            if (treePath.getLastPathComponent() instanceof XMLTreeNode) {
                XMLTreeNode node = (XMLTreeNode) treePath.getLastPathComponent();
                selectedNodes.add(node);
            }
        }
        return selectedNodes;
    }

    public void addTreeListener(javax.swing.event.TreeSelectionListener aListener) {
        addTreeSelectionListener(aListener);
    }

    @Override
    public XMLTreeModel getModel() {
        return xMLTreeModel;
    }
}
Ahora, si usas NetBeans, compila el proyecto y agregalo(XMLTree) a la paleta de componentes y ya :D. pero como me falta como 2 horas para salir del trabajo, vamos a crear un ejemplo sencillo de como implementarlo. para esto crearemos una clase llamada XMLTreeMain.java. La cual nos quedara asi:

package CustomJtreeXML;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;

/**
 *
 * @author McCubo
 */
public class XMLTreeMain extends javax.swing.JFrame {

    private XMLTree xMLTree;
    private JButton button = new JButton("getValue");

    public XMLTreeMain() {
        super("TreeXML");
        initComponents();
        setVisible(true);
    }

    private void initComponents() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new BorderLayout());
        setSize(new Dimension(300, 250));
        xMLTree = new XMLTree();
        xMLTree.getModel().readDocument("demo.xml");
        xMLTree.addTreeListener(treeListener());
        button.addActionListener(buttonListener());
        getContentPane().add(xMLTree, BorderLayout.CENTER);
        getContentPane().add(button, BorderLayout.SOUTH);
    }

    private ActionListener buttonListener() {
        return new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                System.out.println(xMLTree.getSelectedNode().getKey());
                for (XMLTreeNode xMLTreeNode : xMLTree.getSelectedNodes()) {
                    System.out.println(xMLTreeNode.getKey());
                }
            }
        };
    }

    private TreeSelectionListener treeListener() {
        return new TreeSelectionListener() {

            public void valueChanged(TreeSelectionEvent e) {
                Object lastPathComponent = e.getPath().getLastPathComponent();
                if (lastPathComponent instanceof XMLTreeNode) {
                    XMLTreeNode selectedNode = (XMLTreeNode) lastPathComponent;
                    System.out.println("node selected = " + selectedNode.getKey());
                }
            }
        };
    }

    public static void main(String[] args) {
        new XMLTreeMain();
    }
}
ahhh y por ultimo la estructura del archivo xml (demo.xml) en mi caso es asi:
<?xml version="1.0"?>
<root NodeLabel="root" key="">

  <node NodeLabel="A" key="">
  	<node NodeLabel="AA" key="1NodeChild.A">
		<entry NodeLabel="AAA" key="1NodeChild.Aa" />
	</node>
  	<entry NodeLabel="AB" key="2NodeChild.A" />
	<entry NodeLabel="AC" key="3NodeChild.A" />
	<entry NodeLabel="AD" key="4NodeChild.A" />
  </node>
   <node NodeLabel="B" key="">
  	<entry NodeLabel="BA" key="1NodeChild.B" />
  	<entry NodeLabel="BB" key="2NodeChild.B" />
	<entry NodeLabel="BC" key="3NodeChild.B" />
	<entry NodeLabel="BD" key="4NodeChild.B" />
  </node>
  <node NodeLabel="C" key="">
  	<entry NodeLabel="CA" key="1NodeChild.C" />
  	<entry NodeLabel="CB" key="2NodeChild.C" />
	<entry NodeLabel="CC" key="3NodeChild.C" />
	<entry NodeLabel="CD" key="4NodeChild.C" />
  </node>
</root>
Bueno eso es todo por este post :D. espero al alguien en algun universo paralelo le sirva xD. solo una cuestion mas, el .xml debe estar en la misma carpeta y mismo nivel o inferior al que el TreeModel. dejo un enlace de descarga del projecto por alguna duda... sin mas que agregar.. nos vemos :-)

Enlace de Descarga 

0 comentarios:

Publicar un comentario