blob: ee17c3a8569770745f032499ebcb89da5de43fc2 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 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 "Xerces" 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, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xerces.dom;
import org.w3c.dom.*;
import org.apache.xerces.dom.events.MutationEventImpl;
import org.w3c.dom.events.*;
/**
* CharacterData is an abstract Node that can carry character data as its
* Value. It provides shared behavior for Text, CData, and
* possibly other node types. All offsets are 0-based.
* <p>
* This implementation includes support for DOM Level 2 Mutation Events.
* If the static boolean NodeImpl.MUTATIONEVENTS is not set true, that support
* is disabled and can be optimized out to reduce code size.
*
* Since this ProcessingInstructionImpl inherits from this class to reuse the
* setNodeValue method, this class isn't declared as implementing the interface
* CharacterData. This is done by relevant subclasses (TexImpl, CommentImpl).
*
* @version
* @since PR-DOM-Level-1-19980818.
*/
public abstract class CharacterDataImpl
extends ChildNode {
//
// Constants
//
/** Serialization version. */
static final long serialVersionUID = 7931170150428474230L;
//
// Data
//
protected String data;
/** Empty child nodes. */
private static transient NodeList singletonNodeList = new NodeList() {
public Node item(int index) { return null; }
public int getLength() { return 0; }
};
//
// Constructors
//
/** Factory constructor. */
protected CharacterDataImpl(DocumentImpl ownerDocument, String data) {
super(ownerDocument);
this.data = data;
}
//
// Node methods
//
/** Returns an empty node list. */
public NodeList getChildNodes() {
return singletonNodeList;
}
/*
* returns the content of this node
*/
public String getNodeValue() {
if (needsSyncData()) {
synchronizeData();
}
return data;
}
/** This function added so that we can distinguish whether
* setNodeValue has been called from some other DOM functions.
* or by the client.<p>
* This is important, because we do one type of Range fix-up,
* from the high-level functions in CharacterData, and another
* type if the client simply calls setNodeValue(value).
*/
void setNodeValueInternal(String value) {
/** flag to indicate whether setNodeValue was called by the
* client or from the DOM.
*/
setValueCalled(true);
setNodeValue(value);
setValueCalled(false);
}
/**
* Sets the content, possibly firing related events,
* and updating ranges (via notification to the document)
*/
public void setNodeValue(String value) {
if (isReadOnly())
throw new DOMExceptionImpl(
DOMException.NO_MODIFICATION_ALLOWED_ERR,
"DOM001 Modification not allowed");
// revisit: may want to set the value in ownerDocument.
// Default behavior, overridden in some subclasses
if (needsSyncData()) {
synchronizeData();
}
// Cache old value for DOMCharacterDataModified.
String oldvalue = this.data;
EnclosingAttr enclosingAttr=null;
if(MUTATIONEVENTS)
{
// MUTATION PREPROCESSING AND PRE-EVENTS:
// If we're within the scope of an Attr and DOMAttrModified
// was requested, we need to preserve its previous value for
// that event.
LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
if(lc.captures+lc.bubbles+lc.defaults>0)
{
enclosingAttr=getEnclosingAttr();
}
} // End mutation preprocessing
this.data = value;
if (!setValueCalled()) {
// notify document
ownerDocument().replacedText(this);
}
if(MUTATIONEVENTS)
{
// MUTATION POST-EVENTS:
LCount lc =
LCount.lookup(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED);
if(lc.captures+lc.bubbles+lc.defaults>0)
{
MutationEvent me= new MutationEventImpl();
//?????ownerDocument.createEvent("MutationEvents");
me.initMutationEvent(
MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
true,false,null,oldvalue,value,null);
dispatchEvent(me);
}
// Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
// if required. (Common to most kinds of mutation)
dispatchAggregateEvents(enclosingAttr);
} // End mutation postprocessing
} // setNodeValue(String)
//
// CharacterData methods
//
/**
* Retrieve character data currently stored in this node.
*
* @throws DOMExcpetion(DOMSTRING_SIZE_ERR) In some implementations,
* the stored data may exceed the permitted length of strings. If so,
* getData() will throw this DOMException advising the user to
* instead retrieve the data in chunks via the substring() operation.
*/
public String getData() {
if (needsSyncData()) {
synchronizeData();
}
return data;
}
/**
* Report number of characters currently stored in this node's
* data. It may be 0, meaning that the value is an empty string.
*/
public int getLength() {
if (needsSyncData()) {
synchronizeData();
}
return data.length();
}
/**
* Concatenate additional characters onto the end of the data
* stored in this node. Note that this, and insert(), are the paths
* by which a DOM could wind up accumulating more data than the
* language's strings can easily handle. (See above discussion.)
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly.
*/
public void appendData(String data) {
if (isReadOnly()) {
throw new DOMExceptionImpl(
DOMException.NO_MODIFICATION_ALLOWED_ERR,
"DOM001 Modification not allowed");
}
if (needsSyncData()) {
synchronizeData();
}
// Handles mutation event generation, if any
setNodeValue(this.data + data);
} // appendData(String)
/**
* Remove a range of characters from the node's value. Throws a
* DOMException if the offset is beyond the end of the
* string. However, a deletion _count_ that exceeds the available
* data is accepted as a delete-to-end request.
*
* @throws DOMException(INDEX_SIZE_ERR) if offset is negative or
* greater than length, or if count is negative.
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is
* readonly.
*/
public void deleteData(int offset, int count)
throws DOMException {
if (isReadOnly()) {
throw new DOMExceptionImpl(
DOMException.NO_MODIFICATION_ALLOWED_ERR,
"DOM001 Modification not allowed");
}
if (count < 0) {
throw new DOMExceptionImpl(DOMException.INDEX_SIZE_ERR,
"DOM004 Index out of bounds");
}
if (needsSyncData()) {
synchronizeData();
}
int tailLength = Math.max(data.length() - count - offset, 0);
try {
// Handles mutation event generation, if any
setNodeValueInternal(data.substring(0, offset) +
(tailLength > 0
? data.substring(offset + count, offset + count + tailLength)
: "") );
// notify document
ownerDocument().deletedText(this, offset, count);
}
catch (StringIndexOutOfBoundsException e) {
throw new DOMExceptionImpl(DOMException.INDEX_SIZE_ERR,
"DOM004 Index out of bounds");
}
} // deleteData(int,int)
/**
* Insert additional characters into the data stored in this node,
* at the offset specified.
*
* @throws DOMException(INDEX_SIZE_ERR) if offset is negative or
* greater than length.
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly.
*/
public void insertData(int offset, String data)
throws DOMException {
if (isReadOnly()) {
throw new DOMExceptionImpl(
DOMException.NO_MODIFICATION_ALLOWED_ERR,
"DOM001 Modification not allowed");
}
if (needsSyncData()) {
synchronizeData();
}
try {
// Handles mutation event generation, if any
setNodeValueInternal(
new StringBuffer(this.data).insert(offset, data).toString()
);
// notify document
ownerDocument().insertedText(this, offset, data.length());
}
catch (StringIndexOutOfBoundsException e) {
throw new DOMExceptionImpl(DOMException.INDEX_SIZE_ERR,
"DOM004 Index out of bounds");
}
} // insertData(int,int)
/**
* Replace a series of characters at the specified (zero-based)
* offset with a new string, NOT necessarily of the same
* length. Convenience method, equivalent to a delete followed by an
* insert. Throws a DOMException if the specified offset is beyond
* the end of the existing data.
*
* @param offset The offset at which to begin replacing.
*
* @param count The number of characters to remove,
* interpreted as in the delete() method.
*
* @param data The new string to be inserted at offset in place of
* the removed data. Note that the entire string will
* be inserted -- the count parameter does not affect
* insertion, and the new data may be longer or shorter
* than the substring it replaces.
*
* @throws DOMException(INDEX_SIZE_ERR) if offset is negative or
* greater than length, or if count is negative.
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is
* readonly.
*/
public void replaceData(int offset, int count, String data)
throws DOMException {
// The read-only check is done by deleteData()
// ***** This could be more efficient w/r/t Mutation Events,
// specifically by aggregating DOMAttrModified and
// DOMSubtreeModified. But mutation events are
// underspecified; I don't feel compelled
// to deal with it right now.
deleteData(offset, count);
insertData(offset, data);
} // replaceData(int,int,String)
/**
* Store character data into this node.
*
* @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly.
*/
public void setData(String value)
throws DOMException {
setNodeValue(value);
}
/**
* Substring is more than a convenience function. In some
* implementations of the DOM, where the stored data may exceed the
* length that can be returned in a single string, the only way to
* read it all is to extract it in chunks via this method.
*
* @param offset Zero-based offset of first character to retrieve.
* @param count Number of characters to retrieve.
*
* If the sum of offset and count exceeds the length, all characters
* to end of data are returned.
*
* @throws DOMException(INDEX_SIZE_ERR) if offset is negative or
* greater than length, or if count is negative.
*
* @throws DOMException(WSTRING_SIZE_ERR) In some implementations,
* count may exceed the permitted length of strings. If so,
* substring() will throw this DOMException advising the user to
* instead retrieve the data in smaller chunks.
*/
public String substringData(int offset, int count)
throws DOMException {
if (needsSyncData()) {
synchronizeData();
}
int length = data.length();
if (count < 0 || offset < 0 || offset > length - 1) {
throw new DOMExceptionImpl(DOMException.INDEX_SIZE_ERR,
"DOM004 Index out of bounds");
}
int tailIndex = Math.min(offset + count, length);
return data.substring(offset, tailIndex);
} // substringData(int,int):String
} // class CharacterDataImpl