All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.htmlunit.cyberneko.xerces.dom.CharacterDataImpl Maven / Gradle / Ivy

There is a newer version: 6.0.36
Show newest version
/*
 * Copyright 2017-2023 Ronald Brill
 *
 * 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
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.htmlunit.cyberneko.xerces.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. */ public abstract class CharacterDataImpl extends ChildNode { protected String data_; /** Empty child nodes. */ private static final NodeList singletonNodeList = new NodeList() { @Override public Node item(final int index) { return null; } @Override public int getLength() { return 0; } }; /** * Factory constructor. * * @param ownerDocument the owner document * @param data the data */ protected CharacterDataImpl(final CoreDocumentImpl ownerDocument, final String data) { super(ownerDocument); data_ = data; } /** * {@inheritDoc} */ @Override public NodeList getChildNodes() { return singletonNodeList; } /* * {@inheritDoc} */ @Override public String getNodeValue() { if (needsSyncData()) { synchronizeData(); } return data_; } /** * Convenience wrapper for calling setNodeValueInternal when we are not * performing a replacement operation * * @param value the value */ protected void setNodeValueInternal(final String value) { setNodeValueInternal(value, false); } /** * 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). * * @param value the value * @param replace flag to signal replace */ protected void setNodeValueInternal(final String value, final boolean replace) { final CoreDocumentImpl ownerDocument = ownerDocument(); // revisit: may want to set the value in ownerDocument. // Default behavior, overridden in some subclasses if (needsSyncData()) { synchronizeData(); } // keep old value for document notification final String oldvalue = this.data_; // notify document ownerDocument.modifyingCharacterData(this, replace); this.data_ = value; // notify document ownerDocument.modifiedCharacterData(this, oldvalue, value, replace); } /** * {@inheritDoc} * * Sets the content, possibly firing related events, and updating ranges (via * notification to the document) */ @Override public void setNodeValue(final String value) { setNodeValueInternal(value); // notify document ownerDocument().replacedText(this); } /** * @return character data currently stored in this node. */ public String getData() { if (needsSyncData()) { synchronizeData(); } return data_; } /** * {@inheritDoc} * * Report number of characters currently stored in this node's data. It may be * 0, meaning that the value is an empty string. */ @Override 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.) * * @param data the data */ public void appendData(final String data) { if (data == null) { return; } if (needsSyncData()) { synchronizeData(); } setNodeValue(this.data_ + data); } /** * 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. * * @param offset the offser * @param count the count * * @throws DOMException INDEX_SIZE_ERR if offset is negative or greater than * length, or if count is negative. */ public void deleteData(final int offset, final int count) throws DOMException { internalDeleteData(offset, count, false); } // NON-DOM INTERNAL: Within DOM actions, we sometimes need to be able // to control which mutation events are spawned. This version of the // deleteData operation allows us to do so. It is not intended // for use by application programs. void internalDeleteData(final int offset, final int count, final boolean replace) throws DOMException { final CoreDocumentImpl ownerDocument = ownerDocument(); if (ownerDocument.errorChecking) { if (count < 0) { final String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); } } if (needsSyncData()) { synchronizeData(); } final int tailLength = Math.max(data_.length() - count - offset, 0); try { final String value = data_.substring(0, offset) + (tailLength > 0 ? data_.substring(offset + count, offset + count + tailLength) : ""); setNodeValueInternal(value, replace); // notify document ownerDocument.deletedText(this, offset, count); } catch (final StringIndexOutOfBoundsException e) { final String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); } } /** * Insert additional characters into the data stored in this node, at the offset * specified. * * @param offset the offset * @param data the data * * @throws DOMException INDEX_SIZE_ERR if offset is negative or greater than * length. */ public void insertData(final int offset, final String data) throws DOMException { internalInsertData(offset, data, false); } // NON-DOM INTERNAL: Within DOM actions, we sometimes need to be able // to control which mutation events are spawned. This version of the // insertData operation allows us to do so. It is not intended // for use by application programs. void internalInsertData(final int offset, final String data, final boolean replace) throws DOMException { final CoreDocumentImpl ownerDocument = ownerDocument(); if (needsSyncData()) { synchronizeData(); } try { final String value = new StringBuilder(this.data_).insert(offset, data).toString(); setNodeValueInternal(value, replace); // notify document ownerDocument.insertedText(this, offset, data.length()); } catch (final StringIndexOutOfBoundsException e) { final String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); } } /** * 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. */ public void replaceData(final int offset, final int count, final String data) throws DOMException { final CoreDocumentImpl ownerDocument = ownerDocument(); if (needsSyncData()) { synchronizeData(); } // notify document ownerDocument.replacingData(this); // keep old value for document notification final String oldvalue = this.data_; internalDeleteData(offset, count, true); internalInsertData(offset, data, true); ownerDocument.replacedCharacterData(this, oldvalue, this.data_); } /** * Store character data into this node. * * @param value the value */ public void setData(final 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. * @return the substring * * @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(final int offset, final int count) throws DOMException { if (needsSyncData()) { synchronizeData(); } final int length = data_.length(); if (count < 0 || offset < 0 || offset > length - 1) { final String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); } final int tailIndex = Math.min(offset + count, length); return data_.substring(offset, tailIndex); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy