/*
* GNU LESSER GENERAL PUBLIC LICENSE Copyright (C) 2006 The Lobo Project
*
* This library 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 2.1 of
* the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Contact info: xamjadmin@users.sourceforge.net
*/
/*
* Created on Sep 3, 2005
*/
package org.cobra_grendel.html.domimpl;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.cobra_grendel.html.HtmlRendererContext;
import org.cobra_grendel.html.UserAgentContext;
import org.cobra_grendel.js.AbstractScriptableDelegate;
import org.cobra_grendel.util.Objects;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.w3c.dom.UserDataHandler;
public abstract class NodeImpl extends AbstractScriptableDelegate implements Node, ModelNode
{
/**
*
*/
private static final long serialVersionUID = 1L;
private static final NodeImpl[] EMPTY_ARRAY = new NodeImpl[0];
// private static final RenderState INVALID_RENDER_STATE = new
// StyleSheetRenderState(null, null);
protected static final Logger logger = Logger.getLogger(NodeImpl.class.getName());
private ChildHTMLCollection childrenCollection;
private volatile String prefix;
private Map userData;
private String baseURI = "";
// TODO: Inform handlers on cloning, etc.
private List userDataHandlers;
protected volatile Document document;
protected ArrayList<NodeImpl> nodeList;
protected volatile boolean notificationsSuspended = false;
protected volatile Node parentNode;
/**
* A tree lock is less deadlock-prone than a node-level lock. This is assigned in setOwnerDocument.
*/
protected volatile Object treeLock = this;
protected UINode uiNode;
public NodeImpl(final int transactionId)
{
super(transactionId);
}
// protected final Object getTreeLock() {
// //TODO: Is this necessary? Why not use a lock per node?
// Object doc = this.document;
// return doc == null ? this : doc;
// }
//
@Override
public Node appendChild(final Node newChild) throws DOMException
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl == null)
{
nl = new ArrayList(3);
nodeList = nl;
}
nl.add(newChild);
if (newChild instanceof NodeImpl)
{
((NodeImpl) newChild).setParentImpl(this);
}
}
if (!notificationsSuspended)
{
informInvalid();
}
return newChild;
}
private void appendChildrenToCollectionImpl(final NodeFilter filter, final Collection collection)
{
ArrayList nl = nodeList;
if (nl != null)
{
Iterator i = nl.iterator();
while (i.hasNext())
{
NodeImpl node = (NodeImpl) i.next();
if (filter.accept(node))
{
collection.add(node);
}
node.appendChildrenToCollectionImpl(filter, collection);
}
}
}
/**
* Sets the document node to null. This is so a single element can be referenced without retaining the entire DOM. If you start calling stuff that references parent nodes, ancestors, etc, expect a
* null reference exception.
*
* @param recursive
*/
public void clearDocument(final boolean recursive)
{
if (document != null)
{
baseURI = document.getBaseURI();
}
setOwnerDocument(null, recursive);
}
/**
* Sets the parent node to null.
*
* @param recursive
*/
public void clearParent()
{
setParentImpl(null);
}
@Override
public Node cloneNode(final boolean deep)
{
try
{
Node newNode = createSimilarNode();
NodeList children = getChildNodes();
int length = children.getLength();
for (int i = 0; i < length; i++)
{
Node child = children.item(i);
Node newChild = deep ? child.cloneNode(deep) : child;
newNode.appendChild(newChild);
}
if (newNode instanceof Element)
{
Element elem = (Element) newNode;
NamedNodeMap nnmap = getAttributes();
if (nnmap != null)
{
int nnlength = nnmap.getLength();
for (int i = 0; i < nnlength; i++)
{
Attr attr = (Attr) nnmap.item(i);
elem.setAttributeNode((Attr) attr.cloneNode(true));
}
}
}
return newNode;
}
catch (Exception err)
{
throw new IllegalStateException(err.getMessage());
}
}
@Override
public short compareDocumentPosition(final Node other) throws DOMException
{
Node parent = getParentNode();
if (!(other instanceof NodeImpl))
{
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Unknwon node implementation");
}
if (parent != null && parent == other.getParentNode())
{
int thisIndex = getNodeIndex();
int otherIndex = ((NodeImpl) other).getNodeIndex();
if (thisIndex == -1 || otherIndex == -1)
{
return Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
}
if (thisIndex < otherIndex)
{
return Node.DOCUMENT_POSITION_FOLLOWING;
}
else
{
return Node.DOCUMENT_POSITION_PRECEDING;
}
}
else if (isAncestorOf(other))
{
return Node.DOCUMENT_POSITION_CONTAINED_BY;
}
else if (((NodeImpl) other).isAncestorOf(this))
{
return Node.DOCUMENT_POSITION_CONTAINS;
}
else
{
return Node.DOCUMENT_POSITION_DISCONNECTED;
}
}
/**
* Should create a node with some cloned properties, like the node name, but not attributes or children.
*/
protected abstract Node createSimilarNode();
public boolean equalAttributes(final Node arg)
{
return false;
}
/**
* Extracts all descendents that match the filter, except those descendents of nodes that match the filter.
*
* @param filter
* @param al
*/
private void extractDescendentsArrayImpl(final NodeFilter filter, final ArrayList al)
{
ArrayList nl = nodeList;
if (nl != null)
{
Iterator i = nl.iterator();
while (i.hasNext())
{
NodeImpl n = (NodeImpl) i.next();
if (filter.accept(n))
{
al.add(n);
}
else if (n.getNodeType() == Node.ELEMENT_NODE)
{
n.extractDescendentsArrayImpl(filter, al);
}
}
}
}
/**
* Tries to get a UINode associated with the current node. Failing that, it tries ancestors recursively.
*/
public UINode findUINode()
{
// Called in GUI thread always.
UINode uiNode = this.uiNode;
if (uiNode != null)
{
return uiNode;
}
NodeImpl parentNode = (NodeImpl) getParentNode();
return parentNode == null ? null : parentNode.findUINode();
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.renderer.RenderableContext#getAlignmentX()
*/
public float getAlignmentX()
{
// TODO: Removable method?
return 0.5f;
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.renderer.RenderableContext#getAlignmentY()
*/
public float getAlignmentY()
{
return 0.5f;
}
@Override
public NamedNodeMap getAttributes()
{
return null;
}
@Override
public String getBaseURI()
{
if (document == null)
{
return baseURI;
}
return document.getBaseURI();
}
Node getChildAtIndex(final int index)
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
try
{
return nl == null ? null : (Node) nl.get(index);
}
catch (IndexOutOfBoundsException iob)
{
this.warn("getChildAtIndex(): Bad index=" + index + " for node=" + this + ".");
return null;
}
}
}
int getChildCount()
{
ArrayList nl = nodeList;
synchronized (treeLock)
{
return nl == null ? 0 : nl.size();
}
}
int getChildIndex(final Node child)
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
return nl == null ? -1 : nl.indexOf(child);
}
}
@Override
public NodeList getChildNodes()
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
return new NodeListImpl(nl == null ? Collections.EMPTY_LIST : nl, transactionId);
}
}
public ChildHTMLCollection getChildren()
{
// Method required by JavaScript
synchronized (this)
{
ChildHTMLCollection collection = childrenCollection;
if (collection == null)
{
collection = new ChildHTMLCollection(this, transactionId);
childrenCollection = collection;
}
return collection;
}
}
public NodeImpl[] getChildrenArray()
{
ArrayList nl = nodeList;
synchronized (treeLock)
{
return nl == null ? null : (NodeImpl[]) nl.toArray(NodeImpl.EMPTY_ARRAY);
}
}
/**
* Gets descendent nodes that match according to the filter, but it does not nest into matching nodes.
*/
public ArrayList getDescendents(final NodeFilter filter)
{
ArrayList al = new ArrayList();
synchronized (treeLock)
{
extractDescendentsArrayImpl(filter, al);
}
return al;
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.renderer.RenderableContext#getDocumentItem(java.lang.String)
*/
@Override
public Object getDocumentItem(final String name)
{
org.w3c.dom.Document document = this.document;
return document == null ? null : document.getUserData(name);
}
public URL getDocumentURL()
{
Object doc = document;
if (doc instanceof HTMLDocumentImpl)
{
return ((HTMLDocumentImpl) doc).getDocumentURL();
}
else
{
return null;
}
}
@Override
public Object getFeature(final String feature, final String version)
{
// TODO What should this do?
return null;
}
@Override
public Node getFirstChild()
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
try
{
return nl == null ? null : (Node) nl.get(0);
}
catch (IndexOutOfBoundsException iob)
{
return null;
}
}
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.renderer.RenderableContext#getFullURL(java.lang.String)
*/
@Override
public URL getFullURL(final String spec) throws MalformedURLException
{
Object doc = document;
if (doc instanceof HTMLDocumentImpl)
{
return ((HTMLDocumentImpl) doc).getFullURL(spec);
}
else
{
return new java.net.URL(spec);
}
}
public HtmlRendererContext getHtmlRendererContext()
{
Object doc = document;
if (doc instanceof HTMLDocumentImpl)
{
return ((HTMLDocumentImpl) doc).getHtmlRendererContext();
}
else
{
return null;
}
}
@Override
public Node getLastChild()
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
try
{
return nl == null ? null : (Node) nl.get(nl.size() - 1);
}
catch (IndexOutOfBoundsException iob)
{
return null;
}
}
}
@Override
public abstract String getLocalName();
@Override
public String getNamespaceURI()
{
return null;
}
@Override
public Node getNextSibling()
{
NodeImpl parent = (NodeImpl) getParentNode();
return parent == null ? null : parent.getNextTo(this);
}
private Node getNextTo(final Node node)
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
int idx = nl == null ? -1 : nl.indexOf(node);
if (idx == -1)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "node not found");
}
try
{
return (Node) nl.get(idx + 1);
}
catch (IndexOutOfBoundsException iob)
{
return null;
}
}
}
private int getNodeIndex()
{
NodeImpl parent = (NodeImpl) getParentNode();
return parent == null ? -1 : parent.getChildIndex(this);
}
protected NodeList getNodeList(final NodeFilter filter)
{
Collection collection = new ArrayList();
synchronized (treeLock)
{
appendChildrenToCollectionImpl(filter, collection);
}
return new NodeListImpl(collection, transactionId);
}
@Override
public abstract String getNodeName();
@Override
public abstract short getNodeType();
@Override
public abstract String getNodeValue() throws DOMException;
@Override
public Document getOwnerDocument()
{
return document;
}
@Override
public final ModelNode getParentModelNode()
{
return (ModelNode) parentNode;
}
@Override
public Node getParentNode()
{
// Should it be synchronized? Could have side-effects.
return parentNode;
}
@Override
public String getPrefix()
{
return prefix;
}
@Override
public Node getPreviousSibling()
{
NodeImpl parent = (NodeImpl) getParentNode();
return parent == null ? null : parent.getPreviousTo(this);
}
private Node getPreviousTo(final Node node)
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
int idx = nl == null ? -1 : nl.indexOf(node);
if (idx == -1)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "node not found");
}
try
{
return (Node) nl.get(idx - 1);
}
catch (IndexOutOfBoundsException iob)
{
return null;
}
}
}
public ArrayList<NodeImpl> getRawNodeList()
{
return nodeList;
}
/**
* Gets the text content of this node and its descendents.
*/
@Override
public String getTextContent() throws DOMException
{
StringBuffer sb = new StringBuffer();
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl != null)
{
Iterator i = nl.iterator();
while (i.hasNext())
{
Node node = (Node) i.next();
short type = node.getNodeType();
switch (type)
{
case Node.CDATA_SECTION_NODE:
case Node.TEXT_NODE:
case Node.ELEMENT_NODE:
String textContent = node.getTextContent();
if (textContent != null)
{
sb.append(textContent);
}
break;
default:
break;
}
}
}
}
return sb.toString();
}
public UINode getUINode()
{
// Called in GUI thread always.
return uiNode;
}
public UserAgentContext getUserAgentContext()
{
Object doc = document;
if (doc instanceof HTMLDocumentImpl)
{
return ((HTMLDocumentImpl) doc).getUserAgentContext();
}
else
{
return null;
}
}
@Override
public Object getUserData(final String key)
{
synchronized (this)
{
Map ud = userData;
return ud == null ? null : ud.get(key);
}
}
@Override
public boolean hasAttributes()
{
return false;
}
@Override
public boolean hasChildNodes()
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
return nl != null && !nl.isEmpty();
}
}
protected void informExternalScriptLoading()
{
// This is called when an attribute or child changes.
// this.forgetRenderState();
HTMLDocumentImpl doc = (HTMLDocumentImpl) document;
if (doc != null)
{
doc.externalScriptLoading(this);
}
}
public void informInvalid()
{
// This is called when an attribute or child changes.
// this.forgetRenderState();
HTMLDocumentImpl doc = (HTMLDocumentImpl) document;
if (doc != null)
{
doc.invalidated(this);
}
}
public void informLayoutInvalid()
{
// This is called by the style properties object.
// this.forgetRenderState();
HTMLDocumentImpl doc = (HTMLDocumentImpl) document;
if (doc != null)
{
doc.invalidated(this);
}
}
public void informLookInvalid()
{
// this.forgetRenderState();
HTMLDocumentImpl doc = (HTMLDocumentImpl) document;
if (doc != null)
{
doc.lookInvalidated(this);
}
}
protected void informNodeLoaded()
{
// This is called when an attribute or child changes.
// this.forgetRenderState();
HTMLDocumentImpl doc = (HTMLDocumentImpl) document;
if (doc != null)
{
doc.nodeLoaded(this);
}
}
public void informPositionInvalid()
{
HTMLDocumentImpl doc = (HTMLDocumentImpl) document;
if (doc != null)
{
doc.positionInParentInvalidated(this);
}
}
public void informSizeInvalid()
{
HTMLDocumentImpl doc = (HTMLDocumentImpl) document;
if (doc != null)
{
doc.sizeInvalidated(this);
}
}
public Node insertAfter(final Node newChild, final Node refChild)
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
int idx = nl == null ? -1 : nl.indexOf(refChild);
if (idx == -1)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "refChild not found");
}
nl.add(idx + 1, newChild);
if (newChild instanceof NodeImpl)
{
((NodeImpl) newChild).setParentImpl(this);
}
}
if (!notificationsSuspended)
{
informInvalid();
}
return newChild;
}
protected Node insertAt(final Node newChild, final int idx) throws DOMException
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl == null)
{
nl = new ArrayList();
nodeList = nl;
}
nl.add(idx, newChild);
if (newChild instanceof NodeImpl)
{
((NodeImpl) newChild).setParentImpl(this);
}
}
if (!notificationsSuspended)
{
informInvalid();
}
return newChild;
}
@Override
public Node insertBefore(final Node newChild, final Node refChild) throws DOMException
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
int idx = nl == null ? -1 : nl.indexOf(refChild);
if (idx == -1)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "refChild not found");
}
nl.add(idx, newChild);
if (newChild instanceof NodeImpl)
{
((NodeImpl) newChild).setParentImpl(this);
}
}
if (!notificationsSuspended)
{
informInvalid();
}
return newChild;
}
private boolean isAncestorOf(final Node other)
{
NodeImpl parent = (NodeImpl) other.getParentNode();
if (parent == this)
{
return true;
}
else if (parent == null)
{
return false;
}
else
{
return isAncestorOf(parent);
}
}
@Override
public boolean isDefaultNamespace(final String namespaceURI)
{
return namespaceURI == null;
}
@Override
public boolean isEqualNode(final Node arg)
{
return arg instanceof NodeImpl && getNodeType() == arg.getNodeType() && Objects.equals(getNodeName(), arg.getNodeName()) && Objects.equals(getNodeValue(), arg.getNodeValue()) && Objects.equals(getLocalName(), arg.getLocalName())
&& Objects.equals(nodeList, ((NodeImpl) arg).nodeList) && equalAttributes(arg);
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.renderer.RenderableContext#isEqualOrDescendentOf(org.xamjwg.html.renderer.RenderableContext)
*/
@Override
public final boolean isEqualOrDescendentOf(final ModelNode otherContext)
{
if (otherContext == this)
{
return true;
}
Object parent = getParentNode();
if (parent instanceof HTMLElementImpl)
{
return ((HTMLElementImpl) parent).isEqualOrDescendentOf(otherContext);
}
else
{
return false;
}
}
@Override
public boolean isSameNode(final Node other)
{
return this == other;
}
@Override
public boolean isSupported(final String feature, final String version)
{
return "HTML".equals(feature) && version.compareTo("4.01") <= 0;
}
@Override
public String lookupNamespaceURI(final String prefix)
{
return null;
}
@Override
public String lookupPrefix(final String namespaceURI)
{
return null;
}
@Override
public void normalize()
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl != null)
{
Iterator i = nl.iterator();
List textNodes = new LinkedList();
boolean prevText = false;
while (i.hasNext())
{
Node child = (Node) i.next();
if (child.getNodeType() == Node.TEXT_NODE)
{
if (!prevText)
{
prevText = true;
textNodes.add(child);
}
}
else
{
prevText = false;
}
}
i = textNodes.iterator();
while (i.hasNext())
{
Text text = (Text) i.next();
this.replaceAdjacentTextNodes(text);
}
}
}
if (!notificationsSuspended)
{
informInvalid();
}
}
protected void removeAllChildren()
{
synchronized (treeLock)
{
removeAllChildrenImpl();
}
}
protected void removeAllChildrenImpl()
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl != null)
{
nl.clear();
// this.nodeList = null;
}
}
if (!notificationsSuspended)
{
informInvalid();
}
}
@Override
public Node removeChild(final Node oldChild) throws DOMException
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl == null || !nl.remove(oldChild))
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "oldChild not found");
}
}
if (!notificationsSuspended)
{
informInvalid();
}
return oldChild;
}
// ----- ModelNode implementation
public Node removeChildAt(final int index) throws DOMException
{
try
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl == null)
{
throw new DOMException(DOMException.INDEX_SIZE_ERR, "Empty list of children");
}
Node n = (Node) nl.remove(index);
if (n == null)
{
throw new DOMException(DOMException.INDEX_SIZE_ERR, "No node with that index");
}
return n;
}
}
finally
{
if (!notificationsSuspended)
{
informInvalid();
}
}
}
protected void removeChildren(final NodeFilter filter)
{
synchronized (treeLock)
{
removeChildrenImpl(filter);
}
if (!notificationsSuspended)
{
informInvalid();
}
}
protected void removeChildrenImpl(final NodeFilter filter)
{
ArrayList nl = nodeList;
if (nl != null)
{
int len = nl.size();
for (int i = len; --i >= 0;)
{
Node node = (Node) nl.get(i);
if (filter.accept(node))
{
nl.remove(i);
}
}
}
}
public Text replaceAdjacentTextNodes(final Text node)
{
try
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl == null)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "Node not a child");
}
int idx = nl.indexOf(node);
if (idx == -1)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "Node not a child");
}
StringBuffer textBuffer = new StringBuffer();
int firstIdx = idx;
List toDelete = new LinkedList();
for (int adjIdx = idx; --adjIdx >= 0;)
{
Object child = nodeList.get(adjIdx);
if (child instanceof Text)
{
firstIdx = adjIdx;
toDelete.add(child);
textBuffer.append(((Text) child).getNodeValue());
}
}
int length = nodeList.size();
for (int adjIdx = idx; ++adjIdx < length;)
{
Object child = nodeList.get(adjIdx);
if (child instanceof Text)
{
toDelete.add(child);
textBuffer.append(((Text) child).getNodeValue());
}
}
nodeList.removeAll(toDelete);
TextImpl textNode = new TextImpl(textBuffer.toString(), transactionId);
textNode.setOwnerDocument(document);
textNode.setParentImpl(this);
nodeList.add(firstIdx, textNode);
return textNode;
}
}
finally
{
if (!notificationsSuspended)
{
informInvalid();
}
}
}
public Text replaceAdjacentTextNodes(final Text node, final String textContent)
{
try
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl == null)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "Node not a child");
}
int idx = nl.indexOf(node);
if (idx == -1)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "Node not a child");
}
int firstIdx = idx;
List toDelete = new LinkedList();
for (int adjIdx = idx; --adjIdx >= 0;)
{
Object child = nodeList.get(adjIdx);
if (child instanceof Text)
{
firstIdx = adjIdx;
toDelete.add(child);
}
}
int length = nodeList.size();
for (int adjIdx = idx; ++adjIdx < length;)
{
Object child = nodeList.get(adjIdx);
if (child instanceof Text)
{
toDelete.add(child);
}
}
nodeList.removeAll(toDelete);
TextImpl textNode = new TextImpl(textContent, transactionId);
textNode.setOwnerDocument(document);
textNode.setParentImpl(this);
nodeList.add(firstIdx, textNode);
return textNode;
}
}
finally
{
if (!notificationsSuspended)
{
informInvalid();
}
}
}
@Override
public Node replaceChild(final Node newChild, final Node oldChild) throws DOMException
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
int idx = nl == null ? -1 : nl.indexOf(oldChild);
if (idx == -1)
{
throw new DOMException(DOMException.NOT_FOUND_ERR, "oldChild not found");
}
nl.set(idx, newChild);
}
if (!notificationsSuspended)
{
informInvalid();
}
return newChild;
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.renderer.RenderableContext#setDocumentItem(java.lang.String, java.lang.Object)
*/
@Override
public void setDocumentItem(final String name, final Object value)
{
org.w3c.dom.Document document = this.document;
if (document == null)
{
return;
}
document.setUserData(name, value, null);
}
@Override
public abstract void setNodeValue(String nodeValue) throws DOMException;
void setOwnerDocument(final Document value)
{
document = value;
treeLock = value == null ? this : (Object) value;
}
void setOwnerDocument(final Document value, final boolean deep)
{
document = value;
treeLock = value == null ? this : (Object) value;
if (deep)
{
synchronized (treeLock)
{
ArrayList nl = nodeList;
if (nl != null)
{
Iterator i = nl.iterator();
while (i.hasNext())
{
NodeImpl child = (NodeImpl) i.next();
child.setOwnerDocument(value, deep);
}
}
}
}
}
final void setParentImpl(final Node parent)
{
// Call holding treeLock.
parentNode = parent;
}
@Override
public void setPrefix(final String prefix) throws DOMException
{
this.prefix = prefix;
}
public void setRawNodeList(final ArrayList<NodeImpl> nodeList)
{
this.nodeList = nodeList;
}
@Override
public void setTextContent(final String textContent) throws DOMException
{
synchronized (treeLock)
{
removeChildrenImpl(new TextFilter());
if (textContent != null && !"".equals(textContent))
{
TextImpl t = new TextImpl(textContent, transactionId);
t.setOwnerDocument(document);
t.setParentImpl(this);
ArrayList nl = nodeList;
if (nl == null)
{
nl = new ArrayList();
nodeList = nl;
}
nl.add(t);
}
}
if (!notificationsSuspended)
{
informInvalid();
}
}
public void setUINode(final UINode uiNode)
{
// Called in GUI thread always.
this.uiNode = uiNode;
}
@Override
public Object setUserData(final String key, final Object data, final UserDataHandler handler)
{
if (org.cobra_grendel.html.parser.HtmlParser.MODIFYING_KEY.equals(key))
{
boolean ns = Boolean.TRUE == data;
notificationsSuspended = ns;
if (!ns)
{
informNodeLoaded();
}
}
synchronized (this)
{
if (handler != null)
{
if (userDataHandlers == null)
{
userDataHandlers = new LinkedList();
}
userDataHandlers.add(handler);
}
Map userData = this.userData;
if (userData == null)
{
userData = new HashMap();
this.userData = userData;
}
return userData.put(key, data);
}
}
@Override
public String toString()
{
return getNodeName();
}
void visit(final NodeVisitor visitor)
{
synchronized (treeLock)
{
visitImpl(visitor);
}
}
void visitImpl(final NodeVisitor visitor)
{
try
{
visitor.visit(this);
}
catch (SkipVisitorException sve)
{
return;
}
catch (StopVisitorException sve)
{
throw sve;
}
ArrayList nl = nodeList;
if (nl != null)
{
Iterator i = nl.iterator();
while (i.hasNext())
{
NodeImpl child = (NodeImpl) i.next();
try
{
// Call with child's synchronization
child.visit(visitor);
}
catch (StopVisitorException sve)
{
throw sve;
}
}
}
}
public void warn(final String message)
{
logger.log(Level.WARNING, message);
}
@Override
public void warn(final String message, final Throwable err)
{
logger.log(Level.WARNING, message, err);
}
// private RenderState renderState = INVALID_RENDER_STATE;
//
// public RenderState getRenderState() {
// // Generally called from the GUI thread, except for
// // offset properties.
// RenderState rs;
// synchronized(this.treeLock) {
// rs = this.renderState;
// if(rs != INVALID_RENDER_STATE) {
// return rs;
// }
// Object parent = this.parentNode;
// if(parent != null || this instanceof Document) {
// RenderState prs = this.getParentRenderState(parent);
// rs = this.createRenderState(prs);
// this.renderState = rs;
// return rs;
// }
// else {
// // Return null without caching.
// // Scenario is possible due to Javascript.
// return null;
// }
// }
// }
//
// protected final RenderState getParentRenderState(Object parent) {
// if(parent instanceof NodeImpl) {
// return ((NodeImpl) parent).getRenderState();
// }
// else {
// return null;
// }
// }
//
// protected RenderState createRenderState(RenderState prevRenderState) {
// return prevRenderState;
// }
//
// protected void forgetRenderState() {
// synchronized(this.treeLock) {
// if(this.renderState != INVALID_RENDER_STATE) {
// this.renderState = INVALID_RENDER_STATE;
// // Note that getRenderState() "validates"
// // ancestor states as well.
// java.util.ArrayList nl = this.nodeList;
// if(nl != null) {
// Iterator i = nl.iterator();
// while(i.hasNext()) {
// ((NodeImpl) i.next()).forgetRenderState();
// }
// }
// }
// }
// }
}