286N/A/*
286N/A * reserved comment block
286N/A * DO NOT REMOVE OR ALTER!
286N/A */
286N/A/*
286N/A * Copyright 1999-2002,2004 The Apache Software Foundation.
286N/A *
286N/A * Licensed under the Apache License, Version 2.0 (the "License");
286N/A * you may not use this file except in compliance with the License.
286N/A * You may obtain a copy of the License at
286N/A *
286N/A * http://www.apache.org/licenses/LICENSE-2.0
286N/A *
286N/A * Unless required by applicable law or agreed to in writing, software
286N/A * distributed under the License is distributed on an "AS IS" BASIS,
286N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
286N/A * See the License for the specific language governing permissions and
286N/A * limitations under the License.
286N/A */
286N/A
286N/Apackage com.sun.org.apache.xerces.internal.dom;
286N/A
286N/Aimport org.w3c.dom.DOMException;
286N/Aimport org.w3c.dom.Node;
286N/Aimport org.w3c.dom.NodeList;
286N/A
286N/A/**
286N/A * CharacterData is an abstract Node that can carry character data as its
286N/A * Value. It provides shared behavior for Text, CData, and
286N/A * possibly other node types. All offsets are 0-based.
286N/A * <p>
286N/A * Since ProcessingInstructionImpl inherits from this class to reuse the
286N/A * setNodeValue method, this class isn't declared as implementing the interface
286N/A * CharacterData. This is done by relevant subclasses (TexImpl, CommentImpl).
286N/A * <p>
286N/A * This class doesn't directly support mutation events, however, it notifies
286N/A * the document when mutations are performed so that the document class do so.
286N/A *
286N/A * @xerces.internal
286N/A *
286N/A * @since PR-DOM-Level-1-19980818.
286N/A */
286N/Apublic abstract class CharacterDataImpl
286N/A extends ChildNode {
286N/A
286N/A //
286N/A // Constants
286N/A //
286N/A
286N/A /** Serialization version. */
286N/A static final long serialVersionUID = 7931170150428474230L;
286N/A
286N/A //
286N/A // Data
286N/A //
286N/A
286N/A protected String data;
286N/A
286N/A /** Empty child nodes. */
286N/A private static transient NodeList singletonNodeList = new NodeList() {
286N/A public Node item(int index) { return null; }
286N/A public int getLength() { return 0; }
286N/A };
286N/A
286N/A //
286N/A // Constructors
286N/A //
286N/A
286N/A public CharacterDataImpl(){}
286N/A
286N/A /** Factory constructor. */
286N/A protected CharacterDataImpl(CoreDocumentImpl ownerDocument, String data) {
286N/A super(ownerDocument);
286N/A this.data = data;
286N/A }
286N/A
286N/A //
286N/A // Node methods
286N/A //
286N/A
286N/A /** Returns an empty node list. */
286N/A public NodeList getChildNodes() {
286N/A return singletonNodeList;
286N/A }
286N/A
286N/A /*
286N/A * returns the content of this node
286N/A */
286N/A public String getNodeValue() {
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A return data;
286N/A }
286N/A
286N/A /** Convenience wrapper for calling setNodeValueInternal when
286N/A * we are not performing a replacement operation
286N/A */
286N/A protected void setNodeValueInternal (String value) {
286N/A setNodeValueInternal(value, false);
286N/A }
286N/A
286N/A /** This function added so that we can distinguish whether
286N/A * setNodeValue has been called from some other DOM functions.
286N/A * or by the client.<p>
286N/A * This is important, because we do one type of Range fix-up,
286N/A * from the high-level functions in CharacterData, and another
286N/A * type if the client simply calls setNodeValue(value).
286N/A */
286N/A protected void setNodeValueInternal(String value, boolean replace) {
286N/A
286N/A CoreDocumentImpl ownerDocument = ownerDocument();
286N/A
286N/A if (ownerDocument.errorChecking && isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A
286N/A // revisit: may want to set the value in ownerDocument.
286N/A // Default behavior, overridden in some subclasses
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A
286N/A // keep old value for document notification
286N/A String oldvalue = this.data;
286N/A
286N/A // notify document
286N/A ownerDocument.modifyingCharacterData(this, replace);
286N/A
286N/A this.data = value;
286N/A
286N/A // notify document
286N/A ownerDocument.modifiedCharacterData(this, oldvalue, value, replace);
286N/A }
286N/A
286N/A /**
286N/A * Sets the content, possibly firing related events,
286N/A * and updating ranges (via notification to the document)
286N/A */
286N/A public void setNodeValue(String value) {
286N/A
286N/A setNodeValueInternal(value);
286N/A
286N/A // notify document
286N/A ownerDocument().replacedText(this);
286N/A }
286N/A
286N/A //
286N/A // CharacterData methods
286N/A //
286N/A
286N/A /**
286N/A * Retrieve character data currently stored in this node.
286N/A *
286N/A * @throws DOMExcpetion(DOMSTRING_SIZE_ERR) In some implementations,
286N/A * the stored data may exceed the permitted length of strings. If so,
286N/A * getData() will throw this DOMException advising the user to
286N/A * instead retrieve the data in chunks via the substring() operation.
286N/A */
286N/A public String getData() {
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A return data;
286N/A }
286N/A
286N/A /**
286N/A * Report number of characters currently stored in this node's
286N/A * data. It may be 0, meaning that the value is an empty string.
286N/A */
286N/A public int getLength() {
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A return data.length();
286N/A }
286N/A
286N/A /**
286N/A * Concatenate additional characters onto the end of the data
286N/A * stored in this node. Note that this, and insert(), are the paths
286N/A * by which a DOM could wind up accumulating more data than the
286N/A * language's strings can easily handle. (See above discussion.)
286N/A *
286N/A * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly.
286N/A */
286N/A public void appendData(String data) {
286N/A
286N/A if (isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A if (data == null) {
286N/A return;
286N/A }
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A
286N/A setNodeValue(this.data + data);
286N/A
286N/A } // appendData(String)
286N/A
286N/A /**
286N/A * Remove a range of characters from the node's value. Throws a
286N/A * DOMException if the offset is beyond the end of the
286N/A * string. However, a deletion _count_ that exceeds the available
286N/A * data is accepted as a delete-to-end request.
286N/A *
286N/A * @throws DOMException(INDEX_SIZE_ERR) if offset is negative or
286N/A * greater than length, or if count is negative.
286N/A *
286N/A * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is
286N/A * readonly.
286N/A */
286N/A public void deleteData(int offset, int count)
286N/A throws DOMException {
286N/A
286N/A internalDeleteData(offset, count, false);
286N/A } // deleteData(int,int)
286N/A
286N/A
286N/A /** NON-DOM INTERNAL: Within DOM actions, we sometimes need to be able
286N/A * to control which mutation events are spawned. This version of the
286N/A * deleteData operation allows us to do so. It is not intended
286N/A * for use by application programs.
286N/A */
286N/A void internalDeleteData (int offset, int count, boolean replace)
286N/A throws DOMException {
286N/A
286N/A CoreDocumentImpl ownerDocument = ownerDocument();
286N/A if (ownerDocument.errorChecking) {
286N/A if (isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A
286N/A if (count < 0) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null);
286N/A throw new DOMException(DOMException.INDEX_SIZE_ERR, msg);
286N/A }
286N/A }
286N/A
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A int tailLength = Math.max(data.length() - count - offset, 0);
286N/A try {
286N/A String value = data.substring(0, offset) +
286N/A (tailLength > 0 ? data.substring(offset + count, offset + count + tailLength) : "");
286N/A
286N/A setNodeValueInternal(value, replace);
286N/A
286N/A // notify document
286N/A ownerDocument.deletedText(this, offset, count);
286N/A }
286N/A catch (StringIndexOutOfBoundsException e) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null);
286N/A throw new DOMException(DOMException.INDEX_SIZE_ERR, msg);
286N/A }
286N/A
286N/A } // internalDeleteData(int,int,boolean)
286N/A
286N/A /**
286N/A * Insert additional characters into the data stored in this node,
286N/A * at the offset specified.
286N/A *
286N/A * @throws DOMException(INDEX_SIZE_ERR) if offset is negative or
286N/A * greater than length.
286N/A *
286N/A * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly.
286N/A */
286N/A public void insertData(int offset, String data)
286N/A throws DOMException {
286N/A
286N/A internalInsertData(offset, data, false);
286N/A
286N/A } // insertData(int,int)
286N/A
286N/A
286N/A
286N/A /** NON-DOM INTERNAL: Within DOM actions, we sometimes need to be able
286N/A * to control which mutation events are spawned. This version of the
286N/A * insertData operation allows us to do so. It is not intended
286N/A * for use by application programs.
286N/A */
286N/A void internalInsertData (int offset, String data, boolean replace)
286N/A throws DOMException {
286N/A
286N/A CoreDocumentImpl ownerDocument = ownerDocument();
286N/A
286N/A if (ownerDocument.errorChecking && isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A try {
286N/A String value =
286N/A new StringBuffer(this.data).insert(offset, data).toString();
286N/A
286N/A
286N/A setNodeValueInternal(value, replace);
286N/A
286N/A // notify document
286N/A ownerDocument.insertedText(this, offset, data.length());
286N/A }
286N/A catch (StringIndexOutOfBoundsException e) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null);
286N/A throw new DOMException(DOMException.INDEX_SIZE_ERR, msg);
286N/A }
286N/A
286N/A } // internalInsertData(int,String,boolean)
286N/A
286N/A
286N/A
286N/A /**
286N/A * Replace a series of characters at the specified (zero-based)
286N/A * offset with a new string, NOT necessarily of the same
286N/A * length. Convenience method, equivalent to a delete followed by an
286N/A * insert. Throws a DOMException if the specified offset is beyond
286N/A * the end of the existing data.
286N/A *
286N/A * @param offset The offset at which to begin replacing.
286N/A *
286N/A * @param count The number of characters to remove,
286N/A * interpreted as in the delete() method.
286N/A *
286N/A * @param data The new string to be inserted at offset in place of
286N/A * the removed data. Note that the entire string will
286N/A * be inserted -- the count parameter does not affect
286N/A * insertion, and the new data may be longer or shorter
286N/A * than the substring it replaces.
286N/A *
286N/A * @throws DOMException(INDEX_SIZE_ERR) if offset is negative or
286N/A * greater than length, or if count is negative.
286N/A *
286N/A * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is
286N/A * readonly.
286N/A */
286N/A public void replaceData(int offset, int count, String data)
286N/A throws DOMException {
286N/A
286N/A CoreDocumentImpl ownerDocument = ownerDocument();
286N/A
286N/A // The read-only check is done by deleteData()
286N/A // ***** This could be more efficient w/r/t Mutation Events,
286N/A // specifically by aggregating DOMAttrModified and
286N/A // DOMSubtreeModified. But mutation events are
286N/A // underspecified; I don't feel compelled
286N/A // to deal with it right now.
286N/A if (ownerDocument.errorChecking && isReadOnly()) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
286N/A throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
286N/A }
286N/A
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A
286N/A //notify document
286N/A ownerDocument.replacingData(this);
286N/A
286N/A // keep old value for document notification
286N/A String oldvalue = this.data;
286N/A
286N/A internalDeleteData(offset, count, true);
286N/A internalInsertData(offset, data, true);
286N/A
286N/A ownerDocument.replacedCharacterData(this, oldvalue, this.data);
286N/A
286N/A } // replaceData(int,int,String)
286N/A
286N/A /**
286N/A * Store character data into this node.
286N/A *
286N/A * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly.
286N/A */
286N/A public void setData(String value)
286N/A throws DOMException {
286N/A setNodeValue(value);
286N/A }
286N/A
286N/A /**
286N/A * Substring is more than a convenience function. In some
286N/A * implementations of the DOM, where the stored data may exceed the
286N/A * length that can be returned in a single string, the only way to
286N/A * read it all is to extract it in chunks via this method.
286N/A *
286N/A * @param offset Zero-based offset of first character to retrieve.
286N/A * @param count Number of characters to retrieve.
286N/A *
286N/A * If the sum of offset and count exceeds the length, all characters
286N/A * to end of data are returned.
286N/A *
286N/A * @throws DOMException(INDEX_SIZE_ERR) if offset is negative or
286N/A * greater than length, or if count is negative.
286N/A *
286N/A * @throws DOMException(WSTRING_SIZE_ERR) In some implementations,
286N/A * count may exceed the permitted length of strings. If so,
286N/A * substring() will throw this DOMException advising the user to
286N/A * instead retrieve the data in smaller chunks.
286N/A */
286N/A public String substringData(int offset, int count)
286N/A throws DOMException {
286N/A
286N/A if (needsSyncData()) {
286N/A synchronizeData();
286N/A }
286N/A
286N/A int length = data.length();
286N/A if (count < 0 || offset < 0 || offset > length - 1) {
286N/A String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null);
286N/A throw new DOMException(DOMException.INDEX_SIZE_ERR, msg);
286N/A }
286N/A
286N/A int tailIndex = Math.min(offset + count, length);
286N/A
286N/A return data.substring(offset, tailIndex);
286N/A
286N/A } // substringData(int,int):String
286N/A
286N/A} // class CharacterDataImpl