1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xerces.internal.dom; 23 24 import org.w3c.dom.DOMException; 25 import org.w3c.dom.Node; 26 import org.w3c.dom.NodeList; 27 28 /** 29 * CharacterData is an abstract Node that can carry character data as its 30 * Value. It provides shared behavior for Text, CData, and 31 * possibly other node types. All offsets are 0-based. 32 * <p> 33 * Since ProcessingInstructionImpl inherits from this class to reuse the 34 * setNodeValue method, this class isn't declared as implementing the interface 35 * CharacterData. This is done by relevant subclasses (TexImpl, CommentImpl). 36 * <p> 37 * This class doesn't directly support mutation events, however, it notifies 38 * the document when mutations are performed so that the document class do so. 39 * 40 * @xerces.internal 41 * 42 * @since PR-DOM-Level-1-19980818. 43 */ 44 public abstract class CharacterDataImpl 45 extends ChildNode { 46 47 // 48 // Constants 49 // 50 51 /** Serialization version. */ 52 static final long serialVersionUID = 7931170150428474230L; 53 54 // 55 // Data 56 // 57 58 protected String data; 59 60 /** Empty child nodes. */ 61 private static transient NodeList singletonNodeList = new NodeList() { 62 public Node item(int index) { return null; } 63 public int getLength() { return 0; } 64 }; 65 66 // 67 // Constructors 68 // 69 CharacterDataImpl()70 public CharacterDataImpl(){} 71 72 /** Factory constructor. */ CharacterDataImpl(CoreDocumentImpl ownerDocument, String data)73 protected CharacterDataImpl(CoreDocumentImpl ownerDocument, String data) { 74 super(ownerDocument); 75 this.data = data; 76 } 77 78 // 79 // Node methods 80 // 81 82 /** Returns an empty node list. */ getChildNodes()83 public NodeList getChildNodes() { 84 return singletonNodeList; 85 } 86 87 /* 88 * returns the content of this node 89 */ getNodeValue()90 public String getNodeValue() { 91 if (needsSyncData()) { 92 synchronizeData(); 93 } 94 return data; 95 } 96 97 /** Convenience wrapper for calling setNodeValueInternal when 98 * we are not performing a replacement operation 99 */ setNodeValueInternal(String value)100 protected void setNodeValueInternal (String value) { 101 setNodeValueInternal(value, false); 102 } 103 104 /** This function added so that we can distinguish whether 105 * setNodeValue has been called from some other DOM functions. 106 * or by the client.<p> 107 * This is important, because we do one type of Range fix-up, 108 * from the high-level functions in CharacterData, and another 109 * type if the client simply calls setNodeValue(value). 110 */ setNodeValueInternal(String value, boolean replace)111 protected void setNodeValueInternal(String value, boolean replace) { 112 113 CoreDocumentImpl ownerDocument = ownerDocument(); 114 115 if (ownerDocument.errorChecking && isReadOnly()) { 116 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 117 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 118 } 119 120 // revisit: may want to set the value in ownerDocument. 121 // Default behavior, overridden in some subclasses 122 if (needsSyncData()) { 123 synchronizeData(); 124 } 125 126 // keep old value for document notification 127 String oldvalue = this.data; 128 129 // notify document 130 ownerDocument.modifyingCharacterData(this, replace); 131 132 this.data = value; 133 134 // notify document 135 ownerDocument.modifiedCharacterData(this, oldvalue, value, replace); 136 } 137 138 /** 139 * Sets the content, possibly firing related events, 140 * and updating ranges (via notification to the document) 141 */ setNodeValue(String value)142 public void setNodeValue(String value) { 143 144 setNodeValueInternal(value); 145 146 // notify document 147 ownerDocument().replacedText(this); 148 } 149 150 // 151 // CharacterData methods 152 // 153 154 /** 155 * Retrieve character data currently stored in this node. 156 * 157 * @throws DOMExcpetion(DOMSTRING_SIZE_ERR) In some implementations, 158 * the stored data may exceed the permitted length of strings. If so, 159 * getData() will throw this DOMException advising the user to 160 * instead retrieve the data in chunks via the substring() operation. 161 */ getData()162 public String getData() { 163 if (needsSyncData()) { 164 synchronizeData(); 165 } 166 return data; 167 } 168 169 /** 170 * Report number of characters currently stored in this node's 171 * data. It may be 0, meaning that the value is an empty string. 172 */ getLength()173 public int getLength() { 174 if (needsSyncData()) { 175 synchronizeData(); 176 } 177 return data.length(); 178 } 179 180 /** 181 * Concatenate additional characters onto the end of the data 182 * stored in this node. Note that this, and insert(), are the paths 183 * by which a DOM could wind up accumulating more data than the 184 * language's strings can easily handle. (See above discussion.) 185 * 186 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly. 187 */ appendData(String data)188 public void appendData(String data) { 189 190 if (isReadOnly()) { 191 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 192 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 193 } 194 if (data == null) { 195 return; 196 } 197 if (needsSyncData()) { 198 synchronizeData(); 199 } 200 201 setNodeValue(this.data + data); 202 203 } // appendData(String) 204 205 /** 206 * Remove a range of characters from the node's value. Throws a 207 * DOMException if the offset is beyond the end of the 208 * string. However, a deletion _count_ that exceeds the available 209 * data is accepted as a delete-to-end request. 210 * 211 * @throws DOMException(INDEX_SIZE_ERR) if offset is negative or 212 * greater than length, or if count is negative. 213 * 214 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is 215 * readonly. 216 */ deleteData(int offset, int count)217 public void deleteData(int offset, int count) 218 throws DOMException { 219 220 internalDeleteData(offset, count, false); 221 } // deleteData(int,int) 222 223 224 /** NON-DOM INTERNAL: Within DOM actions, we sometimes need to be able 225 * to control which mutation events are spawned. This version of the 226 * deleteData operation allows us to do so. It is not intended 227 * for use by application programs. 228 */ internalDeleteData(int offset, int count, boolean replace)229 void internalDeleteData (int offset, int count, boolean replace) 230 throws DOMException { 231 232 CoreDocumentImpl ownerDocument = ownerDocument(); 233 if (ownerDocument.errorChecking) { 234 if (isReadOnly()) { 235 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 236 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 237 } 238 239 if (count < 0) { 240 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); 241 throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); 242 } 243 } 244 245 if (needsSyncData()) { 246 synchronizeData(); 247 } 248 int tailLength = Math.max(data.length() - count - offset, 0); 249 try { 250 String value = data.substring(0, offset) + 251 (tailLength > 0 ? data.substring(offset + count, offset + count + tailLength) : ""); 252 253 setNodeValueInternal(value, replace); 254 255 // notify document 256 ownerDocument.deletedText(this, offset, count); 257 } 258 catch (StringIndexOutOfBoundsException e) { 259 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); 260 throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); 261 } 262 263 } // internalDeleteData(int,int,boolean) 264 265 /** 266 * Insert additional characters into the data stored in this node, 267 * at the offset specified. 268 * 269 * @throws DOMException(INDEX_SIZE_ERR) if offset is negative or 270 * greater than length. 271 * 272 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly. 273 */ insertData(int offset, String data)274 public void insertData(int offset, String data) 275 throws DOMException { 276 277 internalInsertData(offset, data, false); 278 279 } // insertData(int,int) 280 281 282 283 /** NON-DOM INTERNAL: Within DOM actions, we sometimes need to be able 284 * to control which mutation events are spawned. This version of the 285 * insertData operation allows us to do so. It is not intended 286 * for use by application programs. 287 */ internalInsertData(int offset, String data, boolean replace)288 void internalInsertData (int offset, String data, boolean replace) 289 throws DOMException { 290 291 CoreDocumentImpl ownerDocument = ownerDocument(); 292 293 if (ownerDocument.errorChecking && isReadOnly()) { 294 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 295 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 296 } 297 298 if (needsSyncData()) { 299 synchronizeData(); 300 } 301 try { 302 String value = 303 new StringBuffer(this.data).insert(offset, data).toString(); 304 305 306 setNodeValueInternal(value, replace); 307 308 // notify document 309 ownerDocument.insertedText(this, offset, data.length()); 310 } 311 catch (StringIndexOutOfBoundsException e) { 312 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); 313 throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); 314 } 315 316 } // internalInsertData(int,String,boolean) 317 318 319 320 /** 321 * Replace a series of characters at the specified (zero-based) 322 * offset with a new string, NOT necessarily of the same 323 * length. Convenience method, equivalent to a delete followed by an 324 * insert. Throws a DOMException if the specified offset is beyond 325 * the end of the existing data. 326 * 327 * @param offset The offset at which to begin replacing. 328 * 329 * @param count The number of characters to remove, 330 * interpreted as in the delete() method. 331 * 332 * @param data The new string to be inserted at offset in place of 333 * the removed data. Note that the entire string will 334 * be inserted -- the count parameter does not affect 335 * insertion, and the new data may be longer or shorter 336 * than the substring it replaces. 337 * 338 * @throws DOMException(INDEX_SIZE_ERR) if offset is negative or 339 * greater than length, or if count is negative. 340 * 341 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is 342 * readonly. 343 */ replaceData(int offset, int count, String data)344 public void replaceData(int offset, int count, String data) 345 throws DOMException { 346 347 CoreDocumentImpl ownerDocument = ownerDocument(); 348 349 // The read-only check is done by deleteData() 350 // ***** This could be more efficient w/r/t Mutation Events, 351 // specifically by aggregating DOMAttrModified and 352 // DOMSubtreeModified. But mutation events are 353 // underspecified; I don't feel compelled 354 // to deal with it right now. 355 if (ownerDocument.errorChecking && isReadOnly()) { 356 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 357 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 358 } 359 360 if (needsSyncData()) { 361 synchronizeData(); 362 } 363 364 //notify document 365 ownerDocument.replacingData(this); 366 367 // keep old value for document notification 368 String oldvalue = this.data; 369 370 internalDeleteData(offset, count, true); 371 internalInsertData(offset, data, true); 372 373 ownerDocument.replacedCharacterData(this, oldvalue, this.data); 374 375 } // replaceData(int,int,String) 376 377 /** 378 * Store character data into this node. 379 * 380 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is readonly. 381 */ setData(String value)382 public void setData(String value) 383 throws DOMException { 384 setNodeValue(value); 385 } 386 387 /** 388 * Substring is more than a convenience function. In some 389 * implementations of the DOM, where the stored data may exceed the 390 * length that can be returned in a single string, the only way to 391 * read it all is to extract it in chunks via this method. 392 * 393 * @param offset Zero-based offset of first character to retrieve. 394 * @param count Number of characters to retrieve. 395 * 396 * If the sum of offset and count exceeds the length, all characters 397 * to end of data are returned. 398 * 399 * @throws DOMException(INDEX_SIZE_ERR) if offset is negative or 400 * greater than length, or if count is negative. 401 * 402 * @throws DOMException(WSTRING_SIZE_ERR) In some implementations, 403 * count may exceed the permitted length of strings. If so, 404 * substring() will throw this DOMException advising the user to 405 * instead retrieve the data in smaller chunks. 406 */ substringData(int offset, int count)407 public String substringData(int offset, int count) 408 throws DOMException { 409 410 if (needsSyncData()) { 411 synchronizeData(); 412 } 413 414 int length = data.length(); 415 if (count < 0 || offset < 0 || offset > length - 1) { 416 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null); 417 throw new DOMException(DOMException.INDEX_SIZE_ERR, msg); 418 } 419 420 int tailIndex = Math.min(offset + count, length); 421 422 return data.substring(offset, tailIndex); 423 424 } // substringData(int,int):String 425 426 } // class CharacterDataImpl 427