blob: 66cfba0f0a56a648dcfacc43345ec25f1223401e [file] [log] [blame]
/*
* $Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Crimson" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Sun Microsystems, Inc.,
* http://www.sun.com. For more information on the Apache Software
* Foundation, please see <http://www.apache.org/>.
*/
package org.apache.xerces.tree;
import java.io.CharArrayWriter;
import java.io.Writer;
import java.io.IOException;
import java.util.Vector;
import org.w3c.dom.*;
import org.xml.sax.AttributeList;
import org.xml.sax.Attributes;
//import org.xml.sax.Attr;
// Should move to use the xerces sax2 Attribute impl
import org.apache.xerces.tree.AttributeListEx;
/**
* Class representing an XML attribute list.
*
* <P> This couples slightly with the Sun XML parser, in that it optionally
* uses an extended SAX 1.0 API to see if an attribute was specified in the
* document or was instead defaulted by attribute processing.
*
* @author David Brownell
* @version $Revision$
*/
final
class AttributeSet implements NamedNodeMap, XmlWritable
{
private boolean readonly;
private Vector list;
private ElementNode nameScope;
/* Constructs an attribute list, with associated name scope. */
// package private
AttributeSet (ElementNode nameScope)
{
list = new Vector (5);
this.nameScope = nameScope;
}
/*
* Constructs a copy of an attribute list, for use in cloning.
* name scopes are set separately.
*/
// package private
AttributeSet (AttributeSet original, boolean deep)
{
int size = original.getLength ();
list = new Vector (size);
for (int i = 0; i < size; i++) {
Node node = original.item (i);
if (!(node instanceof AttributeNode))
throw new IllegalArgumentException (((NodeBase)node).
getMessage ("A-003"));
node = node.cloneNode (deep);
// temporarily undo binding to element ... it's rebound
// by the caller
((AttributeNode)node).setNameScope (null);
list.addElement (node);
}
}
// package private
AttributeSet (AttributeList source)
throws DOMException
{
int len = source.getLength ();
AttributeListEx ex = null;
list = new Vector (len);
if (source instanceof AttributeListEx)
ex = (AttributeListEx) source;
for (int i = 0; i < len; i++) {
list.addElement (new AttributeNode (
source.getName (i),
source.getValue (i),
ex == null // remember if it was specified
? true
: ex.isSpecified (i),
ex == null // remember any default value
? null
: ex.getDefault (i)
));
}
list.trimToSize ();
}
/**
* <b>DOM2:</b> Create DOM NamedNodeMap from SAX2 Attributes object
*/
AttributeSet(Attributes source) throws DOMException {
int len = source.getLength();
AttributeListEx ex = null;
list = new Vector(len);
if (source instanceof AttributeListEx) { // XXX fix this
ex = (AttributeListEx) source;
}
for (int i = 0; i < len; i++) {
// Translate "" of SAX2 to null. See DOM2 spec under Node
// namespaceURI
String uri = source.getURI(i);
if (uri.equals("")) {
uri = null;
}
AttributeNode attrNode =
new AttributeNode(uri,
source.getQName(i),
source.getValue(i),
ex == null // remember if it was specified
? true
: ex.isSpecified(i),
ex == null // remember any default value
? null
: ex.getDefault(i));
list.addElement(attrNode);
}
list.trimToSize();
}
// package private
void trimToSize () { list.trimToSize (); }
// package private
public void setReadonly ()
{
readonly = true;
for (int i = 0; i < list.size (); i++)
((AttributeNode)list.elementAt (i)).setReadonly (true);
}
public boolean isReadonly () {
if (readonly)
return true;
for (int i = 0; i < list.size (); i++) {
if (((AttributeNode)list.elementAt (i)).isReadonly ()) {
return true;
}
}
return false;
}
// package private
void setNameScope (ElementNode e)
{
if (e != null && nameScope != null)
throw new IllegalStateException (e.getMessage ("A-004"));
nameScope = e;
// need to bind the attributes to this element
int length = list.size ();
for (int i = 0; i < length; i++) {
AttributeNode node;
node = (AttributeNode) list.elementAt (i);
node.setNameScope (null);
node.setNameScope (e);
}
}
// package private
ElementNode getNameScope ()
{
return nameScope;
}
// package private
String getValue (String name)
{
Attr attr = (Attr) getNamedItem (name);
if (attr == null)
return "";
else
return attr.getValue ();
}
public Node getNamedItem (String name)
{
int length = list.size ();
Node value;
for (int i = 0; i < length; i++) {
value = item (i);
if (value.getNodeName ().equals (name))
return value;
}
return null;
}
/**
* <b>DOM2:</b>
*/
public Node getNamedItemNS(String namespaceURI, String localName) {
for (int i = 0; i < list.size(); i++) {
Node value = item(i);
String iLocalName = value.getLocalName();
if (iLocalName != null && iLocalName.equals(localName)) {
String iNamespaceURI = value.getNamespaceURI();
if (iNamespaceURI != null
&& iNamespaceURI.equals(namespaceURI)) {
return value;
}
}
}
return null;
}
public int getLength ()
{
return list.size ();
}
public Node item (int index)
{
if (index < 0 || index >= list.size ())
return null;
return (Node) list.elementAt (index);
}
public Node removeNamedItem (String name)
throws DOMException
{
int length = list.size ();
Node value;
if (readonly)
throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR);
for (int i = 0; i < length; i++) {
value = item (i);
if (value.getNodeName ().equals (name)) {
AttributeNode att = (AttributeNode) value;
if (att.getDefaultValue () != null) {
att = new AttributeNode (att);
att.setOwnerDocument ((XmlDocument)
nameScope.getOwnerDocument ());
list.setElementAt (att, i);
} else
list.removeElementAt (i);
att.setNameScope (null);
return value;
}
}
throw new DomEx (DomEx.NOT_FOUND_ERR);
}
/**
* <b>DOM2:</b>
*/
public Node removeNamedItemNS(String namespaceURI, String localName)
throws DOMException
{
if (readonly) {
throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
}
for (int i = 0; i < list.size(); i++) {
Node value = item(i);
String iLocalName = value.getLocalName();
if (iLocalName != null && iLocalName.equals(localName)) {
String iNamespaceURI = value.getNamespaceURI();
if (iNamespaceURI != null
&& iNamespaceURI.equals(namespaceURI)) {
AttributeNode attr = (AttributeNode) value;
if (attr.getDefaultValue() != null) {
attr = new AttributeNode(attr);
list.setElementAt(attr, i);
} else {
list.removeElementAt(i);
}
return value;
}
}
}
throw new DomEx(DomEx.NOT_FOUND_ERR);
}
public Node setNamedItem (Node value)
throws DOMException
{
AttributeNode node;
if (readonly)
throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR);
if (!(value instanceof AttributeNode)
|| value.getOwnerDocument ()
!= nameScope.getOwnerDocument ())
throw new DomEx (DomEx.WRONG_DOCUMENT_ERR);
node = (AttributeNode)value;
if (node.getNameScope () != null)
throw new DomEx (DomEx.INUSE_ATTRIBUTE_ERR);
int length = list.size ();
AttributeNode oldValue;
for (int i = 0; i < length; i++) {
oldValue = (AttributeNode) item (i);
if (oldValue.getNodeName ().equals (value.getNodeName ())) {
if (oldValue.isReadonly ())
throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR);
node.setNameScope (nameScope);
list.setElementAt (value, i);
oldValue.setNameScope (null);
return oldValue;
}
}
node.setNameScope (nameScope);
list.addElement (value);
return null;
}
/**
* <b>DOM2:</b>
* XXX spec allows Element nodes also, but this code assumes Attr nodes
* only
*/
public Node setNamedItemNS(Node arg) throws DOMException {
if (readonly) {
throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
}
AttributeNode attr = (AttributeNode) arg;
if (attr.getOwnerElement() != null) {
throw new DomEx(DomEx.INUSE_ATTRIBUTE_ERR);
}
for (int i = 0; i < list.size(); i++) {
AttributeNode oldNode = (AttributeNode) item(i);
String localName = oldNode.getLocalName();
String namespaceURI = oldNode.getNamespaceURI();
if (arg.getLocalName().equals(localName)
&& arg.getNamespaceURI().equals(namespaceURI)) {
// Found a matching node so replace it
list.setElementAt(arg, i);
return oldNode;
}
}
// Append instead of replace
list.addElement(arg);
return null;
}
/**
* Writes out the attribute list. Attributes known to have been
* derived from the DTD are not (at this time) written out. Part
* of writing standalone XML is first ensuring that all attributes
* are flagged as being specified in the "printed" form (or else
* are defaulted only in the internal DTD subset).
*/
public void writeXml (XmlWriteContext context) throws IOException
{
Writer out = context.getWriter ();
int length = list.size ();
AttributeNode tmp;
for (int i = 0; i < length; i++) {
tmp = (AttributeNode) list.elementAt (i);
if (tmp.getSpecified ()) {
out.write (' ');
tmp.writeXml (context);
}
}
}
/**
* Does nothing; this type of node has no children.
*/
public void writeChildrenXml (XmlWriteContext context) throws IOException
{
}
public String toString ()
{
try {
CharArrayWriter w = new CharArrayWriter ();
XmlWriteContext x = new XmlWriteContext (w);
writeXml (x);
return w.toString ();
} catch (IOException e) {
return super.toString ();
}
}
}