/* * The Apache Software License, Version 1.1 * * * Copyright (c) 1999-2002 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 * . */ package com.sun.org.apache.xerces.internal.dom; import org.w3c.dom.DOMException; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * 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. *

* Since 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). *

* This class doesn't directly support mutation events, however, it notifies * the document when mutations are performed so that the document class do so. * * @version $Id: CharacterDataImpl.java,v 1.20 2003/05/08 19:52:40 elena Exp $ * @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 // public CharacterDataImpl(){} /** Factory constructor. */ protected CharacterDataImpl(CoreDocumentImpl 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.

* 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). */ protected void setNodeValueInternal(String value) { if (isReadOnly()) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); } // revisit: may want to set the value in ownerDocument. // Default behavior, overridden in some subclasses if (needsSyncData()) { synchronizeData(); } // keep old value for document notification String oldvalue = this.data; CoreDocumentImpl ownerDocument = ownerDocument(); // notify document ownerDocument.modifyingCharacterData(this); this.data = value; // notify document ownerDocument.modifiedCharacterData(this, oldvalue, value); } /** * Sets the content, possibly firing related events, * and updating ranges (via notification to the document) */ public void setNodeValue(String value) { setNodeValueInternal(value); // notify document ownerDocument().replacedText(this); } // // 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()) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); } if (data == null) { return; } if (needsSyncData()) { synchronizeData(); } 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()) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); } if (count < 0) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); } if (needsSyncData()) { synchronizeData(); } int tailLength = Math.max(data.length() - count - offset, 0); try { String value = data.substring(0, offset) + (tailLength > 0 ? data.substring(offset + count, offset + count + tailLength) : ""); setNodeValueInternal(value); // notify document ownerDocument().deletedText(this, offset, count); } catch (StringIndexOutOfBoundsException e) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); } } // 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()) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); } if (needsSyncData()) { synchronizeData(); } try { String value = new StringBuffer(this.data).insert(offset, data).toString(); setNodeValueInternal(value); // notify document ownerDocument().insertedText(this, offset, data.length()); } catch (StringIndexOutOfBoundsException e) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); } } // 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) { String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); } int tailIndex = Math.min(offset + count, length); return data.substring(offset, tailIndex); } // substringData(int,int):String } // class CharacterDataImpl