Package org.itsnat.impl.core.domimpl

Source Code of org.itsnat.impl.core.domimpl.ElementsByIdImpl

/*
  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.domimpl;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.batik.dom.AbstractAttr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

/**
*
    Batik mete la pata haciendo que AbstractDocument.elementsById pueda apuntar a elementos que ya no est�n
    en el documento (aunque luego el getElementsById s�lo pida los del documento)
    y por otra parte via Garbage Collector se eliminan de la colecci�n por el hilo CleanerThread
    pero a trav�s con referencias SoftReference que son m�s conservadoras.
    El resultado es un elementsById lleno de elementos in�tiles temporalmente, que tenemos que evitar que
    se serialicen en el caso de replicaci�n de sesiones.
    El resultado es que el nuevo elementsById al deserializar no contendr� los nodos fuera del documento
    esto es compatible con Batik pues una posible b�squeda de nodos fuera del documento
    puede estar en el m�todo getElementById de SVGSVGElement (implementado en SVGOMSVGElement)
    pero nosotros no usamos la API SVG en el servidor.
    Habr�a que estudiar el problema de los nodos que no est�n en el documento
    pero que de alguna forma se han serializado, al insertar de nuevo en el documento
    quiz�s no se inserten en elementsById

    Gracias a estos cambios no tenemos el dichoso CleanerThread que tantos problemas dio,
    por ejemplo tuvimos que hacer aquel org.apache.batik.util.CleanerThreadFinisher para matarlo cuando
    "se estropeaba" al recargar muchas veces la aplicaci�n en desarrollo, y el caso de GAE que
    no dejaba crearlo (creando memory leaks)
    Era la gesti�n del registro de ids el �nico caso que creaba el hilo indirectamente al crear las
    SoftReference. Hay otros casos de uso de CleanerThread en Batik pero nosotros no los usamos
   (apenas usamos el DOM y sin APIs SVG)


* @author jmarranz
*/
public class ElementsByIdImpl implements EventListener,Serializable
{
    protected DocumentImpl doc;
    protected Map<String,Object> elementsById;

    public ElementsByIdImpl(DocumentImpl doc)
    {
        this.doc = doc;

        doc.addEventListenerInternal("DOMNodeInserted",this,false);
        doc.addEventListenerInternal("DOMNodeRemoved",this,false);
    }

    public Node getRoot(Node n)
    {
        Node r = n;
        while (n != null) {
            r = n;
            n = n.getParentNode();
        }
        return r;
    }

    public boolean isInsideDoc(Node node)
    {
        return getRoot(node) == doc;
    }

    public Element getElementById(String id)
    {
        return getChildElementById(doc.getDocumentElement(), id);
    }

    public Element getChildElementById(Node requestor, String id)
    {
        if ((id == null) || (id.length()==0)) return null;
        if (elementsById == null) return null;

        Object o = elementsById.get(id);
        if (o == null) return null;

        Node root = getRoot(requestor);
        boolean all = (root == doc);
        if (o instanceof Element)
        {
            Element e = (Element)o;
            if (all || (getRoot(e) == root))
                return e;
        }
        else
        {
            @SuppressWarnings("unchecked")
            List<Element> l = (List<Element>)o;
            for (Element e : l)
            {
                if (all || (getRoot(e) == root))
                    return e;
            }
        }
        return null;
    }

    public void removeIdEntry(Element e, String id)
    {
        removeIdEntry(e,id,true);
    }

    public void removeIdEntry(Element e, String id,boolean checkInDoc)
    {
        if (id == null) return;
        if (elementsById == null) return;

        if (checkInDoc && !isInsideDoc(e))
            return;

        Object o = elementsById.get(id);
        if (o == null) return;

        if (o instanceof Element)
        {
            elementsById.remove(id);
        }
        else
        {
            @SuppressWarnings("unchecked")
            List<Element> l = (List<Element>)o;
            for (Iterator<Element> li = l.iterator(); li.hasNext(); )
            {
                Element currElem = li.next();
                if (e == currElem)
                {
                    li.remove();
                    break;
                }
            }
            if (l.isEmpty())
                elementsById.remove(id);
        }
    }

    public void addIdEntry(Element e, String id)
    {
        addIdEntry(e,id,true);
    }   

    public void addIdEntry(Element e, String id,boolean checkInDoc)
    {
        if (id == null) return;

        if (checkInDoc && !isInsideDoc(e))
            return;

        if (elementsById == null)
            this.elementsById = new HashMap<String,Object>();

        // Add new Id mapping.
        Object o = elementsById.get(id);
        if (o == null)
        {
            elementsById.put(id,e);
        }
        else if (o instanceof Element)
        {
            Element currElem = (Element)o;
            // Create new List for this new element (and current).
            List<Element> l = new ArrayList<Element>(4);
            l.add(currElem);
            l.add(e);
            elementsById.put(id, l);
        }
        else // List
        {
            @SuppressWarnings("unchecked")
            List<Element> l = (List<Element>)o;
            l.add(e);
        }
    }

    public void updateIdEntry(Element e, String oldId, String newId)
    {
        updateIdEntry(e, oldId, newId,true);
    }

    public void updateIdEntry(Element e, String oldId, String newId,boolean checkInDoc)
    {
        if ((oldId == newId) || ((oldId != null) && (oldId.equals(newId))))
            return;

        if (checkInDoc && !isInsideDoc(e))
            return;

        removeIdEntry(e, oldId,false);
        addIdEntry(e, newId,false);
    }

    public void handleEvent(Event evt)
    {
        Node node = (Node)evt.getTarget(); // node es el nuevo o a eliminar

        String type = evt.getType();
        if (type.equals("DOMNodeInserted"))
            processTreeAddRemoveElementsWithId(node,true);
        else // DOMNodeRemoved
            processTreeAddRemoveElementsWithId(node,false);
    }

    protected void processTreeAddRemoveElementsWithId(Node node,boolean add)
    {
        if (node.getNodeType() != Node.ELEMENT_NODE) return;

        Element elem = (Element)node;
        if (elem.hasAttributes())
        {
            NamedNodeMap attributes = elem.getAttributes();
            for(int i = 0; i < attributes.getLength(); i++)
            {
                AbstractAttr attr = (AbstractAttr)attributes.item(i);
                if (attr.isId())
                {
                    if (add)
                        addIdEntry(elem,attr.getValue(),false); // checkInDoc es false porque SABEMOS que est� en el documento
                    else
                        removeIdEntry(elem,attr.getValue(),false);   // checkInDoc es false porque SABEMOS que est� en el documento (incluso cuando eliminamos porque no se ha eliminado todav�a)
                    break;
                }
            }
        }

        Node child = elem.getFirstChild();
        while (child != null)
        {
            processTreeAddRemoveElementsWithId(child,add);
            child = child.getNextSibling();
        }
    }

    private void writeObject(ObjectOutputStream out) throws IOException
    {
        out.defaultWriteObject();
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
    {
        in.defaultReadObject();
       
        doc.addEventListenerInternal("DOMNodeInserted",this,false);
        doc.addEventListenerInternal("DOMNodeRemoved",this,false);
    }
}
TOP

Related Classes of org.itsnat.impl.core.domimpl.ElementsByIdImpl

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.