blob: 51032e142839634a05b77ae11294eda3ac0c4c45 [file] [log] [blame]
david_williams96213482004-11-11 09:07:12 +00001/*******************************************************************************
2 * Copyright (c) 2001, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Jens Lukowski/Innoopract - initial renaming/restructuring
11 *
12 *******************************************************************************/
13package org.eclipse.wst.xml.core.internal.document;
14
15
16
17import java.util.Enumeration;
18import java.util.Iterator;
19import java.util.Vector;
20
david_williams4ad020f2005-04-18 08:00:30 +000021import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
22import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList;
23import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
24import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
david_williams56777022005-04-11 06:21:55 +000025import org.eclipse.wst.xml.core.internal.commentelement.impl.CommentElementConfiguration;
26import org.eclipse.wst.xml.core.internal.commentelement.impl.CommentElementRegistry;
david_williams4ad020f2005-04-18 08:00:30 +000027import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
28import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
david_williams56777022005-04-11 06:21:55 +000029import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
david_williams96213482004-11-11 09:07:12 +000030import org.w3c.dom.Attr;
31import org.w3c.dom.DOMException;
32import org.w3c.dom.Element;
33import org.w3c.dom.NamedNodeMap;
34import org.w3c.dom.Node;
35import org.w3c.dom.Text;
36
37
38/**
39 * XMLModelParser
40 */
david_williams63f5c322005-03-15 06:02:57 +000041public class XMLModelParser {
david_williams96213482004-11-11 09:07:12 +000042 private XMLModelContext context = null;
david_williams56777022005-04-11 06:21:55 +000043 private DOMModelImpl model = null;
david_williams96213482004-11-11 09:07:12 +000044
45 /**
46 */
david_williams56777022005-04-11 06:21:55 +000047 protected XMLModelParser(DOMModelImpl model) {
david_williams96213482004-11-11 09:07:12 +000048 super();
49
50 if (model != null) {
51 this.model = model;
david_williams96213482004-11-11 09:07:12 +000052 }
53 }
54
55 /**
56 */
57 protected boolean canBeImplicitTag(Element element) {
david_williamsfa0ae312005-06-20 20:16:53 +000058 ModelParserAdapter adapter = getParserAdapter();
59 if (adapter != null) {
60 return adapter.canBeImplicitTag(element);
david_williams96213482004-11-11 09:07:12 +000061 }
62 return false;
63 }
64
65 /**
66 */
67 protected boolean canBeImplicitTag(Element element, Node child) {
david_williamsfa0ae312005-06-20 20:16:53 +000068 ModelParserAdapter adapter = getParserAdapter();
69 if (adapter != null) {
70 return adapter.canBeImplicitTag(element, child);
david_williams96213482004-11-11 09:07:12 +000071 }
72 return false;
73 }
74
75 /**
76 */
77 protected boolean canContain(Element element, Node child) {
78 if (element == null || child == null)
79 return false;
80 ElementImpl impl = (ElementImpl) element;
81 if (impl.isEndTag())
82 return false; // invalid (floating) end tag
83 if (!impl.isContainer())
84 return false;
85 if (child.getNodeType() != Node.TEXT_NODE) {
86 if (impl.isJSPContainer() || impl.isCDATAContainer()) {
87 // accepts only Text child
88 return false;
89 }
90 }
david_williamsfa0ae312005-06-20 20:16:53 +000091 ModelParserAdapter adapter = getParserAdapter();
92 if (adapter != null) {
93 return adapter.canContain(element, child);
david_williams96213482004-11-11 09:07:12 +000094 }
95 return true;
96 }
97
98 /**
99 */
100 private void changeAttrEqual(IStructuredDocumentRegion flatNode, ITextRegion region) {
101 int offset = flatNode.getStart();
102 if (offset < 0)
103 return;
104 NodeImpl root = (NodeImpl) this.context.getRootNode();
105 if (root == null)
106 return;
107 Node node = root.getNodeAt(offset);
108 if (node == null)
109 return;
110 if (node.getNodeType() != Node.ELEMENT_NODE) {
111 if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
112 // just notify the change instead of setting data
113 ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node;
114 pi.notifyValueChanged();
115 }
116 return;
117 }
118 // actually, do nothing
119 }
120
121 /**
122 * changeAttrName method
123 *
david_williams96213482004-11-11 09:07:12 +0000124 */
125 private void changeAttrName(IStructuredDocumentRegion flatNode, ITextRegion region) {
126 int offset = flatNode.getStart();
127 if (offset < 0)
128 return;
129 NodeImpl root = (NodeImpl) this.context.getRootNode();
130 if (root == null)
131 return;
132 Node node = root.getNodeAt(offset);
133 if (node == null)
134 return;
135 if (node.getNodeType() != Node.ELEMENT_NODE) {
136 if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
137 // just notify the change instead of setting data
138 ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node;
139 pi.notifyValueChanged();
140 }
141 return;
142 }
143
144 ElementImpl element = (ElementImpl) node;
145 NamedNodeMap attributes = element.getAttributes();
146 if (attributes == null)
147 return;
148 int length = attributes.getLength();
149 for (int i = 0; i < length; i++) {
150 AttrImpl attr = (AttrImpl) attributes.item(i);
151 if (attr == null)
152 continue;
153 if (attr.getNameRegion() != region)
154 continue;
155
156 String name = flatNode.getText(region);
157 attr.setName(name);
158 break;
159 }
160 }
161
162 /**
163 * changeAttrValue method
164 *
david_williams96213482004-11-11 09:07:12 +0000165 */
166 private void changeAttrValue(IStructuredDocumentRegion flatNode, ITextRegion region) {
167 int offset = flatNode.getStart();
168 if (offset < 0)
169 return;
170 NodeImpl root = (NodeImpl) this.context.getRootNode();
171 if (root == null)
172 return;
173 Node node = root.getNodeAt(offset);
174 if (node == null)
175 return;
176 if (node.getNodeType() != Node.ELEMENT_NODE) {
177 if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
178 // just notify the change instead of setting data
179 ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node;
180 pi.notifyValueChanged();
181 }
182 return;
183 }
184
185 ElementImpl element = (ElementImpl) node;
186 NamedNodeMap attributes = element.getAttributes();
187 if (attributes == null)
188 return;
189 int length = attributes.getLength();
190 for (int i = 0; i < length; i++) {
191 AttrImpl attr = (AttrImpl) attributes.item(i);
192 if (attr == null)
193 continue;
194 if (attr.getValueRegion() != region)
195 continue;
196 // just notify the change instead of setting value
197 attr.notifyValueChanged();
198 break;
199 }
200 }
201
202 /**
203 * changeData method
204 *
david_williams96213482004-11-11 09:07:12 +0000205 */
206 private void changeData(IStructuredDocumentRegion flatNode, ITextRegion region) {
207 int offset = flatNode.getStart();
208 if (offset < 0)
209 return;
210 NodeImpl root = (NodeImpl) this.context.getRootNode();
211 if (root == null)
212 return;
213 Node node = root.getNodeAt(offset);
214 if (node == null)
215 return;
216 switch (node.getNodeType()) {
217 case Node.TEXT_NODE : {
218 TextImpl text = (TextImpl) node;
219 if (text.isSharingStructuredDocumentRegion(flatNode)) {
220 // has consecutive text sharing IStructuredDocumentRegion
221 changeStructuredDocumentRegion(flatNode);
222 return;
223 }
224 this.context.setNextNode(node);
225 cleanupText();
226 break;
227 }
228 case Node.CDATA_SECTION_NODE :
229 case Node.PROCESSING_INSTRUCTION_NODE :
230 break;
231 case Node.COMMENT_NODE :
232 case Node.ELEMENT_NODE :
233 // comment tag
234 changeStructuredDocumentRegion(flatNode);
235 return;
236 default :
237 return;
238 }
239
240 // just notify the change instead of setting data
241 NodeImpl impl = (NodeImpl) node;
242 impl.notifyValueChanged();
243 }
244
245 /**
246 */
247 private void changeEndTag(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) {
248 int offset = flatNode.getStart();
249 if (offset < 0)
250 return; // error
251 NodeImpl root = (NodeImpl) this.context.getRootNode();
252 if (root == null)
253 return; // error
254 Node node = root.getNodeAt(offset);
255 if (node == null)
256 return; // error
257
258 if (node.getNodeType() != Node.ELEMENT_NODE) {
259 changeStructuredDocumentRegion(flatNode);
260 return;
261 }
262
263 // check if change is only for close tag
264 if (newRegions != null) {
265 Iterator e = newRegions.iterator();
266 while (e.hasNext()) {
267 ITextRegion region = (ITextRegion) e.next();
268 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +0000269 if (regionType == DOMRegionContext.XML_TAG_CLOSE)
david_williams96213482004-11-11 09:07:12 +0000270 continue;
271
272 // other region has changed
273 changeStructuredDocumentRegion(flatNode);
274 return;
275 }
276 }
277 if (oldRegions != null) {
278 Iterator e = oldRegions.iterator();
279 while (e.hasNext()) {
280 ITextRegion region = (ITextRegion) e.next();
281 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +0000282 if (regionType == DOMRegionContext.XML_TAG_CLOSE)
david_williams96213482004-11-11 09:07:12 +0000283 continue;
284
285 // other region has changed
286 changeStructuredDocumentRegion(flatNode);
287 return;
288 }
289 }
290
291 // change for close tag has no impact
292 // do nothing
293 }
294
295 /**
296 * changeRegion method
297 *
david_williams96213482004-11-11 09:07:12 +0000298 */
299 void changeRegion(IStructuredDocumentRegion flatNode, ITextRegion region) {
300 if (flatNode == null || region == null)
301 return;
david_williamsfa0ae312005-06-20 20:16:53 +0000302 if (this.model.getDocument() == null)
david_williams96213482004-11-11 09:07:12 +0000303 return;
david_williamsfa0ae312005-06-20 20:16:53 +0000304 this.context = new XMLModelContext(this.model.getDocument());
david_williams96213482004-11-11 09:07:12 +0000305
306 // optimize typical cases
307 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +0000308 if (regionType == DOMRegionContext.XML_CONTENT || regionType == DOMRegionContext.XML_COMMENT_TEXT || regionType == DOMRegionContext.XML_CDATA_TEXT || regionType == DOMRegionContext.BLOCK_TEXT || isNestedContent(regionType)) {
david_williams96213482004-11-11 09:07:12 +0000309 changeData(flatNode, region);
david_williams63f5c322005-03-15 06:02:57 +0000310 }
david_williams56777022005-04-11 06:21:55 +0000311 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
david_williams96213482004-11-11 09:07:12 +0000312 changeAttrName(flatNode, region);
david_williams63f5c322005-03-15 06:02:57 +0000313 }
david_williams56777022005-04-11 06:21:55 +0000314 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
david_williams96213482004-11-11 09:07:12 +0000315 changeAttrValue(flatNode, region);
david_williams63f5c322005-03-15 06:02:57 +0000316 }
david_williams56777022005-04-11 06:21:55 +0000317 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
david_williams96213482004-11-11 09:07:12 +0000318 changeAttrEqual(flatNode, region);
david_williams63f5c322005-03-15 06:02:57 +0000319 }
david_williams56777022005-04-11 06:21:55 +0000320 else if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) {
david_williams96213482004-11-11 09:07:12 +0000321 changeTagName(flatNode, region);
david_williams63f5c322005-03-15 06:02:57 +0000322 }
323 else {
david_williams96213482004-11-11 09:07:12 +0000324 changeStructuredDocumentRegion(flatNode);
325 }
326 }
327
david_williams63f5c322005-03-15 06:02:57 +0000328
329
david_williams96213482004-11-11 09:07:12 +0000330 /**
331 */
332 private void changeStartTag(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) {
333 int offset = flatNode.getStart();
334 if (offset < 0)
335 return; // error
336 NodeImpl root = (NodeImpl) this.context.getRootNode();
337 if (root == null)
338 return; // error
339 Node node = root.getNodeAt(offset);
340 if (node == null)
341 return; // error
342
343 if (node.getNodeType() != Node.ELEMENT_NODE) {
344 changeStructuredDocumentRegion(flatNode);
345 return;
346 }
347 ElementImpl element = (ElementImpl) node;
348
349 // check if changes are only for attributes and close tag
350 boolean tagNameUnchanged = false;
351 if (newRegions != null) {
352 Iterator e = newRegions.iterator();
353 while (e.hasNext()) {
354 ITextRegion region = (ITextRegion) e.next();
355 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +0000356 if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME || regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS || regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)
david_williams96213482004-11-11 09:07:12 +0000357 continue;
david_williams56777022005-04-11 06:21:55 +0000358 if (regionType == DOMRegionContext.XML_TAG_CLOSE) {
david_williams96213482004-11-11 09:07:12 +0000359 // change from empty tag may have impact on structure
360 if (!element.isEmptyTag())
361 continue;
david_williams63f5c322005-03-15 06:02:57 +0000362 }
david_williams56777022005-04-11 06:21:55 +0000363 else if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) {
david_williams96213482004-11-11 09:07:12 +0000364 String oldTagName = element.getTagName();
365 String newTagName = flatNode.getText(region);
366 if (oldTagName != null && newTagName != null && oldTagName.equals(newTagName)) {
367 // the tag name is unchanged
368 tagNameUnchanged = true;
369 continue;
370 }
371 }
372
373 // other region has changed
374 changeStructuredDocumentRegion(flatNode);
375 return;
376 }
377 }
378 if (oldRegions != null) {
379 Iterator e = oldRegions.iterator();
380 while (e.hasNext()) {
381 ITextRegion region = (ITextRegion) e.next();
382 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +0000383 if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME || regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS || regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)
david_williams96213482004-11-11 09:07:12 +0000384 continue;
david_williams56777022005-04-11 06:21:55 +0000385 if (regionType == DOMRegionContext.XML_TAG_CLOSE) {
david_williams96213482004-11-11 09:07:12 +0000386 // change from empty tag may have impact on structure
387 if (!element.isEmptyTag())
388 continue;
david_williams63f5c322005-03-15 06:02:57 +0000389 }
david_williams56777022005-04-11 06:21:55 +0000390 else if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) {
david_williams96213482004-11-11 09:07:12 +0000391 // if new tag name is unchanged, it's OK
392 if (tagNameUnchanged)
393 continue;
394 }
395
396 // other region has changed
397 changeStructuredDocumentRegion(flatNode);
398 return;
399 }
400 }
401
402 // update attributes
403 ITextRegionList regions = flatNode.getRegions();
404 if (regions == null)
405 return; // error
406 NamedNodeMap attributes = element.getAttributes();
407 if (attributes == null)
408 return; // error
409
410 // first remove attributes
411 int regionIndex = 0;
412 int attrIndex = 0;
413 AttrImpl attr = null;
414 while (attrIndex < attributes.getLength()) {
415 attr = (AttrImpl) attributes.item(attrIndex);
416 if (attr == null) { // error
417 attrIndex++;
418 continue;
419 }
420 ITextRegion nameRegion = attr.getNameRegion();
421 if (nameRegion == null) { // error
422 element.removeAttributeNode(attr);
423 continue;
424 }
425 boolean found = false;
426 for (int i = regionIndex; i < regions.size(); i++) {
427 ITextRegion region = regions.get(i);
428 if (region == nameRegion) {
429 regionIndex = i + 1; // next region
430 found = true;
431 break;
432 }
433 }
434 if (found) {
435 attrIndex++;
david_williams63f5c322005-03-15 06:02:57 +0000436 }
437 else {
david_williams96213482004-11-11 09:07:12 +0000438 element.removeAttributeNode(attr);
439 }
440 }
441
442 // insert or update attributes
443 attrIndex = 0; // reset to first
444 AttrImpl newAttr = null;
445 ITextRegion oldValueRegion = null;
446 Iterator e = regions.iterator();
447 while (e.hasNext()) {
448 ITextRegion region = (ITextRegion) e.next();
449 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +0000450 if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
david_williams96213482004-11-11 09:07:12 +0000451 if (newAttr != null) {
452 // insert deferred new attribute
453 element.insertAttributeNode(newAttr, attrIndex++);
454 newAttr = null;
david_williams63f5c322005-03-15 06:02:57 +0000455 }
456 else if (attr != null && oldValueRegion != null) {
david_williams96213482004-11-11 09:07:12 +0000457 // notify existing attribute value removal
458 attr.notifyValueChanged();
459 }
460
461 oldValueRegion = null;
462 attr = (AttrImpl) attributes.item(attrIndex);
463 if (attr != null && attr.getNameRegion() == region) {
464 // existing attribute
465 attrIndex++;
466 // clear other regions
467 oldValueRegion = attr.getValueRegion();
468 attr.setEqualRegion(null);
469 attr.setValueRegion(null);
david_williams63f5c322005-03-15 06:02:57 +0000470 }
471 else {
david_williams96213482004-11-11 09:07:12 +0000472 String name = flatNode.getText(region);
david_williamsfa0ae312005-06-20 20:16:53 +0000473 attr = (AttrImpl) this.model.getDocument().createAttribute(name);
david_williams96213482004-11-11 09:07:12 +0000474 if (attr != null)
475 attr.setNameRegion(region);
476 // defer insertion of new attribute
477 newAttr = attr;
478 }
david_williams63f5c322005-03-15 06:02:57 +0000479 }
david_williams56777022005-04-11 06:21:55 +0000480 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
david_williams96213482004-11-11 09:07:12 +0000481 if (attr != null) {
482 attr.setEqualRegion(region);
483 }
david_williams63f5c322005-03-15 06:02:57 +0000484 }
david_williams56777022005-04-11 06:21:55 +0000485 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
david_williams96213482004-11-11 09:07:12 +0000486 if (attr != null) {
487 attr.setValueRegion(region);
488 if (attr != newAttr && oldValueRegion != region) {
489 // notify existing attribute value changed
490 attr.notifyValueChanged();
491 }
492 oldValueRegion = null;
493 attr = null;
494 }
495 }
496 }
497
498 if (newAttr != null) {
499 // insert deferred new attribute
500 element.appendAttributeNode(newAttr);
david_williams63f5c322005-03-15 06:02:57 +0000501 }
502 else if (attr != null && oldValueRegion != null) {
david_williams96213482004-11-11 09:07:12 +0000503 // notify existing attribute value removal
504 attr.notifyValueChanged();
505 }
506 }
507
508 /**
509 * changeStructuredDocumentRegion method
510 *
david_williams96213482004-11-11 09:07:12 +0000511 */
512 private void changeStructuredDocumentRegion(IStructuredDocumentRegion flatNode) {
513 if (flatNode == null)
514 return;
david_williamsfa0ae312005-06-20 20:16:53 +0000515 if (this.model.getDocument() == null)
david_williams96213482004-11-11 09:07:12 +0000516 return;
517
518 setupContext(flatNode);
519
520 removeStructuredDocumentRegion(flatNode);
521 // make sure the parent is set to deepest level
522 // when end tag has been removed
523 this.context.setLast();
524 insertStructuredDocumentRegion(flatNode);
525
526 cleanupText();
527 cleanupEndTag();
528 }
529
530 /**
531 */
532 private void changeTagName(IStructuredDocumentRegion flatNode, ITextRegion region) {
533 int offset = flatNode.getStart();
534 if (offset < 0)
535 return; // error
536 NodeImpl root = (NodeImpl) this.context.getRootNode();
537 if (root == null)
538 return; // error
539 Node node = root.getNodeAt(offset);
540 if (node == null)
541 return; // error
542
543 if (node.getNodeType() != Node.ELEMENT_NODE) {
544 changeStructuredDocumentRegion(flatNode);
545 return;
546 }
547
548 ElementImpl element = (ElementImpl) node;
549 String newTagName = flatNode.getText(region);
550 if (newTagName == null || !element.matchTagName(newTagName)) {
551 // the tag name is changed
552 changeStructuredDocumentRegion(flatNode);
553 return;
554 }
555
556 // the tag name is unchanged
557 // this happens when typing spaces after the tag name
558 // do nothing, but...
559 // if it's not a change in the end tag of an element with the start
560 // tag,
561 // and case has been changed, set to element and notify
david_williams56777022005-04-11 06:21:55 +0000562 if (!element.hasStartTag() || StructuredDocumentRegionUtil.getFirstRegionType(flatNode) != DOMRegionContext.XML_END_TAG_OPEN) {
david_williams96213482004-11-11 09:07:12 +0000563 String tagName = element.getTagName();
564 if (tagName == null || !tagName.equals(newTagName)) {
565 element.setTagName(newTagName);
566 element.notifyValueChanged();
567 }
568 }
569 }
570
571 /**
572 * cleanupContext method
573 */
574 private void cleanupEndTag() {
575 Node parent = this.context.getParentNode();
576 Node next = this.context.getNextNode();
577 while (parent != null) {
578 while (next != null) {
579 if (next.getNodeType() == Node.ELEMENT_NODE) {
580 ElementImpl element = (ElementImpl) next;
581 if (element.isEndTag()) {
582 // floating end tag
583 String tagName = element.getTagName();
584 String rootName = getFindRootName(tagName);
585 ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName);
586 if (start != null) {
587 insertEndTag(start);
588 // move the end tag from 'element' to 'start'
589 start.addEndTag(element);
590 removeNode(element);
591 parent = this.context.getParentNode();
592 next = this.context.getNextNode();
593 continue;
594 }
595 }
596 }
597
598 Node first = next.getFirstChild();
599 if (first != null) {
600 parent = next;
601 next = first;
602 this.context.setNextNode(next);
david_williams63f5c322005-03-15 06:02:57 +0000603 }
604 else {
david_williams96213482004-11-11 09:07:12 +0000605 next = next.getNextSibling();
606 this.context.setNextNode(next);
607 }
608 }
609
610 if (parent.getNodeType() == Node.ELEMENT_NODE) {
611 ElementImpl element = (ElementImpl) parent;
612 if (!element.hasEndTag() && element.hasStartTag() && element.getNextSibling() == null) {
613 String tagName = element.getTagName();
614 ElementImpl end = (ElementImpl) this.context.findEndTag(tagName);
615 if (end != null) {
616 // move the end tag from 'end' to 'element'
617 element.addEndTag(end);
618 removeEndTag(end);
619 this.context.setParentNode(parent); // reset context
620 continue;
621 }
622 }
623 }
624
625 next = parent.getNextSibling();
626 parent = parent.getParentNode();
627 if (next != null) {
628 this.context.setNextNode(next);
david_williams63f5c322005-03-15 06:02:57 +0000629 }
630 else {
david_williams96213482004-11-11 09:07:12 +0000631 this.context.setParentNode(parent);
632 }
633 }
634 }
635
636 /**
637 */
638 private void cleanupText() {
639 Node parent = this.context.getParentNode();
640 if (parent == null)
641 return; // error
642 Node next = this.context.getNextNode();
643 Node prev = (next == null ? parent.getLastChild() : next.getPreviousSibling());
644
645 TextImpl nextText = null;
646 TextImpl prevText = null;
647 if (next != null && next.getNodeType() == Node.TEXT_NODE) {
648 nextText = (TextImpl) next;
649 }
650 if (prev != null && prev.getNodeType() == Node.TEXT_NODE) {
651 prevText = (TextImpl) prev;
652 }
653 if (nextText == null && prevText == null)
654 return;
655 if (nextText != null && prevText != null) {
656 // consecutive Text nodes created by setupContext(),
657 // concat them
658 IStructuredDocumentRegion flatNode = nextText.getStructuredDocumentRegion();
659 if (flatNode != null)
660 prevText.appendStructuredDocumentRegion(flatNode);
661 Node newNext = next.getNextSibling();
662 parent.removeChild(next);
663 next = null;
664 this.context.setNextNode(newNext);
665 }
666
667 TextImpl childText = (prevText != null ? prevText : nextText);
668 if (childText.getNextSibling() == null && childText.getPreviousSibling() == null) {
669 if (parent.getNodeType() == Node.ELEMENT_NODE) {
670 ElementImpl parentElement = (ElementImpl) parent;
671 if (!parentElement.hasStartTag() && !parentElement.hasEndTag()) {
672 if (childText.isWhitespace() || childText.isInvalid()) {
673 // implicit parent is not required
674 Node newParent = parent.getParentNode();
675 if (newParent != null) {
676 Node newNext = parent.getNextSibling();
677 newParent.removeChild(parent);
678 parent.removeChild(childText);
679 newParent.insertBefore(childText, newNext);
680 if (childText == next) {
681 this.context.setNextNode(childText);
david_williams63f5c322005-03-15 06:02:57 +0000682 }
683 else if (newNext != null) {
david_williams96213482004-11-11 09:07:12 +0000684 this.context.setNextNode(newNext);
david_williams63f5c322005-03-15 06:02:57 +0000685 }
686 else {
david_williams96213482004-11-11 09:07:12 +0000687 this.context.setParentNode(newParent);
688 }
689 // try again
690 cleanupText();
691 }
692 }
693 }
694 }
695 }
696 }
697
698 /**
699 * This routine create an Element from comment data for comment style
700 * elements, such as SSI and METADATA
701 */
702 protected Element createCommentElement(String data, boolean isJSPTag) {
703 String trimmedData = data.trim();
704 CommentElementConfiguration[] configs = CommentElementRegistry.getInstance().getConfigurations();
705 for (int iConfig = 0; iConfig < configs.length; iConfig++) {
706 CommentElementConfiguration config = configs[iConfig];
707 if ((isJSPTag && !config.acceptJSPComment()) || (!isJSPTag && !config.acceptXMLComment())) {
708 continue;
709 }
710 String[] prefixes = config.getPrefix();
711 for (int iPrefix = 0; iPrefix < prefixes.length; iPrefix++) {
712 if (trimmedData.startsWith(prefixes[iPrefix])) {
david_williamsfa0ae312005-06-20 20:16:53 +0000713 return config.createElement(this.model.getDocument(), data, isJSPTag);
david_williams96213482004-11-11 09:07:12 +0000714 }
715 }
716 }
david_williamsfa0ae312005-06-20 20:16:53 +0000717 ModelParserAdapter adapter = getParserAdapter();
718 if (adapter != null) {
719 return adapter.createCommentElement(this.model.getDocument(), data, isJSPTag);
david_williams96213482004-11-11 09:07:12 +0000720 }
721 return null;
722 }
723
724 /**
725 * This routine create an implicit Element for given parent and child,
726 * such as HTML, BODY, HEAD, and TBODY for HTML document.
727 */
728 protected Element createImplicitElement(Node parent, Node child) {
david_williamsfa0ae312005-06-20 20:16:53 +0000729 ModelParserAdapter adapter = getParserAdapter();
730 if (adapter != null) {
731 return adapter.createImplicitElement(this.model.getDocument(), parent, child);
david_williams96213482004-11-11 09:07:12 +0000732 }
733 return null;
734 }
735
736 /**
737 */
738 private void demoteNodes(Node root, Node newParent, Node oldParent, Node next) {
739 if (newParent.getNodeType() != Node.ELEMENT_NODE)
740 return;
741 ElementImpl newElement = (ElementImpl) newParent;
742
743 // find next
744 while (next == null) {
745 if (oldParent.getNodeType() != Node.ELEMENT_NODE)
746 return;
747 ElementImpl oldElement = (ElementImpl) oldParent;
748 if (oldElement.hasEndTag())
749 return;
750 oldParent = oldElement.getParentNode();
751 if (oldParent == null)
752 return; // error
753 next = oldElement.getNextSibling();
754 }
755
756 while (next != null) {
757 boolean done = false;
758 if (next.getNodeType() == Node.ELEMENT_NODE) {
759 ElementImpl nextElement = (ElementImpl) next;
760 if (!nextElement.hasStartTag()) {
761 Node nextChild = nextElement.getFirstChild();
762 if (nextChild != null) {
763 // demote children
764 next = nextChild;
765 oldParent = nextElement;
766 continue;
767 }
768
769 if (nextElement.hasEndTag()) {
770 if (nextElement.matchEndTag(newElement)) {
771 // stop at the matched invalid end tag
772 next = nextElement.getNextSibling();
773 oldParent.removeChild(nextElement);
774 newElement.addEndTag(nextElement);
775
776 if (newElement == root)
777 return;
778 Node p = newElement.getParentNode();
779 // check if reached to top
780 if (p == null || p == oldParent || p.getNodeType() != Node.ELEMENT_NODE)
781 return;
782 newElement = (ElementImpl) p;
783 done = true;
784 }
david_williams63f5c322005-03-15 06:02:57 +0000785 }
786 else {
david_williams96213482004-11-11 09:07:12 +0000787 // remove implicit element
788 next = nextElement.getNextSibling();
789 oldParent.removeChild(nextElement);
790 done = true;
791 }
792 }
793 }
794
795 if (!done) {
796 if (!canContain(newElement, next)) {
797 if (newElement == root)
798 return;
799 Node p = newElement.getParentNode();
800 // check if reached to top
801 if (p == null || p == oldParent || p.getNodeType() != Node.ELEMENT_NODE)
802 return;
803 newElement = (ElementImpl) p;
804 continue;
805 }
806
807 Node child = next;
808 next = next.getNextSibling();
809 oldParent.removeChild(child);
810 insertNode(newElement, child, null);
811 Node childParent = child.getParentNode();
812 if (childParent != newElement) {
813 newElement = (ElementImpl) childParent;
814 }
815 }
816
817 // find next parent and sibling
818 while (next == null) {
819 if (oldParent.getNodeType() != Node.ELEMENT_NODE)
820 return;
821 ElementImpl oldElement = (ElementImpl) oldParent;
822
823 // dug parent must not have children at this point
824 if (!oldElement.hasChildNodes() && !oldElement.hasStartTag()) {
825 oldParent = oldElement.getParentNode();
826 if (oldParent == null)
827 return; // error
828 next = oldElement;
829 break;
830 }
831
832 if (oldElement.hasEndTag())
833 return;
834 oldParent = oldElement.getParentNode();
835 if (oldParent == null)
836 return; // error
837 next = oldElement.getNextSibling();
838 }
839 }
840 }
841
david_williamsfa0ae312005-06-20 20:16:53 +0000842 private ModelParserAdapter getParserAdapter() {
843 return (ModelParserAdapter) this.model.getDocument().getAdapterFor(ModelParserAdapter.class);
david_williams96213482004-11-11 09:07:12 +0000844 }
david_williamsfa0ae312005-06-20 20:16:53 +0000845
david_williams96213482004-11-11 09:07:12 +0000846 /**
847 */
848 protected String getFindRootName(String tagName) {
david_williamsfa0ae312005-06-20 20:16:53 +0000849 ModelParserAdapter adapter = getParserAdapter();
850 if (adapter != null) {
851 return adapter.getFindRootName(tagName);
david_williams96213482004-11-11 09:07:12 +0000852 }
853 return null;
854 }
855
856 /**
857 */
david_williamsc39caaf2005-04-05 06:07:16 +0000858 protected final IDOMModel getModel() {
david_williams96213482004-11-11 09:07:12 +0000859 return this.model;
860 }
861
862 /**
863 * insertCDATASection method
864 *
david_williams96213482004-11-11 09:07:12 +0000865 */
866 private void insertCDATASection(IStructuredDocumentRegion flatNode) {
867 ITextRegionList regions = flatNode.getRegions();
868 if (regions == null)
869 return;
870
871 CDATASectionImpl cdata = null;
872 try {
david_williamsfa0ae312005-06-20 20:16:53 +0000873 cdata = (CDATASectionImpl) this.model.getDocument().createCDATASection(null);
david_williams63f5c322005-03-15 06:02:57 +0000874 }
875 catch (DOMException ex) {
david_williams96213482004-11-11 09:07:12 +0000876 }
877 if (cdata == null) { // CDATA section might not be supported
878 insertInvalidDecl(flatNode); // regard as invalid decl
879 return;
880 }
881
882 cdata.setStructuredDocumentRegion(flatNode);
883 insertNode(cdata);
884 }
885
886 /**
887 * insertComment method
888 *
david_williams96213482004-11-11 09:07:12 +0000889 */
890 private void insertComment(IStructuredDocumentRegion flatNode) {
891 ITextRegionList regions = flatNode.getRegions();
892 if (regions == null)
893 return;
894
895 String data = null;
896 boolean isJSPTag = false;
897 Iterator e = regions.iterator();
898 while (e.hasNext()) {
899 ITextRegion region = (ITextRegion) e.next();
900 String regionType = region.getType();
david_williams63f5c322005-03-15 06:02:57 +0000901 if (isNestedCommentOpen(regionType)) {
david_williams96213482004-11-11 09:07:12 +0000902 isJSPTag = true;
david_williams63f5c322005-03-15 06:02:57 +0000903 }
david_williams56777022005-04-11 06:21:55 +0000904 else if (regionType == DOMRegionContext.XML_COMMENT_TEXT || isNestedCommentText(regionType)) {
david_williams96213482004-11-11 09:07:12 +0000905 if (data == null) {
906 data = flatNode.getText(region);
907 }
908 }
909 }
910
911 if (data != null) {
912 ElementImpl element = (ElementImpl) createCommentElement(data, isJSPTag);
913 if (element != null) {
914 if (!isEndTag(element)) {
915 element.setStartStructuredDocumentRegion(flatNode);
916 insertStartTag(element);
917 return;
918 }
919
920 // end tag
921 element.setEndStructuredDocumentRegion(flatNode);
922
923 String tagName = element.getTagName();
924 String rootName = getFindRootName(tagName);
925 ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName);
926 if (start != null) { // start tag found
927 insertEndTag(start);
928 start.addEndTag(element);
929 return;
930 }
931
932 // invalid end tag
933 insertNode(element);
934 return;
935 }
936 }
937
david_williamsfa0ae312005-06-20 20:16:53 +0000938 CommentImpl comment = (CommentImpl) this.model.getDocument().createComment(null);
david_williams96213482004-11-11 09:07:12 +0000939 if (comment == null)
940 return;
941 if (isJSPTag)
942 comment.setJSPTag(true);
943 comment.setStructuredDocumentRegion(flatNode);
944 insertNode(comment);
945 }
946
947 /**
948 * insertDecl method
949 *
david_williams96213482004-11-11 09:07:12 +0000950 */
951 private void insertDecl(IStructuredDocumentRegion flatNode) {
952 ITextRegionList regions = flatNode.getRegions();
953 if (regions == null)
954 return;
955
956 boolean isDocType = false;
957 String name = null;
958 String publicId = null;
959 String systemId = null;
960 Iterator e = regions.iterator();
961 while (e.hasNext()) {
962 ITextRegion region = (ITextRegion) e.next();
963 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +0000964 if (regionType == DOMRegionContext.XML_DOCTYPE_DECLARATION) {
david_williams96213482004-11-11 09:07:12 +0000965 isDocType = true;
david_williams63f5c322005-03-15 06:02:57 +0000966 }
david_williams56777022005-04-11 06:21:55 +0000967 else if (regionType == DOMRegionContext.XML_DOCTYPE_NAME) {
david_williams96213482004-11-11 09:07:12 +0000968 if (name == null)
969 name = flatNode.getText(region);
david_williams63f5c322005-03-15 06:02:57 +0000970 }
david_williams56777022005-04-11 06:21:55 +0000971 else if (regionType == DOMRegionContext.XML_DOCTYPE_EXTERNAL_ID_PUBREF) {
david_williams96213482004-11-11 09:07:12 +0000972 if (publicId == null)
973 publicId = StructuredDocumentRegionUtil.getAttrValue(flatNode, region);
david_williams63f5c322005-03-15 06:02:57 +0000974 }
david_williams56777022005-04-11 06:21:55 +0000975 else if (regionType == DOMRegionContext.XML_DOCTYPE_EXTERNAL_ID_SYSREF) {
david_williams96213482004-11-11 09:07:12 +0000976 if (systemId == null)
977 systemId = StructuredDocumentRegionUtil.getAttrValue(flatNode, region);
978 }
979 }
980
981 // invalid declaration
982 if (!isDocType) {
983 insertInvalidDecl(flatNode);
984 return;
985 }
986
david_williamsfa0ae312005-06-20 20:16:53 +0000987 DocumentTypeImpl docType = (DocumentTypeImpl) this.model.getDocument().createDoctype(name);
david_williams96213482004-11-11 09:07:12 +0000988 if (docType == null)
989 return;
990 if (publicId != null)
991 docType.setPublicId(publicId);
992 if (systemId != null)
993 docType.setSystemId(systemId);
994 docType.setStructuredDocumentRegion(flatNode);
995 insertNode(docType);
996 }
997
998 /**
david_williams63f5c322005-03-15 06:02:57 +0000999 * insertEndTag method can be used by subclasses, but not overrided.
david_williams96213482004-11-11 09:07:12 +00001000 *
1001 * @param element
1002 * org.w3c.dom.Element
1003 */
david_williams63f5c322005-03-15 06:02:57 +00001004 protected void insertEndTag(Element element) {
david_williams96213482004-11-11 09:07:12 +00001005 if (element == null)
1006 return;
1007
1008 Node newParent = element.getParentNode();
1009 if (newParent == null)
1010 return; // error
1011
1012 if (!((ElementImpl) element).isContainer()) {
1013 // just update context
1014 Node elementNext = element.getNextSibling();
1015 if (elementNext != null)
1016 this.context.setNextNode(elementNext);
1017 else
1018 this.context.setParentNode(newParent);
1019 return;
1020 }
1021
1022 // promote children
1023 Node newNext = element.getNextSibling();
1024 Node oldParent = this.context.getParentNode();
1025 if (oldParent == null)
1026 return; // error
1027 Node oldNext = this.context.getNextNode();
1028 promoteNodes(element, newParent, newNext, oldParent, oldNext);
1029
1030 // update context
1031 // re-check the next sibling
1032 newNext = element.getNextSibling();
1033 if (newNext != null)
1034 this.context.setNextNode(newNext);
1035 else
1036 this.context.setParentNode(newParent);
1037 }
1038
1039 /**
1040 * insertEndTag method
1041 *
david_williams96213482004-11-11 09:07:12 +00001042 */
1043 private void insertEndTag(IStructuredDocumentRegion flatNode) {
1044 ITextRegionList regions = flatNode.getRegions();
1045 if (regions == null)
1046 return;
1047
1048 String tagName = null;
1049 Iterator e = regions.iterator();
1050 while (e.hasNext()) {
1051 ITextRegion region = (ITextRegion) e.next();
david_williams63f5c322005-03-15 06:02:57 +00001052 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +00001053 if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) {
david_williams96213482004-11-11 09:07:12 +00001054 if (tagName == null)
1055 tagName = flatNode.getText(region);
1056 }
1057 }
1058
1059 if (tagName == null) { // invalid end tag
1060 insertText(flatNode); // regard as invalid text
1061 return;
1062 }
1063
1064 String rootName = getFindRootName(tagName);
1065 ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName);
1066 if (start != null) { // start tag found
1067 insertEndTag(start);
1068 start.setEndStructuredDocumentRegion(flatNode);
1069 return;
1070 }
1071
1072 // invalid end tag
1073 ElementImpl end = null;
1074 try {
david_williamsfa0ae312005-06-20 20:16:53 +00001075 end = (ElementImpl) this.model.getDocument().createElement(tagName);
david_williams63f5c322005-03-15 06:02:57 +00001076 }
1077 catch (DOMException ex) {
david_williams96213482004-11-11 09:07:12 +00001078 }
1079 if (end == null) { // invalid end tag
1080 insertText(flatNode); // regard as invalid text
1081 return;
1082 }
1083 end.setEndStructuredDocumentRegion(flatNode);
1084 insertNode(end);
1085 }
1086
1087 /**
1088 * insertEntityRef method
1089 *
david_williams96213482004-11-11 09:07:12 +00001090 */
1091 private void insertEntityRef(IStructuredDocumentRegion flatNode) {
1092 ITextRegionList regions = flatNode.getRegions();
1093 if (regions == null)
1094 return;
1095
1096 String name = null;
1097 Iterator e = regions.iterator();
1098 while (e.hasNext()) {
1099 ITextRegion region = (ITextRegion) e.next();
1100 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +00001101 if (regionType == DOMRegionContext.XML_ENTITY_REFERENCE || regionType == DOMRegionContext.XML_CHAR_REFERENCE) {
david_williams96213482004-11-11 09:07:12 +00001102 if (name == null)
1103 name = StructuredDocumentRegionUtil.getEntityRefName(flatNode, region);
1104 }
1105 }
1106
1107 if (name == null) { // invalid entity
1108 insertText(flatNode);
1109 return;
1110 }
1111
david_williamsfa0ae312005-06-20 20:16:53 +00001112 // ISSUE: avoid this cast
1113 String value = ((DocumentImpl)this.model.getDocument()).getCharValue(name);
david_williams96213482004-11-11 09:07:12 +00001114 if (value != null) { // character entity
1115 TextImpl text = (TextImpl) this.context.findPreviousText();
1116 if (text != null) { // existing text found
1117 // do not append data
1118 text.appendStructuredDocumentRegion(flatNode);
1119 // notify the change
1120 text.notifyValueChanged();
1121 return;
1122 }
1123
1124 // new text
david_williamsfa0ae312005-06-20 20:16:53 +00001125 text = (TextImpl) this.model.getDocument().createTextNode(null);
david_williams96213482004-11-11 09:07:12 +00001126 if (text == null)
1127 return;
1128 text.setStructuredDocumentRegion(flatNode);
1129 insertNode(text);
1130 return;
1131 }
1132
1133 // general entity reference
1134 EntityReferenceImpl ref = null;
1135 try {
david_williamsfa0ae312005-06-20 20:16:53 +00001136 ref = (EntityReferenceImpl) this.model.getDocument().createEntityReference(name);
david_williams63f5c322005-03-15 06:02:57 +00001137 }
1138 catch (DOMException ex) {
david_williams96213482004-11-11 09:07:12 +00001139 }
1140 if (ref == null) { // entity reference might not be supported
1141 insertText(flatNode); // regard as invalid text
1142 return;
1143 }
1144
1145 ref.setStructuredDocumentRegion(flatNode);
1146 insertNode(ref);
1147 }
1148
1149 /**
1150 * insertInvalidDecl method
1151 *
david_williams96213482004-11-11 09:07:12 +00001152 */
1153 private void insertInvalidDecl(IStructuredDocumentRegion flatNode) {
1154 ITextRegionList regions = flatNode.getRegions();
1155 if (regions == null)
1156 return;
1157
1158 ElementImpl element = null;
1159 try {
david_williamsfa0ae312005-06-20 20:16:53 +00001160 element = (ElementImpl) this.model.getDocument().createElement("!");//$NON-NLS-1$
david_williams63f5c322005-03-15 06:02:57 +00001161 }
1162 catch (DOMException ex) {
david_williams96213482004-11-11 09:07:12 +00001163 }
1164 if (element == null) { // invalid tag
1165 insertText(flatNode); // regard as invalid text
1166 return;
1167 }
1168 element.setEmptyTag(true);
1169 element.setStartStructuredDocumentRegion(flatNode);
1170 insertNode(element);
1171 }
1172
1173 /**
1174 * insertJSPTag method
1175 *
david_williams96213482004-11-11 09:07:12 +00001176 */
david_williams63f5c322005-03-15 06:02:57 +00001177 private void insertNestedTag(IStructuredDocumentRegion flatNode) {
david_williams96213482004-11-11 09:07:12 +00001178 ITextRegionList regions = flatNode.getRegions();
1179 if (regions == null)
1180 return;
1181
1182 String tagName = null;
1183 AttrImpl attr = null;
1184 Vector attrNodes = null;
1185 boolean isCloseTag = false;
1186 Iterator e = regions.iterator();
1187 while (e.hasNext()) {
1188 ITextRegion region = (ITextRegion) e.next();
1189 String regionType = region.getType();
david_williams63f5c322005-03-15 06:02:57 +00001190 if (isNestedTagOpen(regionType) || isNestedTagName(regionType)) {
1191 tagName = computeNestedTag(regionType, tagName, flatNode, region);
1192 }
1193 else if (isNestedTagClose(regionType)) {
david_williams96213482004-11-11 09:07:12 +00001194 isCloseTag = true;
david_williams63f5c322005-03-15 06:02:57 +00001195 }
david_williams56777022005-04-11 06:21:55 +00001196 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
david_williams96213482004-11-11 09:07:12 +00001197 String name = flatNode.getText(region);
david_williamsfa0ae312005-06-20 20:16:53 +00001198 attr = (AttrImpl) this.model.getDocument().createAttribute(name);
david_williams96213482004-11-11 09:07:12 +00001199 if (attr != null) {
1200 attr.setNameRegion(region);
1201 if (attrNodes == null)
1202 attrNodes = new Vector();
1203 attrNodes.addElement(attr);
1204 }
david_williams63f5c322005-03-15 06:02:57 +00001205 }
david_williams56777022005-04-11 06:21:55 +00001206 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
david_williams96213482004-11-11 09:07:12 +00001207 if (attr != null) {
1208 attr.setEqualRegion(region);
1209 }
david_williams63f5c322005-03-15 06:02:57 +00001210 }
david_williams56777022005-04-11 06:21:55 +00001211 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
david_williams96213482004-11-11 09:07:12 +00001212 if (attr != null) {
1213 attr.setValueRegion(region);
1214 attr = null;
1215 }
1216 }
1217 }
1218
1219 if (tagName == null) {
1220 if (isCloseTag) {
1221 // close JSP tag
1222 Node parent = this.context.getParentNode();
1223 if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) {
1224 ElementImpl start = (ElementImpl) parent;
1225 if (start.isJSPContainer()) {
1226 insertEndTag(start);
1227 start.setEndStructuredDocumentRegion(flatNode);
1228 return;
1229 }
1230 }
1231 }
1232 // invalid JSP tag
1233 insertText(flatNode); // regard as invalid text
1234 return;
1235 }
1236
1237 ElementImpl element = null;
1238 try {
david_williamsfa0ae312005-06-20 20:16:53 +00001239 element = (ElementImpl) this.model.getDocument().createElement(tagName);
david_williams63f5c322005-03-15 06:02:57 +00001240 }
1241 catch (DOMException ex) {
david_williams96213482004-11-11 09:07:12 +00001242 }
1243 if (element == null) { // invalid tag
1244 insertText(flatNode); // regard as invalid text
1245 return;
1246 }
1247 if (attrNodes != null) {
1248 Enumeration ae = attrNodes.elements();
1249 while (ae.hasMoreElements()) {
1250 Attr a = (Attr) ae.nextElement();
1251 if (a == null)
1252 continue;
1253 element.appendAttributeNode(a);
1254 }
1255 }
1256 element.setJSPTag(true);
1257 element.setStartStructuredDocumentRegion(flatNode);
1258 insertStartTag(element);
1259 }
1260
david_williams63f5c322005-03-15 06:02:57 +00001261 protected boolean isNestedTagClose(String regionType) {
1262 boolean result = false;
1263 return result;
1264 }
1265
1266 protected boolean isNestedTagOpen(String regionType) {
1267 boolean result = false;
1268 return result;
1269 }
1270
1271 protected String computeNestedTag(String regionType, String tagName, IStructuredDocumentRegion structuredDocumentRegion, ITextRegion region) {
1272 return tagName;
1273 }
1274
david_williams96213482004-11-11 09:07:12 +00001275 /**
1276 * insertNode method
1277 *
1278 * @param child
1279 * org.w3c.dom.Node
1280 */
1281 private void insertNode(Node node) {
1282 if (node == null)
1283 return;
1284 if (this.context == null)
1285 return;
1286
1287 Node parent = this.context.getParentNode();
1288 if (parent == null)
1289 return;
1290 Node next = this.context.getNextNode();
1291 while (parent.getNodeType() == Node.ELEMENT_NODE) {
1292 ElementImpl element = (ElementImpl) parent;
1293 if (canContain(element, node)) {
1294 if (!element.hasStartTag() && next == element.getFirstChild()) {
1295 // first child of implicit tag
1296 // deletege to the parent
1297 parent = element.getParentNode();
1298 if (parent == null)
1299 return;
1300 next = element;
1301 this.context.setNextNode(next);
1302 continue;
1303 }
1304 break;
1305 }
1306 parent = element.getParentNode();
1307 if (parent == null)
1308 return;
1309
1310 // promote siblings
1311 Node newNext = element.getNextSibling();
1312 Node child = next;
1313 while (child != null) {
1314 Node nextChild = child.getNextSibling();
1315 element.removeChild(child);
1316 parent.insertBefore(child, newNext);
1317 child = nextChild;
1318 }
1319
1320 // leave the old end tag where it is
1321 if (element.hasEndTag()) {
1322 Element end = element.removeEndTag();
1323 if (end != null) {
1324 parent.insertBefore(end, newNext);
1325 if (next == null)
1326 next = end;
1327 }
1328 }
1329 if (!element.hasStartTag()) {
1330 // implicit element
1331 if (!element.hasChildNodes()) {
1332 parent.removeChild(element);
1333 }
1334 }
1335
1336 // update context
1337 if (next == null)
1338 next = newNext;
1339 if (next != null)
1340 this.context.setNextNode(next);
1341 else
1342 this.context.setParentNode(parent);
1343 }
1344
1345 insertNode(parent, node, next);
1346 next = node.getNextSibling();
1347 if (next != null)
1348 this.context.setNextNode(next);
1349 else
1350 this.context.setParentNode(node.getParentNode());
1351 }
1352
1353 /**
1354 */
1355 private void insertNode(Node parent, Node node, Node next) {
1356 while (next != null && next.getNodeType() == Node.ELEMENT_NODE) {
1357 ElementImpl nextElement = (ElementImpl) next;
1358 if (nextElement.hasStartTag())
1359 break;
1360 if (!canBeImplicitTag(nextElement, node))
1361 break;
1362 parent = nextElement;
1363 next = nextElement.getFirstChild();
1364 }
1365 Element implicitElement = createImplicitElement(parent, node);
1366 if (implicitElement != null)
1367 node = implicitElement;
1368 parent.insertBefore(node, next);
1369 }
1370
1371 /**
1372 * insertPI method
1373 *
david_williams96213482004-11-11 09:07:12 +00001374 */
1375 private void insertPI(IStructuredDocumentRegion flatNode) {
1376 ITextRegionList regions = flatNode.getRegions();
1377 if (regions == null)
1378 return;
1379
1380 String target = null;
1381 Iterator e = regions.iterator();
1382 while (e.hasNext()) {
1383 ITextRegion region = (ITextRegion) e.next();
1384 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +00001385 if (regionType == DOMRegionContext.XML_PI_OPEN || regionType == DOMRegionContext.XML_PI_CLOSE)
david_williams96213482004-11-11 09:07:12 +00001386 continue;
1387 if (target == null)
1388 target = flatNode.getText(region);
1389 }
1390
david_williamsfa0ae312005-06-20 20:16:53 +00001391 ProcessingInstructionImpl pi = (ProcessingInstructionImpl) this.model.getDocument().createProcessingInstruction(target, null);
david_williams96213482004-11-11 09:07:12 +00001392 if (pi == null)
1393 return;
1394 pi.setStructuredDocumentRegion(flatNode);
1395 insertNode(pi);
1396 }
1397
1398 /**
david_williams63f5c322005-03-15 06:02:57 +00001399 * insertStartTag method can be used by subclasses, but not overridden.
david_williams96213482004-11-11 09:07:12 +00001400 *
1401 * @param element
1402 * org.w3c.dom.Element
1403 */
david_williams63f5c322005-03-15 06:02:57 +00001404 protected void insertStartTag(Element element) {
david_williams96213482004-11-11 09:07:12 +00001405 if (element == null)
1406 return;
1407 if (this.context == null)
1408 return;
1409
1410 insertNode(element);
1411
1412 ElementImpl newElement = (ElementImpl) element;
1413 if (newElement.isEmptyTag() || !newElement.isContainer())
1414 return;
1415
1416 // demote siblings
1417 Node parent = this.context.getParentNode();
1418 if (parent == null)
1419 return; // error
1420 Node next = this.context.getNextNode();
1421 demoteNodes(element, element, parent, next);
1422
1423 // update context
1424 Node firstChild = element.getFirstChild();
1425 if (firstChild != null)
1426 this.context.setNextNode(firstChild);
1427 else
1428 this.context.setParentNode(element);
1429 }
1430
1431 /**
1432 * insertStartTag method
1433 *
david_williams96213482004-11-11 09:07:12 +00001434 */
1435 private void insertStartTag(IStructuredDocumentRegion flatNode) {
1436 ITextRegionList regions = flatNode.getRegions();
1437 if (regions == null)
1438 return;
1439
1440 String tagName = null;
1441 boolean isEmptyTag = false;
1442 AttrImpl attr = null;
1443 Vector attrNodes = null;
1444 Iterator e = regions.iterator();
1445 while (e.hasNext()) {
1446 ITextRegion region = (ITextRegion) e.next();
1447 String regionType = region.getType();
david_williams56777022005-04-11 06:21:55 +00001448 if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) {
david_williams96213482004-11-11 09:07:12 +00001449 if (tagName == null)
1450 tagName = flatNode.getText(region);
david_williams63f5c322005-03-15 06:02:57 +00001451 }
david_williams56777022005-04-11 06:21:55 +00001452 else if (regionType == DOMRegionContext.XML_EMPTY_TAG_CLOSE) {
david_williams96213482004-11-11 09:07:12 +00001453 isEmptyTag = true;
david_williams63f5c322005-03-15 06:02:57 +00001454 }
david_williams56777022005-04-11 06:21:55 +00001455 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
david_williams96213482004-11-11 09:07:12 +00001456 String name = flatNode.getText(region);
david_williamsfa0ae312005-06-20 20:16:53 +00001457 attr = (AttrImpl) this.model.getDocument().createAttribute(name);
david_williams96213482004-11-11 09:07:12 +00001458 if (attr != null) {
1459 attr.setNameRegion(region);
1460 if (attrNodes == null)
1461 attrNodes = new Vector();
1462 attrNodes.addElement(attr);
1463 }
david_williams63f5c322005-03-15 06:02:57 +00001464 }
david_williams56777022005-04-11 06:21:55 +00001465 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
david_williams96213482004-11-11 09:07:12 +00001466 if (attr != null) {
1467 attr.setEqualRegion(region);
1468 }
david_williams63f5c322005-03-15 06:02:57 +00001469 }
david_williams56777022005-04-11 06:21:55 +00001470 else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
david_williams96213482004-11-11 09:07:12 +00001471 if (attr != null) {
1472 attr.setValueRegion(region);
1473 attr = null;
1474 }
1475 }
1476 }
1477
1478 if (tagName == null) { // invalid start tag
1479 insertText(flatNode); // regard as invalid text
1480 return;
1481 }
1482
1483 ElementImpl element = null;
1484 try {
david_williamsfa0ae312005-06-20 20:16:53 +00001485 element = (ElementImpl) this.model.getDocument().createElement(tagName);
david_williams63f5c322005-03-15 06:02:57 +00001486 }
1487 catch (DOMException ex) {
1488 // typically invalid name
david_williams96213482004-11-11 09:07:12 +00001489 }
1490 if (element == null) { // invalid tag
1491 insertText(flatNode); // regard as invalid text
1492 return;
1493 }
1494 if (attrNodes != null) {
1495 Enumeration ae = attrNodes.elements();
1496 while (ae.hasMoreElements()) {
1497 Attr a = (Attr) ae.nextElement();
1498 if (a == null)
1499 continue;
1500 element.appendAttributeNode(a);
1501 }
1502 }
1503 if (isEmptyTag)
1504 element.setEmptyTag(true);
1505 element.setStartStructuredDocumentRegion(flatNode);
1506 insertStartTag(element);
1507 }
1508
1509 /**
1510 * insertStructuredDocumentRegion method
1511 *
david_williams96213482004-11-11 09:07:12 +00001512 */
david_williams63f5c322005-03-15 06:02:57 +00001513 protected void insertStructuredDocumentRegion(IStructuredDocumentRegion flatNode) {
david_williams96213482004-11-11 09:07:12 +00001514 String regionType = StructuredDocumentRegionUtil.getFirstRegionType(flatNode);
david_williams56777022005-04-11 06:21:55 +00001515 if (regionType == DOMRegionContext.XML_TAG_OPEN) {
david_williams96213482004-11-11 09:07:12 +00001516 insertStartTag(flatNode);
david_williams63f5c322005-03-15 06:02:57 +00001517 }
david_williams56777022005-04-11 06:21:55 +00001518 else if (regionType == DOMRegionContext.XML_END_TAG_OPEN) {
david_williams96213482004-11-11 09:07:12 +00001519 insertEndTag(flatNode);
david_williams63f5c322005-03-15 06:02:57 +00001520 }
david_williams56777022005-04-11 06:21:55 +00001521 else if (regionType == DOMRegionContext.XML_COMMENT_OPEN || isNestedCommentOpen(regionType)) {
david_williams96213482004-11-11 09:07:12 +00001522 insertComment(flatNode);
david_williams63f5c322005-03-15 06:02:57 +00001523 }
david_williams56777022005-04-11 06:21:55 +00001524 else if (regionType == DOMRegionContext.XML_ENTITY_REFERENCE || regionType == DOMRegionContext.XML_CHAR_REFERENCE) {
david_williams96213482004-11-11 09:07:12 +00001525 insertEntityRef(flatNode);
david_williams63f5c322005-03-15 06:02:57 +00001526 }
david_williams56777022005-04-11 06:21:55 +00001527 else if (regionType == DOMRegionContext.XML_DECLARATION_OPEN) {
david_williams96213482004-11-11 09:07:12 +00001528 insertDecl(flatNode);
david_williams63f5c322005-03-15 06:02:57 +00001529 }
david_williams56777022005-04-11 06:21:55 +00001530 else if (regionType == DOMRegionContext.XML_PI_OPEN) {
david_williams96213482004-11-11 09:07:12 +00001531 insertPI(flatNode);
david_williams63f5c322005-03-15 06:02:57 +00001532 }
david_williams56777022005-04-11 06:21:55 +00001533 else if (regionType == DOMRegionContext.XML_CDATA_OPEN) {
david_williams96213482004-11-11 09:07:12 +00001534 insertCDATASection(flatNode);
david_williams63f5c322005-03-15 06:02:57 +00001535 }
1536 else if (isNestedTag(regionType)) {
1537 insertNestedTag(flatNode);
1538 }
1539 else {
david_williams96213482004-11-11 09:07:12 +00001540 insertText(flatNode);
1541 }
1542 }
1543
david_williams63f5c322005-03-15 06:02:57 +00001544 protected boolean isNestedTag(String regionType) {
1545 boolean result = false;
1546 return result;
1547 }
1548
1549 protected boolean isNestedCommentText(String regionType) {
1550 boolean result = false;
1551 return result;
1552 }
1553
1554
1555 protected boolean isNestedCommentOpen(String regionType) {
1556 boolean result = false;
1557 return result;
1558 }
1559
1560 protected boolean isNestedTagName(String regionType) {
1561 boolean result = false;
1562 return result;
1563 }
1564
1565 protected boolean isNestedContent(String regionType) {
1566 boolean result = false;
1567 return result;
1568 }
1569
david_williams96213482004-11-11 09:07:12 +00001570 /**
david_williams63f5c322005-03-15 06:02:57 +00001571 * insertText method Can be called from subclasses, not to be overrided or
1572 * re-implemented.
david_williams96213482004-11-11 09:07:12 +00001573 *
david_williams96213482004-11-11 09:07:12 +00001574 */
david_williams63f5c322005-03-15 06:02:57 +00001575 protected void insertText(IStructuredDocumentRegion flatNode) {
david_williams96213482004-11-11 09:07:12 +00001576 TextImpl text = (TextImpl) this.context.findPreviousText();
1577 if (text != null) { // existing text found
1578 text.appendStructuredDocumentRegion(flatNode);
1579 // notify the change
1580 text.notifyValueChanged();
1581 return;
1582 }
1583
1584 // new text
david_williamsfa0ae312005-06-20 20:16:53 +00001585 text = (TextImpl) this.model.getDocument().createTextNode(null);
david_williams96213482004-11-11 09:07:12 +00001586 if (text == null)
1587 return;
1588 text.setStructuredDocumentRegion(flatNode);
1589 insertNode(text);
1590 }
1591
1592 /**
1593 */
david_williamsc39caaf2005-04-05 06:07:16 +00001594 protected boolean isEndTag(IDOMElement element) {
david_williamsfa0ae312005-06-20 20:16:53 +00001595 ModelParserAdapter adapter = getParserAdapter();
1596 if (adapter != null) {
1597 return adapter.isEndTag(element);
david_williams96213482004-11-11 09:07:12 +00001598 }
1599 return element.isEndTag();
1600 }
1601
1602 /**
1603 */
1604 private void promoteNodes(Node root, Node newParent, Node newNext, Node oldParent, Node next) {
1605 ElementImpl newElement = null;
1606 if (newParent.getNodeType() == Node.ELEMENT_NODE) {
1607 newElement = (ElementImpl) newParent;
1608 }
1609
1610 Node rootParent = root.getParentNode();
1611 while (oldParent != rootParent) {
1612 while (next != null) {
1613 boolean done = false;
1614 boolean endTag = false;
1615 if (next.getNodeType() == Node.ELEMENT_NODE) {
1616 ElementImpl nextElement = (ElementImpl) next;
1617 if (!nextElement.hasStartTag()) {
1618 Node nextChild = nextElement.getFirstChild();
1619 if (nextChild != null) {
1620 // promote children
1621 next = nextChild;
1622 oldParent = nextElement;
1623 continue;
1624 }
1625
1626 if (nextElement.hasEndTag()) {
1627 if (nextElement.matchEndTag(newElement)) {
1628 endTag = true;
1629 }
david_williams63f5c322005-03-15 06:02:57 +00001630 }
1631 else {
david_williams96213482004-11-11 09:07:12 +00001632 // remove implicit element
1633 next = nextElement.getNextSibling();
1634 oldParent.removeChild(nextElement);
1635 done = true;
1636 }
1637 }
1638 }
1639
1640 if (!done) {
1641 if (!endTag && newElement != null && !canContain(newElement, next)) {
1642 newParent = newElement.getParentNode();
1643 if (newParent == null)
1644 return; // error
1645 Node elementNext = newElement.getNextSibling();
1646 // promote siblings
1647 promoteNodes(newElement, newParent, elementNext, newElement, newNext);
1648 newNext = newElement.getNextSibling();
1649 if (newParent.getNodeType() == Node.ELEMENT_NODE) {
1650 newElement = (ElementImpl) newParent;
david_williams63f5c322005-03-15 06:02:57 +00001651 }
1652 else {
david_williams96213482004-11-11 09:07:12 +00001653 newElement = null;
1654 }
1655 continue;
1656 }
1657
1658 Node child = next;
1659 next = next.getNextSibling();
1660 oldParent.removeChild(child);
1661 insertNode(newParent, child, newNext);
1662 Node childParent = child.getParentNode();
1663 if (childParent != newParent) {
1664 newParent = childParent;
1665 newElement = (ElementImpl) newParent;
1666 newNext = child.getNextSibling();
1667 }
1668 }
1669 }
1670
1671 if (oldParent.getNodeType() != Node.ELEMENT_NODE)
1672 return;
1673 ElementImpl oldElement = (ElementImpl) oldParent;
1674 oldParent = oldElement.getParentNode();
1675 if (oldParent == null)
1676 return; // error
1677 next = oldElement.getNextSibling();
1678
1679 if (oldElement.hasEndTag()) {
1680 Element end = null;
1681 if (!oldElement.hasChildNodes() && !oldElement.hasStartTag()) {
1682 oldParent.removeChild(oldElement);
1683 end = oldElement;
david_williams63f5c322005-03-15 06:02:57 +00001684 }
1685 else {
david_williams96213482004-11-11 09:07:12 +00001686 end = oldElement.removeEndTag();
1687 }
1688 if (end != null) {
1689 insertNode(newParent, end, newNext);
1690 Node endParent = end.getParentNode();
1691 if (endParent != newParent) {
1692 newParent = endParent;
1693 newElement = (ElementImpl) newParent;
1694 newNext = end.getNextSibling();
1695 }
1696 }
1697 }
1698 }
1699 }
1700
1701 /**
1702 * removeEndTag method
1703 *
1704 * @param element
1705 * org.w3c.dom.Element
1706 */
1707 private void removeEndTag(Element element) {
1708 if (element == null)
1709 return;
1710 if (this.context == null)
1711 return;
1712
1713 Node parent = element.getParentNode();
1714 if (parent == null)
1715 return; // error
1716
1717 if (!((ElementImpl) element).isContainer()) {
1718 // just update context
1719 Node elementNext = element.getNextSibling();
1720 if (elementNext != null)
1721 this.context.setNextNode(elementNext);
1722 else
1723 this.context.setParentNode(parent);
1724 return;
1725 }
1726
1727 // demote siblings
1728 Node next = element.getNextSibling();
1729 ElementImpl newElement = (ElementImpl) element;
1730 // find new parent
1731 for (Node last = newElement.getLastChild(); last != null; last = last.getLastChild()) {
1732 if (last.getNodeType() != Node.ELEMENT_NODE)
1733 break;
1734 ElementImpl lastElement = (ElementImpl) last;
1735 if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer())
1736 break;
1737 newElement = lastElement;
1738 }
1739 Node lastChild = newElement.getLastChild();
1740 demoteNodes(element, newElement, parent, next);
1741
1742 // update context
1743 Node newNext = null;
1744 if (lastChild != null)
1745 newNext = lastChild.getNextSibling();
1746 else
1747 newNext = newElement.getFirstChild();
1748 if (newNext != null)
1749 this.context.setNextNode(newNext);
1750 else
1751 this.context.setParentNode(newElement);
1752 }
1753
1754 /**
1755 * Remove the specified node if it is no longer required implicit tag with
1756 * remaining child nodes promoted.
1757 */
1758 private Element removeImplicitElement(Node parent) {
1759 if (parent == null)
1760 return null;
1761 if (parent.getNodeType() != Node.ELEMENT_NODE)
1762 return null;
1763 ElementImpl element = (ElementImpl) parent;
1764 if (!element.isImplicitTag())
1765 return null;
1766 if (canBeImplicitTag(element))
1767 return null;
1768
1769 Node elementParent = element.getParentNode();
1770 if (elementParent == null)
1771 return null; // error
1772 Node firstChild = element.getFirstChild();
1773 Node child = firstChild;
1774 Node elementNext = element.getNextSibling();
1775 while (child != null) {
1776 Node nextChild = child.getNextSibling();
1777 element.removeChild(child);
1778 elementParent.insertBefore(child, elementNext);
1779 child = nextChild;
1780 }
1781
1782 // reset context
1783 if (this.context.getParentNode() == element) {
1784 Node oldNext = this.context.getNextNode();
1785 if (oldNext != null) {
1786 this.context.setNextNode(oldNext);
david_williams63f5c322005-03-15 06:02:57 +00001787 }
1788 else {
david_williams96213482004-11-11 09:07:12 +00001789 if (elementNext != null) {
1790 this.context.setNextNode(elementNext);
david_williams63f5c322005-03-15 06:02:57 +00001791 }
1792 else {
david_williams96213482004-11-11 09:07:12 +00001793 this.context.setParentNode(elementParent);
1794 }
1795 }
david_williams63f5c322005-03-15 06:02:57 +00001796 }
1797 else if (this.context.getNextNode() == element) {
david_williams96213482004-11-11 09:07:12 +00001798 if (firstChild != null) {
1799 this.context.setNextNode(firstChild);
david_williams63f5c322005-03-15 06:02:57 +00001800 }
1801 else {
david_williams96213482004-11-11 09:07:12 +00001802 if (elementNext != null) {
1803 this.context.setNextNode(elementNext);
david_williams63f5c322005-03-15 06:02:57 +00001804 }
1805 else {
david_williams96213482004-11-11 09:07:12 +00001806 this.context.setParentNode(elementParent);
1807 }
1808 }
1809 }
1810
1811 removeNode(element);
1812 return element;
1813 }
1814
1815 /**
1816 * removeNode method
1817 *
1818 * @param node
1819 * org.w3c.dom.Node
1820 */
1821 private void removeNode(Node node) {
1822 if (node == null)
1823 return;
1824 if (this.context == null)
1825 return;
1826
1827 Node parent = node.getParentNode();
1828 if (parent == null)
1829 return;
1830 Node next = node.getNextSibling();
1831 Node prev = node.getPreviousSibling();
1832
1833 // update context
1834 Node oldParent = this.context.getParentNode();
1835 if (node == oldParent) {
1836 if (next != null)
1837 this.context.setNextNode(next);
1838 else
1839 this.context.setParentNode(parent);
david_williams63f5c322005-03-15 06:02:57 +00001840 }
1841 else {
david_williams96213482004-11-11 09:07:12 +00001842 Node oldNext = this.context.getNextNode();
1843 if (node == oldNext) {
1844 this.context.setNextNode(next);
1845 }
1846 }
1847
1848 parent.removeChild(node);
1849
1850 if (removeImplicitElement(parent) != null)
1851 return;
1852
1853 // demote sibling
1854 if (prev != null && prev.getNodeType() == Node.ELEMENT_NODE) {
1855 ElementImpl newElement = (ElementImpl) prev;
1856 if (!newElement.hasEndTag() && !newElement.isEmptyTag() && newElement.isContainer()) {
1857 // find new parent
1858 for (Node last = newElement.getLastChild(); last != null; last = last.getLastChild()) {
1859 if (last.getNodeType() != Node.ELEMENT_NODE)
1860 break;
1861 ElementImpl lastElement = (ElementImpl) last;
1862 if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer())
1863 break;
1864 newElement = lastElement;
1865 }
1866 Node lastChild = newElement.getLastChild();
1867 demoteNodes(prev, newElement, parent, next);
1868
1869 // update context
1870 Node newNext = null;
1871 if (lastChild != null)
1872 newNext = lastChild.getNextSibling();
1873 else
1874 newNext = newElement.getFirstChild();
1875 if (newNext != null)
1876 this.context.setNextNode(newNext);
1877 else
1878 this.context.setParentNode(newElement);
1879 }
1880 }
1881 }
1882
1883 /**
1884 * removeStartTag method
1885 *
1886 * @param element
1887 * org.w3c.dom.Element
1888 */
1889 private void removeStartTag(Element element) {
1890 if (element == null)
1891 return;
1892 if (this.context == null)
1893 return;
1894
1895 // for implicit tag
1896 ElementImpl oldElement = (ElementImpl) element;
1897 if (canBeImplicitTag(oldElement)) {
1898 Node newParent = null;
1899 Node prev = oldElement.getPreviousSibling();
1900 if (prev != null && prev.getNodeType() == Node.ELEMENT_NODE) {
1901 ElementImpl prevElement = (ElementImpl) prev;
1902 if (!prevElement.hasEndTag()) {
1903 if (prevElement.hasStartTag() || prevElement.matchTagName(oldElement.getTagName())) {
1904 newParent = prevElement;
1905 }
1906 }
1907 }
1908 if (newParent == null) {
1909 // this element should stay as implicit tag
1910 // just remove all attributes
1911 oldElement.removeStartTag();
1912
1913 // update context
1914 Node child = oldElement.getFirstChild();
1915 if (child != null) {
1916 this.context.setNextNode(child);
david_williams63f5c322005-03-15 06:02:57 +00001917 }
1918 else if (oldElement.hasEndTag()) {
david_williams96213482004-11-11 09:07:12 +00001919 this.context.setParentNode(oldElement);
1920 }
1921 return;
1922 }
1923 }
1924 // for comment tag
1925 if (oldElement.isCommentTag())
1926 oldElement.removeStartTag();
1927
1928 // promote children
1929 Node elementParent = element.getParentNode();
1930 Node parent = elementParent;
1931 if (parent == null)
1932 return;
1933 Node first = element.getFirstChild();
david_williams63f5c322005-03-15 06:02:57 +00001934 Node firstElement = null; // for the case first is removed as end
1935 // tag
david_williams96213482004-11-11 09:07:12 +00001936 if (first != null) {
1937 // find new parent for children
1938 ElementImpl newElement = null;
1939 for (Node last = element.getPreviousSibling(); last != null; last = last.getLastChild()) {
1940 if (last.getNodeType() != Node.ELEMENT_NODE)
1941 break;
1942 ElementImpl lastElement = (ElementImpl) last;
1943 if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer())
1944 break;
1945 newElement = lastElement;
1946 }
1947 Node next = first;
1948 if (newElement != null) {
1949 while (next != null) {
1950 if (!newElement.hasEndTag() && newElement.hasStartTag() && next.getNodeType() == Node.ELEMENT_NODE) {
1951 ElementImpl nextElement = (ElementImpl) next;
1952 if (!nextElement.hasStartTag() && nextElement.hasEndTag() && nextElement.matchEndTag(newElement)) {
1953 // stop at the matched invalid end tag
1954 Node elementChild = nextElement.getFirstChild();
1955 while (elementChild != null) {
1956 Node nextChild = elementChild.getNextSibling();
1957 nextElement.removeChild(elementChild);
1958 newElement.appendChild(elementChild);
1959 elementChild = nextChild;
1960 }
1961
1962 next = nextElement.getNextSibling();
1963 element.removeChild(nextElement);
1964 newElement.addEndTag(nextElement);
1965 if (nextElement == first)
1966 firstElement = newElement;
1967
1968 Node newParent = newElement.getParentNode();
1969 if (newParent == parent)
1970 break;
1971 if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE)
1972 break; // error
1973 newElement = (ElementImpl) newParent;
1974 continue;
1975 }
1976 }
1977 if (!canContain(newElement, next)) {
1978 Node newParent = newElement.getParentNode();
1979 if (newParent == parent)
1980 break;
1981 if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE)
1982 break; // error
1983 newElement = (ElementImpl) newParent;
1984 continue;
1985 }
1986 Node child = next;
1987 next = next.getNextSibling();
1988 element.removeChild(child);
1989 newElement.appendChild(child);
1990 }
1991 newElement = null;
1992 }
1993 if (parent.getNodeType() == Node.ELEMENT_NODE) {
1994 newElement = (ElementImpl) parent;
1995 }
1996 while (next != null) {
1997 if (newElement == null || canContain(newElement, next)) {
1998 Node child = next;
1999 next = next.getNextSibling();
2000 element.removeChild(child);
2001 parent.insertBefore(child, element);
2002 continue;
2003 }
2004
2005 parent = newElement.getParentNode();
2006 if (parent == null)
2007 return;
2008
2009 // promote siblings
2010 Node newNext = newElement.getNextSibling();
2011 Node child = element;
2012 while (child != null) {
2013 Node nextChild = child.getNextSibling();
2014 newElement.removeChild(child);
2015 parent.insertBefore(child, newNext);
2016 child = nextChild;
2017 }
2018
2019 // leave the old end tag where it is
2020 if (newElement.hasEndTag()) {
2021 Element end = newElement.removeEndTag();
2022 if (end != null) {
2023 parent.insertBefore(end, newNext);
2024 }
2025 }
2026 if (!newElement.hasStartTag()) {
2027 // implicit element
2028 if (!newElement.hasChildNodes()) {
2029 parent.removeChild(newElement);
2030 }
2031 }
2032
2033 if (parent.getNodeType() == Node.ELEMENT_NODE) {
2034 newElement = (ElementImpl) parent;
david_williams63f5c322005-03-15 06:02:57 +00002035 }
2036 else {
david_williams96213482004-11-11 09:07:12 +00002037 newElement = null;
2038 }
2039 }
2040 }
2041
2042 Node newNext = element;
2043 Node startElement = null; // for the case element is removed as end
2044 // tag
2045 if (oldElement.hasEndTag()) {
2046 // find new parent for invalid end tag and siblings
2047 ElementImpl newElement = null;
2048 for (Node last = element.getPreviousSibling(); last != null; last = last.getLastChild()) {
2049 if (last.getNodeType() != Node.ELEMENT_NODE)
2050 break;
2051 ElementImpl lastElement = (ElementImpl) last;
2052 if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer())
2053 break;
2054 newElement = lastElement;
2055 }
2056 if (newElement != null) {
2057 // demote invalid end tag and sibling
2058 Node next = element;
2059 while (next != null) {
2060 if (!newElement.hasEndTag() && newElement.hasStartTag() && next.getNodeType() == Node.ELEMENT_NODE) {
2061 ElementImpl nextElement = (ElementImpl) next;
2062 if (!nextElement.hasStartTag() && nextElement.hasEndTag() && nextElement.matchEndTag(newElement)) {
2063 // stop at the matched invalid end tag
2064 Node elementChild = nextElement.getFirstChild();
2065 while (elementChild != null) {
2066 Node nextChild = elementChild.getNextSibling();
2067 nextElement.removeChild(elementChild);
2068 newElement.appendChild(elementChild);
2069 elementChild = nextChild;
2070 }
2071
2072 next = nextElement.getNextSibling();
2073 parent.removeChild(nextElement);
2074 newElement.addEndTag(nextElement);
2075 if (nextElement == newNext)
2076 startElement = newElement;
2077
2078 Node newParent = newElement.getParentNode();
2079 if (newParent == parent)
2080 break;
2081 if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE)
2082 break; // error
2083 newElement = (ElementImpl) newParent;
2084 continue;
2085 }
2086 }
2087 if (!canContain(newElement, next)) {
2088 Node newParent = newElement.getParentNode();
2089 if (newParent == parent)
2090 break;
2091 if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE)
2092 break; // error
2093 newElement = (ElementImpl) newParent;
2094 continue;
2095 }
2096 Node child = next;
2097 next = next.getNextSibling();
2098 parent.removeChild(child);
2099 if (child == oldElement) {
2100 if (!oldElement.isCommentTag()) {
2101 // clone (re-create) end tag
2102 Element end = oldElement.removeEndTag();
2103 if (end != null) {
2104 child = end;
2105 newNext = end;
2106 }
2107 }
2108 }
2109 newElement.appendChild(child);
2110 }
david_williams63f5c322005-03-15 06:02:57 +00002111 }
2112 else {
david_williams96213482004-11-11 09:07:12 +00002113 if (!oldElement.isCommentTag()) {
2114 // clone (re-create) end tag
2115 Element end = oldElement.removeEndTag();
2116 if (end != null) {
2117 parent.insertBefore(end, oldElement);
2118 parent.removeChild(oldElement);
2119 newNext = end;
2120 }
2121 }
2122 }
david_williams63f5c322005-03-15 06:02:57 +00002123 }
2124 else {
david_williams96213482004-11-11 09:07:12 +00002125 newNext = oldElement.getNextSibling();
2126 parent.removeChild(oldElement);
2127 }
2128
2129 // update context
2130 Node oldParent = this.context.getParentNode();
2131 Node oldNext = this.context.getNextNode();
2132 if (element == oldParent) {
2133 if (oldNext != null) {
2134 this.context.setNextNode(oldNext); // reset for new parent
david_williams63f5c322005-03-15 06:02:57 +00002135 }
2136 else if (newNext != null) {
david_williams96213482004-11-11 09:07:12 +00002137 this.context.setNextNode(newNext);
david_williams63f5c322005-03-15 06:02:57 +00002138 }
2139 else {
david_williams96213482004-11-11 09:07:12 +00002140 this.context.setParentNode(parent);
2141 }
david_williams63f5c322005-03-15 06:02:57 +00002142 }
2143 else if (element == oldNext) {
david_williams96213482004-11-11 09:07:12 +00002144 if (firstElement != null) {
2145 this.context.setParentNode(firstElement);
david_williams63f5c322005-03-15 06:02:57 +00002146 }
2147 else if (first != null) {
david_williams96213482004-11-11 09:07:12 +00002148 this.context.setNextNode(first);
david_williams63f5c322005-03-15 06:02:57 +00002149 }
2150 else if (startElement != null) {
david_williams96213482004-11-11 09:07:12 +00002151 this.context.setParentNode(startElement);
david_williams63f5c322005-03-15 06:02:57 +00002152 }
2153 else {
david_williams96213482004-11-11 09:07:12 +00002154 this.context.setNextNode(newNext);
2155 }
2156 }
2157
2158 removeImplicitElement(elementParent);
2159 }
2160
2161 /**
2162 * removeStructuredDocumentRegion method
2163 *
david_williams96213482004-11-11 09:07:12 +00002164 */
2165 private void removeStructuredDocumentRegion(IStructuredDocumentRegion oldStructuredDocumentRegion) {
2166 NodeImpl next = (NodeImpl) this.context.getNextNode();
2167 if (next != null) {
2168 short nodeType = next.getNodeType();
2169 if (nodeType != Node.ELEMENT_NODE) {
2170 IStructuredDocumentRegion flatNode = next.getStructuredDocumentRegion();
2171 if (flatNode == oldStructuredDocumentRegion) {
2172 removeNode(next);
2173 return;
2174 }
2175 if (nodeType != Node.TEXT_NODE) {
2176 throw new StructuredDocumentRegionManagementException();
2177 }
2178 if (flatNode == null) {
2179 // this is the case for empty Text
2180 // remove and continue
2181 removeNode(next);
2182 removeStructuredDocumentRegion(oldStructuredDocumentRegion);
2183 return;
2184 }
2185 TextImpl text = (TextImpl) next;
2186 boolean isShared = text.isSharingStructuredDocumentRegion(oldStructuredDocumentRegion);
2187 if (isShared) {
2188 // make sure there is next Text node sharing this
2189 TextImpl nextText = (TextImpl) this.context.findNextText();
2190 if (nextText == null || !nextText.hasStructuredDocumentRegion(oldStructuredDocumentRegion)) {
2191 isShared = false;
2192 }
2193 }
2194 oldStructuredDocumentRegion = text.removeStructuredDocumentRegion(oldStructuredDocumentRegion);
2195 if (oldStructuredDocumentRegion == null) {
2196 throw new StructuredDocumentRegionManagementException();
2197 }
2198 if (text.getStructuredDocumentRegion() == null) {
2199 // this is the case partial IStructuredDocumentRegion is
2200 // removed
2201 removeNode(text);
david_williams63f5c322005-03-15 06:02:57 +00002202 }
2203 else {
david_williams96213482004-11-11 09:07:12 +00002204 // notify the change
2205 text.notifyValueChanged();
2206 }
2207 // if shared, continue to remove IStructuredDocumentRegion
2208 // from them
2209 if (isShared)
2210 removeStructuredDocumentRegion(oldStructuredDocumentRegion);
2211 return;
2212 }
2213
2214 ElementImpl element = (ElementImpl) next;
2215 if (element.hasStartTag()) {
2216 IStructuredDocumentRegion flatNode = element.getStartStructuredDocumentRegion();
2217 if (flatNode != oldStructuredDocumentRegion) {
2218 throw new StructuredDocumentRegionManagementException();
2219 }
2220 if (element.hasEndTag() || element.hasChildNodes()) {
2221 element.setStartStructuredDocumentRegion(null);
2222 removeStartTag(element);
david_williams63f5c322005-03-15 06:02:57 +00002223 }
2224 else {
david_williams96213482004-11-11 09:07:12 +00002225 removeNode(element);
2226 }
david_williams63f5c322005-03-15 06:02:57 +00002227 }
2228 else {
david_williams96213482004-11-11 09:07:12 +00002229 Node child = element.getFirstChild();
2230 if (child != null) {
2231 this.context.setNextNode(child);
2232 removeStructuredDocumentRegion(oldStructuredDocumentRegion);
2233 return;
2234 }
2235
2236 if (!element.hasEndTag()) {
2237 // implicit element
2238 removeNode(element);
2239 removeStructuredDocumentRegion(oldStructuredDocumentRegion);
2240 return;
2241 }
2242
2243 IStructuredDocumentRegion flatNode = element.getEndStructuredDocumentRegion();
2244 if (flatNode != oldStructuredDocumentRegion) {
2245 throw new StructuredDocumentRegionManagementException();
2246 }
2247 removeNode(element);
2248 }
2249 return;
2250 }
2251
2252 Node parent = this.context.getParentNode();
2253 if (parent == null || parent.getNodeType() != Node.ELEMENT_NODE) {
2254 throw new StructuredDocumentRegionManagementException();
2255 }
2256
2257 ElementImpl end = (ElementImpl) parent;
2258 if (end.hasEndTag()) {
2259 IStructuredDocumentRegion flatNode = end.getEndStructuredDocumentRegion();
2260 if (flatNode != oldStructuredDocumentRegion) {
2261 throw new StructuredDocumentRegionManagementException();
2262 }
2263 if (!end.hasStartTag() && !end.hasChildNodes()) {
2264 this.context.setNextNode(end);
2265 removeNode(end);
david_williams63f5c322005-03-15 06:02:57 +00002266 }
2267 else {
david_williams96213482004-11-11 09:07:12 +00002268 end.setEndStructuredDocumentRegion(null);
2269 removeEndTag(end);
2270 }
2271 return;
2272 }
2273
2274 next = (NodeImpl) end.getNextSibling();
2275 if (next != null) {
2276 this.context.setNextNode(next);
2277 removeStructuredDocumentRegion(oldStructuredDocumentRegion);
2278 return;
2279 }
2280
david_williamsc040dc52005-11-19 23:07:34 +00002281 parent = end.getParentNode();
david_williams96213482004-11-11 09:07:12 +00002282 if (parent != null) {
2283 this.context.setParentNode(parent);
2284 removeStructuredDocumentRegion(oldStructuredDocumentRegion);
2285 return;
2286 }
2287 }
2288
2289 /**
2290 * replaceRegions method
2291 *
david_williams96213482004-11-11 09:07:12 +00002292 * @param newRegions
2293 * java.util.Vector
2294 * @param oldRegions
2295 * java.util.Vector
2296 */
2297 void replaceRegions(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) {
2298 if (flatNode == null)
2299 return;
david_williamsfa0ae312005-06-20 20:16:53 +00002300 if (this.model.getDocument() == null)
david_williams96213482004-11-11 09:07:12 +00002301 return;
david_williamsfa0ae312005-06-20 20:16:53 +00002302 this.context = new XMLModelContext(this.model.getDocument());
david_williams96213482004-11-11 09:07:12 +00002303
2304 // optimize typical cases
2305 String regionType = StructuredDocumentRegionUtil.getFirstRegionType(flatNode);
david_williams56777022005-04-11 06:21:55 +00002306 if (regionType == DOMRegionContext.XML_TAG_OPEN) {
david_williams96213482004-11-11 09:07:12 +00002307 changeStartTag(flatNode, newRegions, oldRegions);
david_williams63f5c322005-03-15 06:02:57 +00002308 }
david_williams56777022005-04-11 06:21:55 +00002309 else if (regionType == DOMRegionContext.XML_END_TAG_OPEN) {
david_williams96213482004-11-11 09:07:12 +00002310 changeEndTag(flatNode, newRegions, oldRegions);
david_williams63f5c322005-03-15 06:02:57 +00002311 }
2312 else {
david_williams96213482004-11-11 09:07:12 +00002313 changeStructuredDocumentRegion(flatNode);
2314 }
2315 }
2316
2317 /**
2318 * replaceStructuredDocumentRegions method
2319 *
david_williams96213482004-11-11 09:07:12 +00002320 */
2321 void replaceStructuredDocumentRegions(IStructuredDocumentRegionList newStructuredDocumentRegions, IStructuredDocumentRegionList oldStructuredDocumentRegions) {
david_williamsfa0ae312005-06-20 20:16:53 +00002322 if (this.model.getDocument() == null)
david_williams96213482004-11-11 09:07:12 +00002323 return;
david_williamsfa0ae312005-06-20 20:16:53 +00002324 this.context = new XMLModelContext(this.model.getDocument());
david_williams96213482004-11-11 09:07:12 +00002325
2326 int newCount = (newStructuredDocumentRegions != null ? newStructuredDocumentRegions.getLength() : 0);
2327 int oldCount = (oldStructuredDocumentRegions != null ? oldStructuredDocumentRegions.getLength() : 0);
2328
2329 if (oldCount > 0) {
2330 setupContext(oldStructuredDocumentRegions.item(0));
2331 // Node startParent = this.context.getParentNode();
2332
2333 Enumeration e = oldStructuredDocumentRegions.elements();
2334 while (e.hasMoreElements()) {
2335 IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement();
2336 if (flatNode == null)
2337 continue;
2338 removeStructuredDocumentRegion(flatNode);
2339 }
david_williams63f5c322005-03-15 06:02:57 +00002340 }
2341 else {
david_williams96213482004-11-11 09:07:12 +00002342 if (newCount == 0)
2343 return;
2344 setupContext(newStructuredDocumentRegions.item(0));
2345 }
2346 // make sure the parent is set to deepest level
2347 // when end tag has been removed
2348 this.context.setLast();
2349
2350 if (newCount > 0) {
2351 Enumeration e = newStructuredDocumentRegions.elements();
2352 while (e.hasMoreElements()) {
2353 IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement();
2354 if (flatNode == null)
2355 continue;
2356 insertStructuredDocumentRegion(flatNode);
2357 }
2358 }
2359
2360 cleanupText();
2361 cleanupEndTag();
2362 }
2363
2364 /**
2365 * setupContext method
2366 *
david_williams96213482004-11-11 09:07:12 +00002367 */
2368 private void setupContext(IStructuredDocumentRegion startStructuredDocumentRegion) {
2369 int offset = startStructuredDocumentRegion.getStart();
2370 if (offset < 0)
2371 return;
2372 NodeImpl root = (NodeImpl) this.context.getRootNode();
2373 if (root == null)
2374 return;
2375
2376 if (offset == 0) {
2377 // at the beggining of document
2378 Node child = root.getFirstChild();
2379 if (child != null)
2380 this.context.setNextNode(child);
2381 else
2382 this.context.setParentNode(root);
2383 return;
2384 }
2385
2386 NodeImpl node = (NodeImpl) root.getNodeAt(offset);
2387 if (node == null) {
2388 // might be at the end of document
2389 this.context.setParentNode(root);
2390 this.context.setLast();
2391 return;
2392 }
2393
2394 if (offset == node.getStartOffset()) {
2395 this.context.setNextNode(node);
2396 return;
2397 }
2398
2399 if (node.getNodeType() == Node.TEXT_NODE) {
2400 TextImpl text = (TextImpl) node;
2401 Text nextText = text.splitText(startStructuredDocumentRegion);
2402 // notify the change
2403 text.notifyValueChanged();
2404 if (nextText == null)
2405 return; // error
2406 this.context.setNextNode(nextText);
2407 return;
2408 }
2409
2410 for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
2411 if (offset >= ((NodeImpl) child).getEndOffset())
2412 continue;
2413 this.context.setNextNode(child);
2414 return;
2415 }
2416 this.context.setParentNode(node);
2417 this.context.setLast();
2418 }
david_williams63f5c322005-03-15 06:02:57 +00002419
2420 protected XMLModelContext getContext() {
2421 return context;
2422 }
2423
david_williams96213482004-11-11 09:07:12 +00002424}