KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > dom > TextImpl


1 /*
2  * Copyright 1999-2002,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.xerces.dom;
18
19 import org.w3c.dom.CharacterData JavaDoc;
20 import org.w3c.dom.DOMException JavaDoc;
21 import org.w3c.dom.Node JavaDoc;
22 import org.w3c.dom.Text JavaDoc;
23
24 /**
25  * Text nodes hold the non-markup, non-Entity content of
26  * an Element or Attribute.
27  * <P>
28  * When a document is first made available to the DOM, there is only
29  * one Text object for each block of adjacent plain-text. Users (ie,
30  * applications) may create multiple adjacent Texts during editing --
31  * see {@link org.w3c.dom.Element#normalize} for discussion.
32  * <P>
33  * Note that CDATASection is a subclass of Text. This is conceptually
34  * valid, since they're really just two different ways of quoting
35  * characters when they're written out as part of an XML stream.
36  *
37  * @xerces.internal
38  *
39  * @version $Id: TextImpl.java,v 1.29 2004/11/08 22:21:39 nddelima Exp $
40  * @since PR-DOM-Level-1-19980818.
41  */

42 public class TextImpl
43     extends CharacterDataImpl
44     implements CharacterData JavaDoc, Text JavaDoc {
45
46     //
47
// Private Data members
48
//
49

50     
51     //
52
// Constants
53
//
54

55     /** Serialization version. */
56     static final long serialVersionUID = -5294980852957403469L;
57         
58     //
59
// Constructors
60
//
61

62     /** Default constructor */
63     public TextImpl(){}
64
65     /** Factory constructor. */
66     public TextImpl(CoreDocumentImpl ownerDoc, String JavaDoc data) {
67         super(ownerDoc, data);
68     }
69     
70     /**
71      * NON-DOM: resets node and sets specified values for the current node
72      *
73      * @param ownerDoc
74      * @param data
75      */

76     public void setValues(CoreDocumentImpl ownerDoc, String JavaDoc data){
77
78         flags=0;
79         nextSibling = null;
80         previousSibling=null;
81         setOwnerDocument(ownerDoc);
82         super.data = data;
83     }
84     //
85
// Node methods
86
//
87

88     /**
89      * A short integer indicating what type of node this is. The named
90      * constants for this value are defined in the org.w3c.dom.Node interface.
91      */

92     public short getNodeType() {
93         return Node.TEXT_NODE;
94     }
95
96     /** Returns the node name. */
97     public String JavaDoc getNodeName() {
98         return "#text";
99     }
100
101     /**
102      * NON-DOM: Set whether this Text is ignorable whitespace.
103      */

104     public void setIgnorableWhitespace(boolean ignore) {
105
106         if (needsSyncData()) {
107             synchronizeData();
108         }
109         isIgnorableWhitespace(ignore);
110
111     } // setIgnorableWhitespace(boolean)
112

113
114     /**
115      * DOM L3 Core CR - Experimental
116      *
117      * Returns whether this text node contains
118      * element content whitespace</a>, often abusively called "ignorable whitespace".
119      * The text node is determined to contain whitespace in element content
120      * during the load of the document or if validation occurs while using
121      * <code>Document.normalizeDocument()</code>.
122      * @since DOM Level 3
123      */

124     public boolean isElementContentWhitespace() {
125         // REVISIT: is this implemenation correct?
126
if (needsSyncData()) {
127             synchronizeData();
128         }
129         return internalIsIgnorableWhitespace();
130     }
131
132
133     /**
134      * DOM Level 3 WD - Experimental.
135      * Returns all text of <code>Text</code> nodes logically-adjacent text
136      * nodes to this node, concatenated in document order.
137      * @since DOM Level 3
138      */

139     public String JavaDoc getWholeText(){
140         
141         if (needsSyncData()) {
142             synchronizeData();
143         }
144      
145         if (fBufferStr == null){
146             fBufferStr = new StringBuffer JavaDoc();
147         }
148         else {
149             fBufferStr.setLength(0);
150         }
151         if (data != null && data.length() != 0) {
152             fBufferStr.append(data);
153         }
154         
155         //concatenate text of logically adjacent text nodes to the left of this node in the tree
156
getWholeTextBackward(this.getPreviousSibling(), fBufferStr, this.getParentNode());
157         String JavaDoc temp = fBufferStr.toString();
158       
159         //clear buffer
160
fBufferStr.setLength(0);
161         
162         //concatenate text of logically adjacent text nodes to the right of this node in the tree
163
getWholeTextForward(this.getNextSibling(), fBufferStr, this.getParentNode());
164         
165         return temp + fBufferStr.toString();
166     
167     }
168     
169     /**
170      * internal method taking a StringBuffer in parameter and inserts the
171      * text content at the start of the buffer
172      *
173      * @param buf
174      */

175     protected void insertTextContent(StringBuffer JavaDoc buf) throws DOMException JavaDoc {
176          String JavaDoc content = getNodeValue();
177          if (content != null) {
178              buf.insert(0, content);
179          }
180      }
181
182     /**
183      * Concatenates the text of all logically-adjacent text nodes to the
184      * right of this node
185      * @param node
186      * @param buffer
187      * @param parent
188      * @return true - if execution was stopped because the type of node
189      * other than EntityRef, Text, CDATA is encountered, otherwise
190      * return false
191      */

192     private boolean getWholeTextForward(Node JavaDoc node, StringBuffer JavaDoc buffer, Node JavaDoc parent){
193         // boolean to indicate whether node is a child of an entity reference
194
boolean inEntRef = false;
195         
196         if (parent!=null) {
197             inEntRef = parent.getNodeType()==Node.ENTITY_REFERENCE_NODE;
198         }
199         
200         while (node != null) {
201             short type = node.getNodeType();
202             if (type == Node.ENTITY_REFERENCE_NODE) {
203                 if (getWholeTextForward(node.getFirstChild(), buffer, node)){
204                     return true;
205                 }
206             }
207             else if (type == Node.TEXT_NODE ||
208                      type == Node.CDATA_SECTION_NODE) {
209                 ((NodeImpl)node).getTextContent(buffer);
210             }
211             else {
212                 return true;
213             }
214
215             node = node.getNextSibling();
216         }
217        
218         // if the parent node is an entity reference node, must
219
// check nodes to the right of the parent entity reference node for logically adjacent
220
// text nodes
221
if (inEntRef) {
222             getWholeTextForward(parent.getNextSibling(), buffer, parent.getParentNode());
223                 return true;
224         }
225         
226         return false;
227     }
228     
229     /**
230      * Concatenates the text of all logically-adjacent text nodes to the left of
231      * the node
232      * @param node
233      * @param buffer
234      * @param parent
235      * @return true - if execution was stopped because the type of node
236      * other than EntityRef, Text, CDATA is encountered, otherwise
237      * return false
238      */

239     private boolean getWholeTextBackward(Node JavaDoc node, StringBuffer JavaDoc buffer, Node JavaDoc parent){
240         
241         // boolean to indicate whether node is a child of an entity reference
242
boolean inEntRef = false;
243         if (parent!=null) {
244             inEntRef = parent.getNodeType()==Node.ENTITY_REFERENCE_NODE;
245         }
246         
247         while (node != null) {
248             short type = node.getNodeType();
249             if (type == Node.ENTITY_REFERENCE_NODE) {
250                 if (getWholeTextBackward(node.getLastChild(), buffer, node)){
251                     return true;
252                 }
253             }
254             else if (type == Node.TEXT_NODE ||
255                      type == Node.CDATA_SECTION_NODE) {
256                 ((TextImpl)node).insertTextContent(buffer);
257             }
258             else {
259                 return true;
260             }
261
262             node = node.getPreviousSibling();
263         }
264         
265         // if the parent node is an entity reference node, must
266
// check nodes to the left of the parent entity reference node for logically adjacent
267
// text nodes
268
if (inEntRef) {
269             getWholeTextBackward(parent.getPreviousSibling(), buffer, parent.getParentNode());
270             return true;
271         }
272         
273         return false;
274     }
275
276     /**
277      * Replaces the text of the current node and all logically-adjacent text
278      * nodes with the specified text. All logically-adjacent text nodes are
279      * removed including the current node unless it was the recipient of the
280      * replacement text.
281      *
282      * @param content
283      * The content of the replacing Text node.
284      * @return text - The Text node created with the specified content.
285      * @since DOM Level 3
286      */

287     public Text JavaDoc replaceWholeText(String JavaDoc content) throws DOMException JavaDoc {
288
289         if (needsSyncData()) {
290             synchronizeData();
291         }
292
293         //if the content is null
294
Node JavaDoc parent = this.getParentNode();
295         if (content == null || content.length() == 0) {
296             // remove current node
297
if (parent != null) { // check if node in the tree
298
parent.removeChild(this);
299             }
300             return null;
301         }
302
303         // make sure we can make the replacement
304
if (ownerDocument().errorChecking) {
305             if (!canModifyPrev(this)) {
306                 throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR,
307                         DOMMessageFormatter.formatMessage(
308                                 DOMMessageFormatter.DOM_DOMAIN,
309                                 "NO_MODIFICATION_ALLOWED_ERR", null));
310             }
311             
312             // make sure we can make the replacement
313
if (!canModifyNext(this)) {
314                 throw new DOMException JavaDoc(DOMException.NO_MODIFICATION_ALLOWED_ERR,
315                         DOMMessageFormatter.formatMessage(
316                                 DOMMessageFormatter.DOM_DOMAIN,
317                                 "NO_MODIFICATION_ALLOWED_ERR", null));
318             }
319         }
320
321         //replace the text node
322
Text JavaDoc currentNode = null;
323         if (isReadOnly()) {
324             Text JavaDoc newNode = this.ownerDocument().createTextNode(content);
325             if (parent != null) { // check if node in the tree
326
parent.insertBefore(newNode, this);
327                 parent.removeChild(this);
328                 currentNode = newNode;
329             } else {
330                 return newNode;
331             }
332         } else {
333             this.setData(content);
334             currentNode = this;
335         }
336
337         //check logically-adjacent text nodes
338
Node JavaDoc prev = currentNode.getPreviousSibling();
339         while (prev != null) {
340             //If the logically-adjacent next node can be removed
341
//remove it. A logically adjacent node can be removed if
342
//it is a Text or CDATASection node or an EntityReference with
343
//Text and CDATA only children.
344
if ((prev.getNodeType() == Node.TEXT_NODE)
345                     || (prev.getNodeType() == Node.CDATA_SECTION_NODE)
346                     || (prev.getNodeType() == Node.ENTITY_REFERENCE_NODE && hasTextOnlyChildren(prev))) {
347                 parent.removeChild(prev);
348                 prev = currentNode;
349             } else {
350                 break;
351             }
352             prev = prev.getPreviousSibling();
353         }
354
355         //check logically-adjacent text nodes
356
Node JavaDoc next = currentNode.getNextSibling();
357         while (next != null) {
358             //If the logically-adjacent next node can be removed
359
//remove it. A logically adjacent node can be removed if
360
//it is a Text or CDATASection node or an EntityReference with
361
//Text and CDATA only children.
362
if ((next.getNodeType() == Node.TEXT_NODE)
363                     || (next.getNodeType() == Node.CDATA_SECTION_NODE)
364                     || (next.getNodeType() == Node.ENTITY_REFERENCE_NODE && hasTextOnlyChildren(next))) {
365                 parent.removeChild(next);
366                 next = currentNode;
367             } else {
368                 break;
369             }
370             next = next.getNextSibling();
371         }
372
373         return currentNode;
374     }
375
376     /**
377      * If any EntityReference to be removed has descendants that are not
378      * EntityReference, Text, or CDATASection nodes, the replaceWholeText method
379      * must fail before performing any modification of the document, raising a
380      * DOMException with the code NO_MODIFICATION_ALLOWED_ERR. Traverse previous
381      * siblings of the node to be replaced. If a previous sibling is an
382      * EntityReference node, get it's last child. If the last child was a Text
383      * or CDATASection node and its previous siblings are neither a replaceable
384      * EntityReference or Text or CDATASection nodes, return false. IF the last
385      * child was neither Text nor CDATASection nor a replaceable EntityReference
386      * Node, then return true. If the last child was a Text or CDATASection node
387      * any its previous sibling was not or was an EntityReference that did not
388      * contain only Text or CDATASection nodes, return false. Check this
389      * recursively for EntityReference nodes.
390      *
391      * @param node
392      * @return true - can replace text false - can't replace exception must be
393      * raised
394      */

395     private boolean canModifyPrev(Node JavaDoc node) {
396         boolean textLastChild = false;
397
398         Node JavaDoc prev = node.getPreviousSibling();
399
400         while (prev != null) {
401
402             short type = prev.getNodeType();
403
404             if (type == Node.ENTITY_REFERENCE_NODE) {
405                 //If the previous sibling was entityreference
406
//check if its content is replaceable
407
Node JavaDoc lastChild = prev.getLastChild();
408
409                 //if the entity reference has no children
410
//return false
411
if (lastChild == null) {
412                     return false;
413                 }
414
415                 //The replacement text of the entity reference should
416
//be either only text,cadatsections or replaceable entity
417
//reference nodes or the last child should be neither of these
418
while (lastChild != null) {
419                     short lType = lastChild.getNodeType();
420
421                     if (lType == Node.TEXT_NODE
422                             || lType == Node.CDATA_SECTION_NODE) {
423                         textLastChild = true;
424                     } else if (lType == Node.ENTITY_REFERENCE_NODE) {
425                         if (!canModifyPrev(lastChild)) {
426                             return false;
427                         } else {
428                             //If the EntityReference child contains
429
//only text, or non-text or ends with a
430
//non-text node.
431
textLastChild = true;
432                         }
433                     } else {
434                         //If the last child was replaceable and others are not
435
//Text or CDataSection or replaceable EntityRef nodes
436
//return false.
437
if (textLastChild) {
438                             return false;
439                         } else {
440                             return true;
441                         }
442                     }
443                     lastChild = lastChild.getPreviousSibling();
444                 }
445             } else if (type == Node.TEXT_NODE
446                     || type == Node.CDATA_SECTION_NODE) {
447                 //If the previous sibling was text or cdatasection move to next
448
} else {
449                 //If the previous sibling was anything but text or
450
//cdatasection or an entity reference, stop search and
451
//return true
452
return true;
453             }
454
455             prev = prev.getPreviousSibling();
456         }
457
458         return true;
459     }
460
461     /**
462      * If any EntityReference to be removed has descendants that are not
463      * EntityReference, Text, or CDATASection nodes, the replaceWholeText method
464      * must fail before performing any modification of the document, raising a
465      * DOMException with the code NO_MODIFICATION_ALLOWED_ERR. Traverse previous
466      * siblings of the node to be replaced. If a previous sibling is an
467      * EntityReference node, get it's last child. If the first child was a Text
468      * or CDATASection node and its next siblings are neither a replaceable
469      * EntityReference or Text or CDATASection nodes, return false. IF the first
470      * child was neither Text nor CDATASection nor a replaceable EntityReference
471      * Node, then return true. If the first child was a Text or CDATASection
472      * node any its next sibling was not or was an EntityReference that did not
473      * contain only Text or CDATASection nodes, return false. Check this
474      * recursively for EntityReference nodes.
475      *
476      * @param node
477      * @return true - can replace text false - can't replace exception must be
478      * raised
479      */

480     private boolean canModifyNext(Node JavaDoc node) {
481         boolean textFirstChild = false;
482
483         Node JavaDoc next = node.getNextSibling();
484         while (next != null) {
485
486             short type = next.getNodeType();
487
488             if (type == Node.ENTITY_REFERENCE_NODE) {
489                 //If the previous sibling was entityreference
490
//check if its content is replaceable
491
Node JavaDoc firstChild = next.getFirstChild();
492
493                 //if the entity reference has no children
494
//return false
495
if (firstChild == null) {
496                     return false;
497                 }
498
499                 //The replacement text of the entity reference should
500
//be either only text,cadatsections or replaceable entity
501
//reference nodes or the last child should be neither of these
502
while (firstChild != null) {
503                     short lType = firstChild.getNodeType();
504
505                     if (lType == Node.TEXT_NODE
506                             || lType == Node.CDATA_SECTION_NODE) {
507                         textFirstChild = true;
508                     } else if (lType == Node.ENTITY_REFERENCE_NODE) {
509                         if (!canModifyNext(firstChild)) {
510                             return false;
511                         } else {
512                             //If the EntityReference child contains
513
//only text, or non-text or ends with a
514
//non-text node.
515
textFirstChild = true;
516                         }
517                     } else {
518                         //If the first child was replaceable text and next
519
//children are not, then return false
520
if (textFirstChild) {
521                             return false;
522                         } else {
523                             return true;
524                         }
525                     }
526                     firstChild = firstChild.getNextSibling();
527                 }
528             } else if (type == Node.TEXT_NODE
529                     || type == Node.CDATA_SECTION_NODE) {
530                 //If the previous sibling was text or cdatasection move to next
531
} else {
532                 //If the next sibling was anything but text or
533
//cdatasection or an entity reference, stop search and
534
//return true
535
return true;
536             }
537
538             next = next.getNextSibling();
539         }
540
541         return true;
542     }
543
544     /**
545      * Check if an EntityReference node has Text Only child nodes
546      *
547      * @param node
548      * @return true - Contains text only children
549      */

550     private boolean hasTextOnlyChildren(Node JavaDoc node) {
551
552         Node JavaDoc child = node;
553         
554         if (child == null) {
555             return false;
556         }
557         
558         child = child.getFirstChild();
559         while (child != null) {
560             int type = child.getNodeType();
561             
562             if (type == Node.ENTITY_REFERENCE_NODE) {
563                 return hasTextOnlyChildren(child);
564             }
565             else if (type != Node.TEXT_NODE
566                     && type != Node.CDATA_SECTION_NODE
567                     && type != Node.ENTITY_REFERENCE_NODE) {
568                 return false;
569             }
570             child = child.getNextSibling();
571         }
572         return true;
573     }
574     
575     
576     /**
577      * NON-DOM: Returns whether this Text is ignorable whitespace.
578      */

579     public boolean isIgnorableWhitespace() {
580
581         if (needsSyncData()) {
582             synchronizeData();
583         }
584         return internalIsIgnorableWhitespace();
585
586     } // isIgnorableWhitespace():boolean
587

588     
589     //
590
// Text methods
591
//
592

593     /**
594      * Break a text node into two sibling nodes. (Note that if the current node
595      * has no parent, they won't wind up as "siblings" -- they'll both be
596      * orphans.)
597      *
598      * @param offset
599      * The offset at which to split. If offset is at the end of the
600      * available data, the second node will be empty.
601      *
602      * @return A reference to the new node (containing data after the offset
603      * point). The original node will contain data up to that point.
604      *
605      * @throws DOMException(INDEX_SIZE_ERR)
606      * if offset is <0 or >length.
607      *
608      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR)
609      * if node is read-only.
610      */

611     public Text JavaDoc splitText(int offset)
612         throws DOMException JavaDoc {
613
614         if (isReadOnly()) {
615             throw new DOMException JavaDoc(
616             DOMException.NO_MODIFICATION_ALLOWED_ERR,
617                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null));
618         }
619
620         if (needsSyncData()) {
621             synchronizeData();
622         }
623         if (offset < 0 || offset > data.length() ) {
624             throw new DOMException JavaDoc(DOMException.INDEX_SIZE_ERR,
625                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null));
626         }
627             
628         // split text into two separate nodes
629
Text JavaDoc newText =
630             getOwnerDocument().createTextNode(data.substring(offset));
631         setNodeValue(data.substring(0, offset));
632
633         // insert new text node
634
Node JavaDoc parentNode = getParentNode();
635         if (parentNode != null) {
636             parentNode.insertBefore(newText, nextSibling);
637         }
638
639         return newText;
640
641     } // splitText(int):Text
642

643     
644     /**
645      * NON-DOM (used by DOMParser): Reset data for the node.
646      */

647     public void replaceData (String JavaDoc value){
648         data = value;
649     }
650
651
652     /**
653      * NON-DOM (used by DOMParser: Sets data to empty string.
654      * Returns the value the data was set to.
655      */

656     public String JavaDoc removeData (){
657         String JavaDoc olddata=data;
658         data = "";
659         return olddata;
660     }
661
662 } // class TextImpl
663
Popular Tags