/src/xerces-c/src/xercesc/dom/impl/DOMTextImpl.cpp
Line | Count | Source |
1 | | /* |
2 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
3 | | * contributor license agreements. See the NOTICE file distributed with |
4 | | * this work for additional information regarding copyright ownership. |
5 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
6 | | * (the "License"); you may not use this file except in compliance with |
7 | | * the License. You may obtain a copy of the License at |
8 | | * |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | * |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | /* |
19 | | * $Id: DOMTextImpl.cpp 1800911 2017-07-05 18:52:15Z scantor $ |
20 | | */ |
21 | | |
22 | | |
23 | | #include <xercesc/util/XMLUniDefs.hpp> |
24 | | |
25 | | #include <xercesc/dom/DOMException.hpp> |
26 | | #include <xercesc/dom/DOMNode.hpp> |
27 | | #include <xercesc/dom/DOMElement.hpp> |
28 | | #include <xercesc/dom/DOMCDATASection.hpp> |
29 | | #include <xercesc/dom/DOMNodeFilter.hpp> |
30 | | #include <xercesc/dom/DOMTreeWalker.hpp> |
31 | | |
32 | | #include "DOMDocumentImpl.hpp" |
33 | | #include "DOMStringPool.hpp" |
34 | | #include "DOMTextImpl.hpp" |
35 | | #include "DOMCharacterDataImpl.hpp" |
36 | | #include "DOMChildNode.hpp" |
37 | | #include "DOMRangeImpl.hpp" |
38 | | #include "DOMCasts.hpp" |
39 | | |
40 | | #include <assert.h> |
41 | | |
42 | | XERCES_CPP_NAMESPACE_BEGIN |
43 | | |
44 | | class DOMDocument; |
45 | | |
46 | | DOMTextImpl::DOMTextImpl(DOMDocument *ownerDoc, const XMLCh *dat) |
47 | 0 | : fNode(this, ownerDoc), fCharacterData(ownerDoc, dat) |
48 | 0 | { |
49 | 0 | fNode.setIsLeafNode(true); |
50 | 0 | } |
51 | | |
52 | | DOMTextImpl:: |
53 | | DOMTextImpl(DOMDocument *ownerDoc, const XMLCh* dat, XMLSize_t n) |
54 | 0 | : fNode(this, ownerDoc), fCharacterData(ownerDoc, dat, n) |
55 | 0 | { |
56 | 0 | fNode.setIsLeafNode(true); |
57 | 0 | } |
58 | | |
59 | | DOMTextImpl::DOMTextImpl(const DOMTextImpl &other, bool) |
60 | 0 | : DOMText(other) |
61 | 0 | , fNode(this, other.fNode) |
62 | 0 | , fCharacterData(other.fCharacterData) |
63 | 0 | { |
64 | 0 | fNode.setIsLeafNode(true); |
65 | 0 | } |
66 | | |
67 | | DOMTextImpl::~DOMTextImpl() |
68 | 0 | { |
69 | 0 | } |
70 | | |
71 | | |
72 | | DOMNode *DOMTextImpl::cloneNode(bool deep) const |
73 | 0 | { |
74 | 0 | DOMNode* newNode = new (getOwnerDocument(), DOMMemoryManager::TEXT_OBJECT) DOMTextImpl(*this, deep); |
75 | 0 | fNode.callUserDataHandlers(DOMUserDataHandler::NODE_CLONED, this, newNode); |
76 | 0 | return newNode; |
77 | 0 | } |
78 | | |
79 | | |
80 | 0 | const XMLCh * DOMTextImpl::getNodeName() const { |
81 | 0 | static const XMLCh gtext[] = {chPound, chLatin_t, chLatin_e, chLatin_x, chLatin_t, chNull}; |
82 | 0 | return gtext; |
83 | 0 | } |
84 | | |
85 | 0 | DOMNode::NodeType DOMTextImpl::getNodeType() const { |
86 | 0 | return DOMNode::TEXT_NODE; |
87 | 0 | } |
88 | | |
89 | | |
90 | | DOMText *DOMTextImpl::splitText(XMLSize_t offset) |
91 | 0 | { |
92 | 0 | if (fNode.isReadOnly()) |
93 | 0 | { |
94 | 0 | throw DOMException( |
95 | 0 | DOMException::NO_MODIFICATION_ALLOWED_ERR, 0, GetDOMNodeMemoryManager); |
96 | 0 | } |
97 | 0 | XMLSize_t len = fCharacterData.fDataBuf->getLen(); |
98 | 0 | if (offset > len) |
99 | 0 | throw DOMException(DOMException::INDEX_SIZE_ERR, 0, GetDOMNodeMemoryManager); |
100 | | |
101 | 0 | DOMDocumentImpl *doc = (DOMDocumentImpl *)getOwnerDocument(); |
102 | 0 | DOMText *newText = doc->createTextNode( |
103 | 0 | this->substringData(offset, len - offset)); |
104 | |
|
105 | 0 | DOMNode *parent = getParentNode(); |
106 | 0 | if (parent != 0) |
107 | 0 | parent->insertBefore(newText, getNextSibling()); |
108 | |
|
109 | 0 | fCharacterData.fDataBuf->chop(offset); |
110 | |
|
111 | 0 | if (doc != 0) { |
112 | 0 | Ranges* ranges = doc->getRanges(); |
113 | 0 | if (ranges != 0) { |
114 | 0 | XMLSize_t sz = ranges->size(); |
115 | 0 | if (sz != 0) { |
116 | 0 | for (XMLSize_t i =0; i<sz; i++) { |
117 | 0 | ranges->elementAt(i)->updateSplitInfo( this, newText, offset); |
118 | 0 | } |
119 | 0 | } |
120 | 0 | } |
121 | 0 | } |
122 | |
|
123 | 0 | return newText; |
124 | 0 | } |
125 | | |
126 | | |
127 | | bool DOMTextImpl::isIgnorableWhitespace() const |
128 | 0 | { |
129 | 0 | return fNode.ignorableWhitespace(); |
130 | 0 | } |
131 | | |
132 | | |
133 | | |
134 | | void DOMTextImpl::setIgnorableWhitespace(bool ignorable) |
135 | 0 | { |
136 | 0 | fNode.ignorableWhitespace(ignorable); |
137 | 0 | } |
138 | | |
139 | | |
140 | | bool DOMTextImpl::getIsElementContentWhitespace() const |
141 | 0 | { |
142 | 0 | return isIgnorableWhitespace(); |
143 | 0 | } |
144 | | |
145 | | const XMLCh* DOMTextImpl::getWholeText() const |
146 | 0 | { |
147 | 0 | DOMDocument *doc = getOwnerDocument(); |
148 | 0 | if (!doc) { |
149 | 0 | throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0, GetDOMNodeMemoryManager); |
150 | 0 | return 0; |
151 | 0 | } |
152 | 0 | DOMNode* root=doc->getDocumentElement(); |
153 | 0 | DOMTreeWalker* pWalker=doc->createTreeWalker(root!=NULL?root:(DOMNode*)this, DOMNodeFilter::SHOW_ALL, NULL, true); |
154 | 0 | pWalker->setCurrentNode((DOMNode*)this); |
155 | | // Logically-adjacent text nodes are Text or CDATASection nodes that can be visited sequentially in document order or in |
156 | | // reversed document order without entering, exiting, or passing over Element, Comment, or ProcessingInstruction nodes. |
157 | 0 | DOMNode* prevNode; |
158 | 0 | while((prevNode=pWalker->previousNode())!=NULL) |
159 | 0 | { |
160 | 0 | if(prevNode->getNodeType()==ELEMENT_NODE || prevNode->getNodeType()==COMMENT_NODE || prevNode->getNodeType()==PROCESSING_INSTRUCTION_NODE) |
161 | 0 | break; |
162 | 0 | } |
163 | 0 | XMLBuffer buff(1023, GetDOMNodeMemoryManager); |
164 | 0 | DOMNode* nextNode; |
165 | 0 | while((nextNode=pWalker->nextNode())!=NULL) |
166 | 0 | { |
167 | 0 | if(nextNode->getNodeType()==ELEMENT_NODE || nextNode->getNodeType()==COMMENT_NODE || nextNode->getNodeType()==PROCESSING_INSTRUCTION_NODE) |
168 | 0 | break; |
169 | 0 | if(nextNode->getNodeType()==TEXT_NODE || nextNode->getNodeType()==CDATA_SECTION_NODE) |
170 | 0 | buff.append(nextNode->getNodeValue()); |
171 | 0 | } |
172 | 0 | pWalker->release(); |
173 | |
|
174 | 0 | XMLCh* wholeString = (XMLCh*)((DOMDocumentImpl*)doc)->allocate((buff.getLen()+1) * sizeof(XMLCh)); |
175 | 0 | XMLString::copyString(wholeString, buff.getRawBuffer()); |
176 | 0 | return wholeString; |
177 | 0 | } |
178 | | |
179 | | DOMText* DOMTextImpl::replaceWholeText(const XMLCh* newText) |
180 | 0 | { |
181 | 0 | DOMDocument *doc = getOwnerDocument(); |
182 | 0 | DOMTreeWalker* pWalker=doc->createTreeWalker(doc->getDocumentElement(), DOMNodeFilter::SHOW_ALL, NULL, true); |
183 | 0 | pWalker->setCurrentNode((DOMNode*)this); |
184 | | // Logically-adjacent text nodes are Text or CDATASection nodes that can be visited sequentially in document order or in |
185 | | // reversed document order without entering, exiting, or passing over Element, Comment, or ProcessingInstruction nodes. |
186 | 0 | DOMNode* pFirstTextNode=this; |
187 | 0 | DOMNode* prevNode; |
188 | 0 | while((prevNode=pWalker->previousNode())!=NULL) |
189 | 0 | { |
190 | 0 | if(prevNode->getNodeType()==ELEMENT_NODE || prevNode->getNodeType()==COMMENT_NODE || prevNode->getNodeType()==PROCESSING_INSTRUCTION_NODE) |
191 | 0 | break; |
192 | 0 | pFirstTextNode=prevNode; |
193 | 0 | } |
194 | | // before doing any change we need to check if we are going to remove an entity reference that doesn't contain just text |
195 | 0 | DOMNode* pCurrentNode=pWalker->getCurrentNode(); |
196 | 0 | DOMNode* nextNode; |
197 | 0 | while((nextNode=pWalker->nextNode())!=NULL) |
198 | 0 | { |
199 | 0 | if(nextNode->getNodeType()==ELEMENT_NODE || nextNode->getNodeType()==COMMENT_NODE || nextNode->getNodeType()==PROCESSING_INSTRUCTION_NODE) |
200 | 0 | break; |
201 | 0 | if(nextNode->getNodeType()==ENTITY_REFERENCE_NODE) |
202 | 0 | { |
203 | 0 | DOMTreeWalker* pInnerWalker=doc->createTreeWalker(nextNode, DOMNodeFilter::SHOW_ALL, NULL, true); |
204 | 0 | while(pInnerWalker->nextNode()) |
205 | 0 | { |
206 | 0 | short nodeType=pInnerWalker->getCurrentNode()->getNodeType(); |
207 | 0 | if(nodeType!=ENTITY_REFERENCE_NODE && nodeType!=TEXT_NODE && nodeType!=CDATA_SECTION_NODE) |
208 | 0 | throw DOMException(DOMException::NO_MODIFICATION_ALLOWED_ERR, 0, GetDOMNodeMemoryManager); |
209 | 0 | } |
210 | 0 | pInnerWalker->release(); |
211 | 0 | } |
212 | 0 | } |
213 | 0 | DOMText* retVal=NULL; |
214 | | // if the first node in the chain is a text node, replace its content, otherwise create a new node |
215 | 0 | if(newText && *newText) |
216 | 0 | { |
217 | 0 | if(!castToNodeImpl(pFirstTextNode)->isReadOnly() && (pFirstTextNode->getNodeType()==TEXT_NODE || pFirstTextNode->getNodeType()==CDATA_SECTION_NODE)) |
218 | 0 | { |
219 | 0 | pFirstTextNode->setNodeValue(newText); |
220 | 0 | retVal=(DOMText*)pFirstTextNode; |
221 | 0 | } |
222 | 0 | else |
223 | 0 | { |
224 | 0 | if(getNodeType()==TEXT_NODE) |
225 | 0 | retVal=doc->createTextNode(newText); |
226 | 0 | else |
227 | 0 | retVal=doc->createCDATASection(newText); |
228 | 0 | pFirstTextNode->getParentNode()->insertBefore(retVal, pFirstTextNode); |
229 | 0 | } |
230 | 0 | } |
231 | | // now delete all the following text nodes |
232 | 0 | pWalker->setCurrentNode(pCurrentNode); |
233 | 0 | while((nextNode=pWalker->nextNode())!=NULL) |
234 | 0 | { |
235 | 0 | if(nextNode->getNodeType()==ELEMENT_NODE || nextNode->getNodeType()==COMMENT_NODE || nextNode->getNodeType()==PROCESSING_INSTRUCTION_NODE) |
236 | 0 | break; |
237 | 0 | if(nextNode!=retVal) |
238 | 0 | { |
239 | | // keep the tree walker valid |
240 | 0 | pWalker->previousNode(); |
241 | 0 | nextNode->getParentNode()->removeChild(nextNode); |
242 | 0 | nextNode->release(); |
243 | 0 | } |
244 | 0 | } |
245 | 0 | pWalker->release(); |
246 | 0 | return retVal; |
247 | 0 | } |
248 | | |
249 | | |
250 | | void DOMTextImpl::release() |
251 | 0 | { |
252 | 0 | if (fNode.isOwned() && !fNode.isToBeReleased()) |
253 | 0 | throw DOMException(DOMException::INVALID_ACCESS_ERR,0, GetDOMNodeMemoryManager); |
254 | | |
255 | 0 | DOMDocumentImpl* doc = (DOMDocumentImpl*) getOwnerDocument(); |
256 | 0 | if (doc) { |
257 | 0 | fNode.callUserDataHandlers(DOMUserDataHandler::NODE_DELETED, 0, 0); |
258 | 0 | fCharacterData.releaseBuffer(); |
259 | 0 | doc->release(this, DOMMemoryManager::TEXT_OBJECT); |
260 | 0 | } |
261 | 0 | else { |
262 | | // shouldn't reach here |
263 | 0 | throw DOMException(DOMException::INVALID_ACCESS_ERR,0, GetDOMNodeMemoryManager); |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | | // |
268 | | // Delegation functions |
269 | | // |
270 | 0 | DOMNode* DOMTextImpl::appendChild(DOMNode *newChild) {return fNode.appendChild (newChild); } |
271 | 0 | DOMNamedNodeMap* DOMTextImpl::getAttributes() const {return fNode.getAttributes (); } |
272 | 0 | DOMNodeList* DOMTextImpl::getChildNodes() const {return fNode.getChildNodes (); } |
273 | 0 | DOMNode* DOMTextImpl::getFirstChild() const {return fNode.getFirstChild (); } |
274 | 0 | DOMNode* DOMTextImpl::getLastChild() const {return fNode.getLastChild (); } |
275 | 0 | const XMLCh* DOMTextImpl::getLocalName() const {return fNode.getLocalName (); } |
276 | 0 | const XMLCh* DOMTextImpl::getNamespaceURI() const {return fNode.getNamespaceURI (); } |
277 | 0 | DOMNode* DOMTextImpl::getNextSibling() const {return fChild.getNextSibling (); } |
278 | 0 | const XMLCh* DOMTextImpl::getNodeValue() const {return fCharacterData.getNodeValue (); } |
279 | 0 | DOMDocument* DOMTextImpl::getOwnerDocument() const {return fNode.getOwnerDocument (); } |
280 | 0 | const XMLCh* DOMTextImpl::getPrefix() const {return fNode.getPrefix (); } |
281 | 0 | DOMNode* DOMTextImpl::getParentNode() const {return fChild.getParentNode (this); } |
282 | 0 | DOMNode* DOMTextImpl::getPreviousSibling() const {return fChild.getPreviousSibling (this); } |
283 | 0 | bool DOMTextImpl::hasChildNodes() const {return fNode.hasChildNodes (); } |
284 | | DOMNode* DOMTextImpl::insertBefore(DOMNode *newChild, DOMNode *refChild) |
285 | 0 | {return fNode.insertBefore (newChild, refChild); } |
286 | 0 | void DOMTextImpl::normalize() {fNode.normalize (); } |
287 | 0 | DOMNode* DOMTextImpl::removeChild(DOMNode *oldChild) {return fNode.removeChild (oldChild); } |
288 | | DOMNode* DOMTextImpl::replaceChild(DOMNode *newChild, DOMNode *oldChild) |
289 | 0 | {return fNode.replaceChild (newChild, oldChild); } |
290 | | bool DOMTextImpl::isSupported(const XMLCh *feature, const XMLCh *version) const |
291 | 0 | {return fNode.isSupported (feature, version); } |
292 | 0 | void DOMTextImpl::setPrefix(const XMLCh *prefix) {fNode.setPrefix(prefix); } |
293 | 0 | bool DOMTextImpl::hasAttributes() const {return fNode.hasAttributes(); } |
294 | 0 | bool DOMTextImpl::isSameNode(const DOMNode* other) const {return fNode.isSameNode(other); } |
295 | 0 | bool DOMTextImpl::isEqualNode(const DOMNode* arg) const {return fNode.isEqualNode(arg); } |
296 | | void* DOMTextImpl::setUserData(const XMLCh* key, void* data, DOMUserDataHandler* handler) |
297 | 0 | {return fNode.setUserData(key, data, handler); } |
298 | 0 | void* DOMTextImpl::getUserData(const XMLCh* key) const {return fNode.getUserData(key); } |
299 | 0 | const XMLCh* DOMTextImpl::getBaseURI() const {return fNode.getBaseURI(); } |
300 | 0 | short DOMTextImpl::compareDocumentPosition(const DOMNode* other) const {return fNode.compareDocumentPosition(other); } |
301 | 0 | const XMLCh* DOMTextImpl::getTextContent() const {return fNode.getTextContent(); } |
302 | 0 | void DOMTextImpl::setTextContent(const XMLCh* textContent){fNode.setTextContent(textContent); } |
303 | 0 | const XMLCh* DOMTextImpl::lookupPrefix(const XMLCh* namespaceURI) const {return fNode.lookupPrefix(namespaceURI); } |
304 | 0 | bool DOMTextImpl::isDefaultNamespace(const XMLCh* namespaceURI) const {return fNode.isDefaultNamespace(namespaceURI); } |
305 | 0 | const XMLCh* DOMTextImpl::lookupNamespaceURI(const XMLCh* prefix) const {return fNode.lookupNamespaceURI(prefix); } |
306 | 0 | void* DOMTextImpl::getFeature(const XMLCh* feature, const XMLCh* version) const {return fNode.getFeature(feature, version); } |
307 | | |
308 | | |
309 | | // Macro-in implementation accessors. |
310 | | DOMNODEIMPL_IMPL(DOMTextImpl); |
311 | | DOMCHILDIMPL_IMPL(DOMTextImpl); |
312 | | |
313 | | |
314 | | // |
315 | | // Delegation of CharacerData functions. |
316 | | // |
317 | | |
318 | | |
319 | 0 | const XMLCh* DOMTextImpl::getData() const {return fCharacterData.getData();} |
320 | 0 | XMLSize_t DOMTextImpl::getLength() const {return fCharacterData.getLength();} |
321 | | const XMLCh* DOMTextImpl::substringData(XMLSize_t offset, XMLSize_t count) const |
322 | 0 | {return fCharacterData.substringData(this, offset, count);} |
323 | 0 | void DOMTextImpl::appendData(const XMLCh *arg) {fCharacterData.appendData(this, arg);} |
324 | | void DOMTextImpl::insertData(XMLSize_t offset, const XMLCh *arg) |
325 | 0 | {fCharacterData.insertData(this, offset, arg);} |
326 | | void DOMTextImpl::deleteData(XMLSize_t offset, XMLSize_t count) |
327 | 0 | {fCharacterData.deleteData(this, offset, count);} |
328 | | void DOMTextImpl::replaceData(XMLSize_t offset, XMLSize_t count, const XMLCh *arg) |
329 | 0 | {fCharacterData.replaceData(this, offset, count, arg);} |
330 | 0 | void DOMTextImpl::setData(const XMLCh *data) {fCharacterData.setData(this, data);} |
331 | 0 | void DOMTextImpl::setNodeValue(const XMLCh *nodeValue) {fCharacterData.setNodeValue (this, nodeValue); } |
332 | | |
333 | 0 | void DOMTextImpl::appendData(const XMLCh *arg, XMLSize_t n) {fCharacterData.appendData(this, arg, n);} |
334 | 0 | void DOMTextImpl::appendDataFast(const XMLCh *arg, XMLSize_t n) {fCharacterData.appendDataFast(this, arg, n);} |
335 | | |
336 | | XERCES_CPP_NAMESPACE_END |