/*
* 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: lobochief@users.sourceforge.net
*/
/*
* Created on Oct 29, 2005
*/
package com.nvarghese.beowulf.common.cobra.html.domimpl;
import java.util.ArrayList;
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 org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMException;
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.TypeInfo;
import com.nvarghese.beowulf.common.cobra.util.Objects;
import com.nvarghese.beowulf.common.cobra.util.Strings;
public class ElementImpl extends NodeImpl implements Element {
private final String name;
public ElementImpl(String name) {
super();
this.name = name;
}
protected Map attributes;
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.domimpl.NodeImpl#getattributes()
*/
public NamedNodeMap getAttributes() {
synchronized (this) {
Map attrs = this.attributes;
if (attrs == null) {
attrs = new HashMap();
this.attributes = attrs;
}
return new NamedNodeMapImpl(this, this.attributes);
}
}
public boolean hasAttributes() {
synchronized (this) {
Map attrs = this.attributes;
return attrs == null ? false : !attrs.isEmpty();
}
}
public boolean equalAttributes(Node arg) {
if (arg instanceof ElementImpl) {
synchronized (this) {
Map attrs1 = this.attributes;
if (attrs1 == null) {
attrs1 = Collections.EMPTY_MAP;
}
Map attrs2 = ((ElementImpl) arg).attributes;
if (attrs2 == null) {
attrs2 = Collections.EMPTY_MAP;
}
return Objects.equals(attrs1, attrs2);
}
} else {
return false;
}
}
private String id;
public String getId() {
String id = this.id;
return id == null ? "" : id;
}
public void setId(String id) {
this.setAttribute("id", id);
}
// private String title;
public String getTitle() {
return this.getAttribute("title");
}
public void setTitle(String title) {
this.setAttribute("title", title);
}
public String getLang() {
return this.getAttribute("lang");
}
public void setLang(String lang) {
this.setAttribute("lang", lang);
}
public String getDir() {
return this.getAttribute("dir");
}
public void setDir(String dir) {
this.setAttribute("dir", dir);
}
public final String getAttribute(String name) {
String normalName = this.normalizeAttributeName(name);
synchronized (this) {
Map attributes = this.attributes;
return attributes == null ? null : (String) attributes.get(normalName);
}
}
private Attr getAttr(String normalName, String value) {
// TODO: "specified" attributes
return new AttrImpl(normalName, value, true, this, "id".equals(normalName));
}
public Attr getAttributeNode(String name) {
String normalName = this.normalizeAttributeName(name);
synchronized (this) {
Map attributes = this.attributes;
String value = attributes == null ? null : (String) attributes.get(normalName);
return value == null ? null : this.getAttr(normalName, value);
}
}
public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
public String getAttributeNS(String namespaceURI, String localName) throws DOMException {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
protected static boolean isTagName(Node node, String name) {
return node.getNodeName().equalsIgnoreCase(name);
}
public NodeList getElementsByTagName(String name) {
boolean matchesAll = "*".equals(name);
List descendents = new LinkedList();
synchronized (this.treeLock) {
ArrayList nl = this.nodeList;
if (nl != null) {
Iterator i = nl.iterator();
while (i.hasNext()) {
Object child = i.next();
if (child instanceof Element) {
Element childElement = (Element) child;
if (matchesAll || isTagName(childElement, name)) {
descendents.add(child);
}
NodeList sublist = childElement.getElementsByTagName(name);
int length = sublist.getLength();
for (int idx = 0; idx < length; idx++) {
descendents.add(sublist.item(idx));
}
}
}
}
}
return new NodeListImpl(descendents);
}
public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
public TypeInfo getSchemaTypeInfo() {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
public String getTagName() {
return this.getNodeName();
}
public boolean hasAttribute(String name) {
String normalName = this.normalizeAttributeName(name);
synchronized (this) {
Map attributes = this.attributes;
return attributes == null ? false : attributes.containsKey(normalName);
}
}
public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
public void removeAttribute(String name) throws DOMException {
String normalName = this.normalizeAttributeName(name);
synchronized (this) {
Map attributes = this.attributes;
if (attributes == null) {
return;
}
attributes.remove(normalName);
}
}
public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
String normalName = this.normalizeAttributeName(oldAttr.getName());
synchronized (this) {
Map attributes = this.attributes;
if (attributes == null) {
return null;
}
String oldValue = (String) attributes.remove(normalName);
// TODO: "specified" attributes
return oldValue == null ? null : this.getAttr(normalName, oldValue);
}
}
public void removeAttributeNS(String namespaceURI, String localName) throws DOMException {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
protected void assignAttributeField(String normalName, String value) {
// Note: overriders assume that processing here is only done after
// checking attribute names, i.e. they may not call the super
// implementation if an attribute is already taken care of.
boolean isName = false;
if ("id".equals(normalName) || (isName = "name".equals(normalName))) {
// Note that the value of name is used
// as an ID, but the value of ID is not
// used as a name.
if (!isName) {
this.id = value;
}
HTMLDocumentImpl document = (HTMLDocumentImpl) this.document;
if (document != null) {
// // Do not remove old ID. Consider scenario where both
// // name and ID are provided in an element.
// if (oldId != null) {
// document.removeElementById(oldId);
// }
document.setElementById(value, this);
if (isName) {
String oldName = this.getAttribute("name");
if (oldName != null) {
document.removeNamedItem(oldName);
}
document.setNamedItem(value, this);
}
}
}
}
protected final String normalizeAttributeName(String name) {
return name.toLowerCase();
}
public void setAttribute(String name, String value) throws DOMException {
String normalName = this.normalizeAttributeName(name);
synchronized (this) {
Map attribs = this.attributes;
if (attribs == null) {
attribs = new HashMap(2);
this.attributes = attribs;
}
attribs.put(normalName, value);
}
this.assignAttributeField(normalName, value);
}
/**
* Fast method to set attributes. It is not thread safe. Calling thread
* should hold a treeLock.
*/
public void setAttributeImpl(String name, String value) throws DOMException {
String normalName = this.normalizeAttributeName(name);
Map attribs = this.attributes;
if (attribs == null) {
attribs = new HashMap(2);
this.attributes = attribs;
}
this.assignAttributeField(normalName, value);
attribs.put(normalName, value);
}
public Attr setAttributeNode(Attr newAttr) throws DOMException {
String normalName = this.normalizeAttributeName(newAttr.getName());
String value = newAttr.getValue();
synchronized (this) {
if (this.attributes == null) {
this.attributes = new HashMap();
}
this.attributes.put(normalName, value);
// this.setIdAttribute(normalName, newAttr.isId());
}
this.assignAttributeField(normalName, value);
return newAttr;
}
public Attr setAttributeNodeNS(Attr newAttr) throws DOMException {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
public void setIdAttribute(String name, boolean isId) throws DOMException {
String normalName = this.normalizeAttributeName(name);
if (!"id".equals(normalName)) {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "IdAttribute can't be anything other than ID");
}
}
public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException {
String normalName = this.normalizeAttributeName(idAttr.getName());
if (!"id".equals(normalName)) {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "IdAttribute can't be anything other than ID");
}
}
public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Namespaces not supported");
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.domimpl.NodeImpl#getLocalName()
*/
public String getLocalName() {
return this.getNodeName();
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.domimpl.NodeImpl#getNodeName()
*/
public String getNodeName() {
return this.name;
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.domimpl.NodeImpl#getNodeType()
*/
public short getNodeType() {
return Node.ELEMENT_NODE;
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.domimpl.NodeImpl#getNodeValue()
*/
public String getNodeValue() throws DOMException {
return null;
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.domimpl.NodeImpl#setNodeValue(java.lang.String)
*/
public void setNodeValue(String nodeValue) throws DOMException {
// nop
}
/**
* Gets inner text of the element, possibly including text in comments. This
* can be used to get Javascript code out of a SCRIPT element.
*
* @param includeComment
*/
protected String getRawInnerText(boolean includeComment) {
synchronized (this.treeLock) {
ArrayList nl = this.nodeList;
if (nl != null) {
Iterator i = nl.iterator();
StringBuilder sb = null;
while (i.hasNext()) {
Object node = i.next();
if (node instanceof Text) {
Text tn = (Text) node;
String txt = tn.getNodeValue();
if (!"".equals(txt)) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append(txt);
}
} else if (node instanceof ElementImpl) {
ElementImpl en = (ElementImpl) node;
String txt = en.getRawInnerText(includeComment);
if (!"".equals(txt)) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append(txt);
}
} else if (includeComment && node instanceof Comment) {
Comment cn = (Comment) node;
String txt = cn.getNodeValue();
if (!"".equals(txt)) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append(txt);
}
}
}
return sb == null ? "" : sb.toString();
} else {
return "";
}
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getNodeName());
sb.append(" [");
NamedNodeMap attribs = this.getAttributes();
int length = attribs.getLength();
for (int i = 0; i < length; i++) {
Attr attr = (Attr) attribs.item(i);
sb.append(attr.getNodeName());
sb.append('=');
sb.append(attr.getNodeValue());
if (i + 1 < length) {
sb.append(',');
}
}
sb.append("]");
return sb.toString();
}
public void setInnerText(String newText) {
org.w3c.dom.Document document = this.document;
if (document == null) {
this.warn("setInnerText(): Element " + this + " does not belong to a document.");
return;
}
synchronized (this.treeLock) {
ArrayList nl = this.nodeList;
if (nl != null) {
nl.clear();
}
}
// Create node and call appendChild outside of synchronized block.
Node textNode = document.createTextNode(newText);
this.appendChild(textNode);
}
protected Node createSimilarNode() {
HTMLDocumentImpl doc = (HTMLDocumentImpl) this.document;
return doc == null ? null : doc.createElement(this.getTagName());
}
protected String htmlEncodeChildText(String text) {
if (com.nvarghese.beowulf.common.cobra.html.parser.HtmlParser.isDecodeEntities(this.name)) {
return Strings.strictHtmlEncode(text, false);
} else {
return text;
}
}
/**
* Attempts to convert the subtree starting at this point to a close text
* representation. BR elements are converted to line breaks, and so forth.
*/
public String getInnerText() {
StringBuilder buffer = new StringBuilder();
synchronized (treeLock) {
appendInnerTextImpl(buffer);
}
return buffer.toString();
}
protected void appendInnerTextImpl(StringBuilder buffer) {
ArrayList nl = nodeList;
if (nl == null) {
return;
}
int size = nl.size();
if (size == 0) {
return;
}
for (int i = 0; i < size; i++) {
Node child = (Node) nl.get(i);
if (child instanceof ElementImpl) {
((ElementImpl) child).appendInnerTextImpl(buffer);
}
if (child instanceof Comment) {
// skip
} else if (child instanceof Text) {
buffer.append(((Text) child).getTextContent());
}
}
}
}