Package org.itsnat.impl.core.path

Source Code of org.itsnat.impl.core.path.DOMPathResolver

/*
  ItsNat Java Web Application Framework
  Copyright (C) 2007-2011 Jose Maria Arranz Santamaria, Spanish citizen

  This software is free software; you can redistribute it and/or modify it
  under the terms of the GNU Lesser General Public License as
  published by the Free Software Foundation; either version 3 of
  the License, or (at your option) any later version.
  This software is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details. You should have received
  a copy of the GNU Lesser General Public License along with this program.
  If not, see <http://www.gnu.org/licenses/>.
*/

package org.itsnat.impl.core.path;

import java.io.Serializable;
import org.itsnat.core.ItsNatException;
import org.itsnat.impl.core.clientdoc.ClientDocumentStfulImpl;
import org.itsnat.impl.core.doc.ItsNatStfulDocumentImpl;
import org.itsnat.impl.core.doc.ItsNatHTMLDocumentImpl;
import org.itsnat.impl.core.domutil.NamespaceUtil;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.views.AbstractView;
import org.w3c.dom.views.DocumentView;

/**
*
Los paths no considerar�n los nodos de texto que son "conflictivos", los filtramos
otros nodos si cuentan tal y como los comentarios, DocumentType etc.

MSIE filtra los nodos con espacios y finales de l�nea salvo que
los introduzcamos expl�citamente via DOM con createTextNode/appendChild etc
por eso s�lo consideramos los elementos.
http://www.w3schools.com/dom/dom_mozilla_vs_ie.asp
http://www.w3schools.com/dom/dom_mozilla_vs_ie.asp
*
Por otra parte as� evitamos el problema de que al eliminar un elemento queden los
nodos anterior y posterior juntos, este es un problema para el control remoto
pues cuando Firefox lee el DOM serializado a observar normaliza el �rbol.
De todas formas con un par de trucos conseguimos tambi�n contruir y resolver paths para
nodos de texto.
*
* @author jmarranz
*/
public abstract class DOMPathResolver implements Serializable
{
    protected ClientDocumentStfulImpl clientDoc;

    /**
     * Creates a new instance of DOMPathResolver
     */
    public DOMPathResolver(ClientDocumentStfulImpl clientDoc)
    {
        this.clientDoc = clientDoc;
    }

    public static DOMPathResolver createDOMPathResolver(ClientDocumentStfulImpl clientDoc)
    {
        ItsNatStfulDocumentImpl itsNatDoc = clientDoc.getItsNatStfulDocument();
        if (itsNatDoc instanceof ItsNatHTMLDocumentImpl) // NO debe ser nulo
            return new DOMPathResolverHTMLDoc(clientDoc);
        else
            return new DOMPathResolverOtherNSDoc(clientDoc);
    }

    public ItsNatStfulDocumentImpl getItsNatStfulDocument()
    {
        return clientDoc.getItsNatStfulDocument();
    }

    public abstract boolean isFilteredInClient(Node node); // Devuelve true si el navegador no refleja dicho nodo en el �rbol DOM

    private static String[] getArrayPathFromString(String pathStr)
    {
        if (pathStr == null) return null;

        String[] path = pathStr.split(",");
        return path;
    }

    private Node getChildNodeFromPos(Node parentNode,int pos,boolean isTextNode)
    {
        if (!parentNode.hasChildNodes()) return null;

        int currPos = 0;
        Node currNode = parentNode.getFirstChild();
        while(currNode != null)
        {
            if (!isFilteredInClient(currNode))
            {
                int type = currNode.getNodeType();
                if (currPos == pos)
                {
                    if (isTextNode || (type != Node.TEXT_NODE))
                    {
                        // Los siguientes comentarios son m�s aplicables al c�digo del cliente que al del servidor:
                        // Si isTextNode es true (buscamos un text node) y currNode no es un nodo de texto es que nos hemos pasado, probablemente el nodo de texto ha sido filtrado
                        // por la normalizacion etc (puede ocurrir en nodos texto con espacios), devolvemos currNode en este caso aunque no sea un text node porque es el m�s pr�ximo
                        // Esto tambi�n es deseable en el cliente pues aunque en el servidor haya un nodo de texto
                        // en el cliente puede que fuera filtrado (ej. BlackBerry) pero si devuelve el pr�ximo probablemente no haya problemas.
                        // Es mejor devolver el siguiente nodo del ausente nodo de texto pues este problema
                        // puede darse en el caso de buscar el elemento de referencia en un insertBefore
                        // en el caso de ausencia de nodo de texto, nos vale el siguiente (el anterior podr�a dar una inserci�n err�nea),
                        // En el caso de eliminaci�n en otro lugar se detecta que buscamos un nodo de texto pero no lo hemos encontrado (devolviendo el nodo siguiente no texto)
                        // evitando una eliminaci�n err�nea por ejemplo del elemento siguiente al nodo de texto filtrado.
                        // Si isTextNode es false y el nodo no es de texto, entonces est� correctamente encontrado
                        return currNode;
                    }
                    // Si isTextNode = false (no buscamos un nodo de texto) y es nodo de texto el encontrado,
                    // lo ignoramos y seguimos iterando hasta encontrar el primer nodo que no sea de texto, currPos ya no se aumenta por lo que
                    // seguir� siendo currPos == pos true
                }
                else if(type != Node.TEXT_NODE) // S�lo contamos nodos que no sean de texto pues los de texto est�n sujetos a filtrado etc normalmente cuando tienen s�lo espacios fines de l�nea etc y segun el navegador (habitual en MSIE por ejemplo en carga)
                        currPos++;
            }

            currNode = currNode.getNextSibling();
        }

        return null;
    }

    protected Node getChildNodeFromStrPos(Node parentNode,String posStr)
    {
        // Vemos si se especifica un atributo o nodo de texto
        if (posStr.equals("de")) // de = documentElement
        {
            Document doc = getItsNatStfulDocument().getDocument();
            return doc.getDocumentElement();
        }

        int posBracket = posStr.indexOf('[');
        if (posBracket == -1)
        {
            int pos = Integer.parseInt(posStr);
            return getChildNodeFromPos(parentNode,pos,false);
        }
        else
        {
            int pos = Integer.parseInt(posStr.substring(0,posBracket));
            // Se especifica un atributo: num[@attrName]
            // o nodo de texto: num[t]
            if (posStr.charAt(posBracket + 1) == '@') // Atributo
            {
                // En ItsNat no es usado pero el usuario podr�a usarlo a trav�s de
                // ScriptUtil.getNodeReference()
                String attrName = posStr.substring(posBracket + 2,posStr.length() - 1);
                Node child = getChildNodeFromPos(parentNode,pos,false);
                return ((Element)child).getAttributeNode(attrName); // Se devuelve un Attr
            }
            else
            {
                // Nodo de texto
                return getChildNodeFromPos(parentNode,pos,true);
            }
        }
    }

    private Node getNodeFromArrayPath(String[] arrayPath,Node topParent)
    {
        ItsNatStfulDocumentImpl itsNatDoc = getItsNatStfulDocument();
        Document doc = itsNatDoc.getDocument();
        AbstractView view = ((DocumentView)doc).getDefaultView();

        if (arrayPath.length == 1)
        {
            String firstPos = arrayPath[0];
            if (firstPos.equals("window"))
                return (Node)view;
            else if (firstPos.equals("document"))
                return doc;
            else if (firstPos.equals("doctype"))
                return doc.getDoctype();
        }

        if (topParent == null) topParent = doc;

        Node node = topParent;

        for(int i = 0; i < arrayPath.length; i++)
        {
            String posStr = arrayPath[i];
            node = getChildNodeFromStrPos(node,posStr);
            if (node == null) return null; // No seguimos buscando porque provocaremos un error, puede ser el caso de que el sub�rbol est� cacheado y los nodos existen en el cliente pero no en el servidor
        }
        return node;
    }

    public Node getNodeFromPath(String pathStr,Node topParent)
    {
        String[] path = getArrayPathFromString(pathStr);
        if (path == null) return null;

        return getNodeFromArrayPath(path,topParent);
    }

    protected String getNodeChildPosition(Node node)
    {
        // Evitamos toda la problem�tica de los comentarios bajo el documento que son filtrados
        // por unos navegadores y otros no, no usando la posici�n num�rica del nodo root, pues
        // en dicha posici�n influyen los comentarios. As� podemos soportar (parcialmente, no AJAX) incluso navegadores antiguos
        // Ya no se necesita por tanto document.childNodes salvo que accedamos a comentarios bajo document
        // pero eso es rar�simo y a d�a de hoy posiblemente no funcionar�a en alg�n navegador.
        Document doc = getItsNatStfulDocument().getDocument();
        if (node == doc.getDocumentElement())
            return "de";

        Node parentNode = node.getParentNode();
        if (parentNode == null)
            throw new ItsNatException("Unexpected error");

        int pos = 0; // la posici�n del nodo o la del nodo de texto pero ignorando los nodos de texto anteriores
        Node currNode = parentNode.getFirstChild();
        while(currNode != null)
        {
            if (!isFilteredInClient(currNode))
            {
                if (currNode == node)
                    return Integer.toString(pos)// As� admitimos que currNode pueda ser un Text Node
                if (currNode.getNodeType() != Node.TEXT_NODE)
                    pos++; // Ignoramos los nodos de texto excepto el dado como argumento (si es un Text Node)               
            }

            currNode = currNode.getNextSibling();
        }

        throw new ItsNatException("Node not found in document to calculate paths",node);
        //return "-1";
    }

    private static String getStringPathFromArray(String[] path)
    {
        StringBuilder code = new StringBuilder();
        for(int i = 0; i < path.length; i++)
        {
            if (i != 0)
                code.append( "," );
            code.append( path[i] );
        }
        return code.toString();
    }

    private static int getNodeDeep(Node node,Node topParent)
    {
        // Cuenta cuantos padres tiene hasta el padre dado incluido �ste
        int i = 0;
        while(node != topParent)
        {
            i++;
            node = node.getParentNode();
            if (node == null) return -1; // El nodo no est� bajo topParent
        }
        return i;
    }

    private String[] getNodePathArray(Node nodeLeaf,Node topParent)
    {
        // Si topParent es null devuelve un path absoluto, es decir hasta el documento como padre
        if (nodeLeaf == null) return null;

        ItsNatStfulDocumentImpl itsNatDoc = getItsNatStfulDocument();
        Document doc = itsNatDoc.getDocument();
        if (topParent == null) topParent = doc;

        if (nodeLeaf.equals(((DocumentView)doc).getDefaultView()))
            return new String[]{"window"};
        else if (nodeLeaf.equals(doc))
            return new String[]{"document"};
        else if (nodeLeaf.equals(doc.getDoctype()))
            return new String[]{"doctype"};

        if (nodeLeaf.getNodeType() == Node.ELEMENT_NODE)
        {
            Element elem = (Element)nodeLeaf;
            String locbyid = elem.getAttributeNS(NamespaceUtil.ITSNAT_NAMESPACE,"locById"); // Especialmente �til en stateless para evitar resolver los nodos padres via tree paths
            if ("true".equals(locbyid))
            {
                String id = elem.getAttribute("id"); // Si no existe devuelve cadena vac�a
                if (!id.equals(""))
                    return new String[]{"eid:" + id}// eid = element id
            }
        }

        Node node = nodeLeaf;
        if (node.getNodeType() == Node.ATTRIBUTE_NODE)
            node = ((Attr)node).getOwnerElement();

        int len = getNodeDeep(node,topParent);
        if (len < 0) return null;
        String[] path = new String[len];
        for(int i = len - 1; i >= 0; i--)
        {
            String pos = getNodeChildPosition(node);
            path[i] = pos;
            node = node.getParentNode();
        }

        path[len - 1] += getSuffix(nodeLeaf);

        return path;
    }

    private static String getSuffix(Node nodeLeaf)
    {
        int type = nodeLeaf.getNodeType();
        if (type == Node.TEXT_NODE)
            return getTextNodeSuffix();
        else if (type == Node.ATTRIBUTE_NODE)
            return "[@" + ((Attr)nodeLeaf).getName() + "]"; // La @ sobra pero es para seguir la sintaxis de XPath
        else
            return ""; // No tiene sufijo
    }

    public static String getTextNodeSuffix()
    {
        return "[t]";
    }

    public String getStringPathFromNode(Node node)
    {
        return getStringPathFromNode(node,null);
    }

    public String getStringPathFromNode(Node node,Node topParent)
    {
        if (node == null) return null;

        String[] path = getNodePathArray(node,topParent);
        if (path == null) return null;
        return getStringPathFromArray(path);
    }

    public String getRelativeStringPathFromNodeParent(Node child)
    {
        // Posici�n relativa respecto al padre
        if (child == null) return null;

        return getStringPathFromNode(child,child.getParentNode());
    }

    public static String removeTextNodeSuffix(String path)
    {
        int len = path.length();
        if (path.charAt(len - 1) != ']')
            return path; // No tiene sufijo
        path = path.substring(0,len - getTextNodeSuffix().length());
        return path;
    }

    public Node getPreviousSiblingInClientDOM(Node node)
    {
        // Devuelve nodos que existir�n en el DOM del cliente
        Node prevSibling = node;
        do
        {
            prevSibling = prevSibling.getPreviousSibling();
        }
        while((prevSibling != null) && isFilteredInClient(prevSibling));
        return prevSibling;
    }

    public Node getNextSiblingInClientDOM(Node node)
    {
        Node nextSibling = node;
        do
        {
            nextSibling = nextSibling.getNextSibling();
        }
        while((nextSibling != null) && isFilteredInClient(nextSibling));
        return nextSibling;
    }
}
TOP

Related Classes of org.itsnat.impl.core.path.DOMPathResolver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.