blob: bc1fe262bb6aa90ae3c5e87fd762ce6cc0cd96be [file] [log] [blame]
/************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0. You can also
* obtain a copy of the License at http://odftoolkit.org/docs/license.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************/
package org.odftoolkit.odfdom.pkg;
import java.util.ArrayList;
import org.apache.xerces.dom.ElementNSImpl;
import org.apache.xerces.dom.ParentNode;
import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
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;
abstract public class OdfElement extends ElementNSImpl {
private static final long serialVersionUID = -4939293285696678939L;
/** Creates a new instance of OdfElement */
public OdfElement(OdfFileDom ownerDocument, String namespaceURI,
String qualifiedName) throws DOMException {
super(ownerDocument, namespaceURI, qualifiedName);
}
/** Creates a new instance of OdfElement */
public OdfElement(OdfFileDom ownerDocument, OdfName aName)
throws DOMException {
super(ownerDocument, aName.getUri(), aName.getQName());
}
abstract public OdfName getOdfName();
protected <T extends OdfElement> T getParentAs(Class<T> clazz) {
Node parent = getParentNode();
if (parent != null && clazz.isInstance(parent)) {
return clazz.cast(parent);
} else {
return null;
}
}
protected <T extends OdfElement> T getAncestorAs(Class<T> clazz) {
Node node = getParentNode();
while (node != null) {
if (clazz.isInstance(node)) {
return clazz.cast(node);
}
node = node.getParentNode();
}
return null;
}
@Override
public String toString() {
return mapNode(this, new StringBuilder()).toString();
}
/** Only Siblings will be traversed by this method as Children */
static private StringBuilder mapNodeTree(Node node, StringBuilder xml) {
while (node != null) {
// mapping node and this mapping include always all descendants
xml = mapNode(node, xml);
// next sibling will be mapped to XML
node = node.getNextSibling();
}
return xml;
}
private static StringBuilder mapNode(Node node, StringBuilder xml) {
if (node instanceof Element) {
xml = mapElementNode(node, xml);
} else if (node instanceof Text) {
xml = mapTextNode(node, xml);
}
return xml;
}
private static StringBuilder mapTextNode(Node node, StringBuilder xml) {
if (node != null) {
xml = xml.append(node.getTextContent());
}
return xml;
}
private static StringBuilder mapElementNode(Node node, StringBuilder xml) {
if (node != null) {
xml = xml.append("<");
xml = xml.append(node.getNodeName());
xml = mapAttributeNode(node, xml);
xml = xml.append(">");
xml = mapNodeTree(node.getFirstChild(), xml);
xml = xml.append("</");
xml = xml.append(node.getNodeName());
xml = xml.append(">");
}
return xml;
}
private static StringBuilder mapAttributeNode(Node node, StringBuilder xml) {
NamedNodeMap attrs = null;
int length;
if ((attrs = node.getAttributes()) != null
&& (length = attrs.getLength()) > 0) {
for (int i = 0; length > i; i++) {
xml = xml.append(" ");
xml = xml.append(attrs.item(i).getNodeName());
xml = xml.append("=\"");
xml = xml.append(attrs.item(i).getNodeValue());
xml = xml.append("\"");
}
}
return xml;
}
/**
* Set the value of an ODF attribute by <code>OdfName</code>.
*
* @param name
* The qualified name of the ODF attribute.
* @param value
* The value to be set in <code>String</code> form
*/
public void setOdfAttributeValue(OdfName name, String value) {
setAttributeNS(name.getUri(), name.getQName(), value);
}
/**
* Set an ODF attribute to this element
*
* @param attribute
* the attribute to be set
*/
public void setOdfAttribute(OdfAttribute attribute) {
setAttributeNodeNS(attribute);
}
/**
* Retrieves a value of an ODF attribute by <code>OdfName</code>.
*
* @param name
* The qualified name of the ODF attribute.
* @return The value of the attribute as <code>String</code> or
* <code>null</code> if the attribute does not exist.
*/
public String getOdfAttributeValue(OdfName name) {
return getAttributeNS(name.getUri(), name.getLocalName());
}
/**
* Retrieves an ODF attribute by <code>OdfName</code>.
*
* @param name
* The qualified name of the ODF attribute.
* @return The <code>OdfAttribute</code> or <code>null</code> if the
* attribute does not exist.
*/
public OdfAttribute getOdfAttribute(OdfName name) {
return (OdfAttribute) getAttributeNodeNS(name.getUri(), name.getLocalName());
}
/**
* Retrieves an ODF attribute by <code>NamespaceName</code>, and local name.
*
* @param namespace
* The namespace of the ODF attribute.
* @param localname
* The local name of the ODF attribute.
* @return The <code>OdfAttribute</code> or <code>null</code> if the
* attribute does not exist.
*/
public OdfAttribute getOdfAttribute(NamespaceName namespace, String localname) {
return (OdfAttribute) getAttributeNodeNS(namespace.getUri(),
localname);
}
/**
* Determines if an ODF attribute exists.
*
* @param name
* The qualified name of the ODF attribute.
* @return True if the attribute exists.
*/
public boolean hasOdfAttribute(OdfName name) {
return hasAttributeNS(name.getUri(), name.getLocalName());
}
/**
* returns the first child node that implements the given class.
*
* @param <T>
* The type of the ODF element to be found.
* @param clazz
* is a class that extends OdfElement.
* @param parentNode
* is the parent O of the children to be found.
* @return the first child node of the given parentNode that is a clazz or
* null if none is found.
*/
@SuppressWarnings("unchecked")
static public <T extends OdfElement> T findFirstChildNode(Class<T> clazz,
Node parentNode) {
if (parentNode != null && parentNode instanceof ParentNode) {
Node node = ((ParentNode) parentNode).getFirstChild();
while ((node != null) && !clazz.isInstance(node)) {
node = node.getNextSibling();
}
if (node != null) {
return (T) node;
}
}
return null;
}
/**
* returns the first sibling after the given reference node that implements
* the given class.
*
* @param <T>
* The type of the ODF element to be found.
* @param clazz
* is a class that extends OdfElement.
* @param refNode
* the reference node of the siblings to be found.
* @return the first sibbling of the given reference node that is a clazz or
* null if none is found.
*/
@SuppressWarnings("unchecked")
static public <T extends OdfElement> T findNextChildNode(Class<T> clazz,
Node refNode) {
if (refNode != null) {
Node node = refNode.getNextSibling();
while (node != null && !clazz.isInstance(node)) {
node = node.getNextSibling();
}
if (node != null) {
return (T) node;
}
}
return null;
}
/**
* returns the first previous sibling before the given reference node that
* implements the given class.
*
* @param clazz
* is a class that extends OdfElement.
* @param refNode
* the reference node which siblings are to be searched.
* @return the first previous sibbling of the given reference node that is a
* clazz or null if none is found.
*/
@SuppressWarnings("unchecked")
static public <T extends OdfElement> T findPreviousChildNode(
Class<T> clazz, Node refNode) {
if (refNode != null) {
Node node = refNode.getPreviousSibling();
while (node != null && !clazz.isInstance(node)) {
node = node.getPreviousSibling();
}
if (node != null) {
return (T) node;
}
}
return null;
}
protected OdfElement cloneOdfElement() {
return ((OdfFileDom) this.ownerDocument).newOdfElement(this.getClass());
}
@Override
public Node cloneNode(boolean deep) {
OdfElement cloneElement = this.cloneOdfElement();
if (attributes != null) {
for (int i = 0; i < attributes.getLength(); i++) {
Node item = attributes.item(i);
String qname = null;
String prefix = item.getPrefix();
if (prefix == null) {
qname = item.getLocalName();
} else {
qname = prefix + ":" + item.getLocalName();
}
cloneElement.setAttributeNS(item.getNamespaceURI(), qname, item.getNodeValue());
}
}
if (deep) {
Node childNode = getFirstChild();
while (childNode != null) {
cloneElement.appendChild(childNode.cloneNode(true));
childNode = childNode.getNextSibling();
}
}
return cloneElement;
}
/**
* indicates if some other object is equal to this one.
*
* @param obj
* - the reference object with which to compare.
* @return true if this object is the same as the obj argument; false
* otherwise.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if ((obj == null) || !(obj instanceof OdfElement)) {
return false;
}
OdfElement compare = (OdfElement) obj;
// compare node name
if (!localName.equals(compare.localName)) {
return false;
}
if (!this.namespaceURI.equals(compare.namespaceURI)) {
return false;
}
// compare node attributes
if (attributes == compare.attributes) {
return true;
}
if ((attributes == null) || (compare.attributes == null)) {
return false;
}
int attr_count1 = attributes.getLength();
int attr_count2 = compare.attributes.getLength();
ArrayList<Node> attr1 = new ArrayList<Node>();
for (int i = 0; i < attr_count1; i++) {
Node node = attributes.item(i);
if (node.getNodeValue().length() == 0) {
continue;
}
attr1.add(node);
}
ArrayList<Node> attr2 = new ArrayList<Node>();
for (int i = 0; i < attr_count2; i++) {
Node node = compare.attributes.item(i);
if (node.getNodeValue().length() == 0) {
continue;
}
attr2.add(node);
}
if (attr1.size() != attr2.size()) {
return false;
}
for (int i = 0; i < attr1.size(); i++) {
Node n1 = attr1.get(i);
if (n1.getLocalName().equals("name")
&& n1.getNamespaceURI().equals(
OdfDocumentNamespace.STYLE.getUri())) {
continue; // do not compare style names
}
Node n2 = null;
int j = 0;
for (j = 0; j < attr2.size(); j++) {
n2 = attr2.get(j);
if (n1.getLocalName().equals(n2.getLocalName())
&& n1.getNamespaceURI().equals(n2.getNamespaceURI())) {
break;
}
}
if (j == attr2.size()) {
return false;
}
if (!n1.getTextContent().equals(n2.getTextContent())) {
return false;
}
}
// now compare child elements
NodeList childs1 = this.getChildNodes();
NodeList childs2 = compare.getChildNodes();
int child_count1 = childs1.getLength();
int child_count2 = childs2.getLength();
if ((child_count1 == 0) && (child_count2 == 0)) {
return true;
}
ArrayList<Node> nodes1 = new ArrayList<Node>();
for (int i = 0; i < child_count1; i++) {
Node node = childs1.item(i);
if (node.getNodeType() == Node.TEXT_NODE) {
if (node.getNodeValue().trim().length() == 0) {
continue; // skip whitespace text nodes
}
}
nodes1.add(node);
}
ArrayList<Node> nodes2 = new ArrayList<Node>();
for (int i = 0; i < child_count2; i++) {
Node node = childs2.item(i);
if (node.getNodeType() == Node.TEXT_NODE) {
if (node.getNodeValue().trim().length() == 0) {
continue; // skip whitespace text nodes
}
}
nodes2.add(node);
}
if (nodes1.size() != nodes2.size()) {
return false;
}
for (int i = 0; i < nodes1.size(); i++) {
Node n1 = nodes1.get(i);
Node n2 = nodes2.get(i);
if (!n1.equals(n2)) {
return false;
}
}
return true;
}
protected void onRemoveNode(Node node) {
Node child = node.getFirstChild();
while (child != null) {
this.onRemoveNode(child);
child = child.getNextSibling();
}
if (OdfElement.class.isInstance(node)) {
((OdfElement) node).onRemoveNode();
}
}
protected void onInsertNode(Node node) {
Node child = node.getFirstChild();
while (child != null) {
this.onInsertNode(child);
child = child.getNextSibling();
}
if (OdfElement.class.isInstance(node)) {
((OdfElement) node).onInsertNode();
}
}
protected void onRemoveNode() {
}
protected void onInsertNode() {
}
@Override
public Node insertBefore(Node newChild, Node refChild) throws DOMException {
onInsertNode(newChild);
return super.insertBefore(newChild, refChild);
}
@Override
public Node removeChild(Node oldChild) throws DOMException {
onRemoveNode(oldChild);
return super.removeChild(oldChild);
}
@Override
public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
onInsertNode(newChild);
onRemoveNode(oldChild);
return super.replaceChild(newChild, oldChild);
}
/**
* Accept an visitor instance to allow the visitor to do some operations.
* Refer to visitor design pattern to get a better understanding.
* @param visitor an instance of DefaultElementVisitor
*/
public void accept(ElementVisitor visitor) {
visitor.visit(this);
}
}