1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sourceforge.jeuclid.biparser;
20
21 import org.w3c.dom.Document;
22 import org.w3c.dom.Element;
23 import org.w3c.dom.Node;
24 import org.xml.sax.Attributes;
25
26
27
28
29
30
31
32 public final class BiNode extends AbstractBiNode {
33
34
35 private IBiNode child;
36
37
38 private final int childOffset;
39
40
41 private boolean invalid;
42
43
44 private String namespaceURI;
45
46
47 private String eName;
48
49
50 private Attributes attrs;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public BiNode(final int co, final String ns, final String n,
66 final Attributes a) {
67 this.childOffset = co;
68 this.namespaceURI = ns;
69 this.eName = n;
70 this.attrs = a;
71 }
72
73
74
75
76
77
78 public String getNodeName() {
79 final String ret;
80
81 if (this.eName == null) {
82 if (this.getNode() == null) {
83 ret = null;
84 } else {
85 ret = this.getNode().getNodeName();
86 }
87 } else {
88 ret = this.eName;
89 }
90
91 return ret;
92 }
93
94
95
96
97
98
99
100 public void addChild(final IBiNode c) {
101
102 if (this.child == null) {
103 this.setChild(c);
104 } else {
105
106 this.child.addSibling(c);
107 }
108 }
109
110
111
112
113
114
115 public IBiNode getChild() {
116 return this.child;
117 }
118
119
120
121
122
123
124
125 public void setChild(final IBiNode c) {
126 if (c != null) {
127 c.setPrevious(this);
128 }
129
130 this.child = c;
131 }
132
133
134
135
136
137
138 public BiType getType() {
139 return BiType.NODE;
140 }
141
142
143 public void insert(final BiTree biTree, final int offset, final int length,
144 final int totalOffset) throws ReparseException,
145 NonIncrementalElementException {
146
147
148
149
150 if (offset >= this.getLength()) {
151
152
153 if (offset == this.getLength() && this.invalid) {
154 throw new ReparseException();
155 }
156
157
158 this.forwardToSibling(true, biTree, offset - this.getLength(),
159 length, totalOffset + this.getLength());
160
161 } else if (this.child != null && !this.invalid
162 && offset >= this.childOffset
163 && offset <= this.childOffset + this.getLengthOfChildren()) {
164
165 try {
166 this.child.insert(biTree, offset - this.childOffset, length,
167 totalOffset + this.childOffset);
168 } catch (final ReparseException ex) {
169 this.parseAndReplace(
170 biTree,
171 biTree.getText().substring(totalOffset,
172 totalOffset + this.getLength() + length),
173 length);
174 }
175 } else if (offset == 0) {
176
177 throw new ReparseException();
178 } else {
179
180 this.parseAndReplace(
181 biTree,
182 biTree.getText().substring(totalOffset,
183 totalOffset + this.getLength() + length), length);
184 }
185 }
186
187
188 public void remove(final BiTree biTree, final int offset, final int length,
189 final int totalOffset) throws ReparseException,
190 NonIncrementalElementException {
191
192
193
194
195 if (offset == 0 && length >= this.getLength()) {
196 throw new ReparseException();
197
198 } else if (offset >= this.getLength()) {
199
200 this.forwardToSibling(false, biTree, offset - this.getLength(),
201 length, totalOffset + this.getLength());
202
203 } else if (this.child != null
204 && !this.invalid
205 && offset >= this.childOffset
206 && offset + length <= this.childOffset
207 + this.getLengthOfChildren()) {
208
209 try {
210 this.child.remove(biTree, offset - this.childOffset, length,
211 totalOffset + this.childOffset);
212 } catch (final ReparseException ex) {
213 this.parseAndReplace(
214 biTree,
215 biTree.getText().substring(totalOffset,
216 totalOffset + this.getLength() - length),
217 -length);
218 }
219 } else {
220
221 this.parseAndReplace(
222 biTree,
223 biTree.getText().substring(totalOffset,
224 totalOffset + this.getLength() - length), -length);
225 }
226 }
227
228
229
230
231
232
233
234
235 private void makeInvalidNode(final Document doc) {
236 Element element;
237
238
239 element = doc.createElement("mi");
240 element.setAttribute("mathcolor", "#F00");
241 element.appendChild(doc.createTextNode("#"));
242
243 if (this.getNode().getParentNode() == null) {
244 doc.replaceChild(element, this.getNode());
245 } else {
246 this.getNode().getParentNode()
247 .replaceChild(element, this.getNode());
248 }
249
250
251 this.setNode(element);
252 this.child = null;
253 this.invalid = true;
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270 private void parseAndReplace(final BiTree biTree, final String text,
271 final int length) throws ReparseException,
272 NonIncrementalElementException {
273 BiTree treePart;
274 Node domValid;
275 BiNode parent;
276 final boolean invalidSibling;
277 final boolean invalidPrevious;
278
279 treePart = SAXBiParser.getInstance().parse(text);
280
281
282 if (treePart == null) {
283 invalidSibling = this.getSibling() != null
284 && this.getSibling().getType() == BiType.NODE
285 && ((BiNode) this.getSibling()).invalid;
286
287 invalidPrevious = this.getPrevious() != null
288 && this.getPrevious().getType() == BiType.NODE
289 && ((BiNode) this.getPrevious()).invalid;
290
291
292
293 if (invalidPrevious || invalidSibling) {
294 throw new ReparseException();
295 }
296
297 if (!this.invalid) {
298 this.makeInvalidNode((Document) biTree.getDocument());
299 }
300
301 this.changeLengthRec(length);
302
303 } else {
304
305 parent = this.getParent();
306 domValid = treePart.getDOMTree((Document) biTree.getDocument());
307 treePart.getRoot().addSibling(this.getSibling());
308
309
310 if (parent == null) {
311
312
313 if (this.getPrevious() == null) {
314 biTree.setRoot(treePart.getRoot());
315 } else {
316
317 this.getPrevious().setSibling(treePart.getRoot());
318 }
319
320
321 biTree.getDocument().replaceChild(domValid, this.getNode());
322
323 } else {
324 if (this.getPrevious() == parent) {
325
326 this.getParent().setChild(treePart.getRoot());
327 } else {
328
329 this.getPrevious().setSibling(treePart.getRoot());
330 }
331
332
333 parent.getNode().replaceChild(domValid, this.getNode());
334 parent.changeLengthRec(length);
335 }
336
337 this.invalid = false;
338 }
339 }
340
341
342
343
344
345
346 public int getLengthOfChildren() {
347 int length = 0;
348 IBiNode childTmp;
349
350 if (this.child != null) {
351
352 length += this.child.getLength();
353
354 childTmp = this.child.getSibling();
355 while (childTmp != null) {
356
357 length += childTmp.getLength();
358 childTmp = childTmp.getSibling();
359 }
360
361 }
362
363 return length;
364 }
365
366
367
368
369
370
371
372
373 public Node createDOMSubtree(final Document doc) {
374 int i;
375 String aName;
376 Node childNode;
377 Element element;
378 IBiNode tmp;
379
380 element = doc.createElementNS(this.namespaceURI, this.eName);
381
382
383 if (this.attrs != null) {
384 for (i = 0; i < this.attrs.getLength(); i++) {
385 aName = this.attrs.getLocalName(i);
386
387 if ("".equals(aName)) {
388 aName = this.attrs.getQName(i);
389 }
390
391 element.setAttribute(aName, this.attrs.getValue(i));
392 }
393 }
394
395
396 if (this.child != null) {
397 tmp = this.child;
398
399 while (tmp != null) {
400 childNode = tmp.createDOMSubtree(doc);
401
402 if (childNode != null) {
403 element.appendChild(childNode);
404 }
405
406 tmp = tmp.getSibling();
407 }
408 }
409
410 this.namespaceURI = null;
411 this.eName = null;
412 this.attrs = null;
413
414 this.setNode(element);
415 return element;
416 }
417
418
419 @Override
420 public TextPosition searchNode(final Node node, final int totalOffset) {
421 TextPosition result;
422
423
424 result = super.searchNode(node, totalOffset);
425
426
427 if (result == null && this.child != null) {
428 result = this.child
429 .searchNode(node, totalOffset + this.childOffset);
430 }
431
432
433 if (result == null && this.getSibling() != null) {
434 result = this.getSibling().searchNode(node,
435 totalOffset + this.getLength());
436 }
437
438 return result;
439 }
440
441 @Override
442 public String toString() {
443 final StringBuffer sb = new StringBuffer(32);
444
445 sb.append('[');
446 sb.append(this.invalid ? "INVALID " : "");
447 sb.append("NODE length: ");
448 sb.append(this.getLength());
449
450 if (!this.invalid) {
451 sb.append(" <");
452 sb.append(this.getNodeName());
453 sb.append("> tag: ");
454 sb.append(this.childOffset);
455 }
456
457 sb.append(']');
458
459 return sb.toString();
460 }
461
462
463 public String toString(final int level) {
464 final StringBuffer sb = new StringBuffer(32);
465 final String nl = System.getProperty("line.separator");
466
467 sb.append(this.formatLength());
468 sb.append(':');
469 for (int i = 0; i <= level; i++) {
470 sb.append(' ');
471 }
472
473 if (this.invalid) {
474 sb.append("INVALID ");
475 }
476
477 if (!this.invalid) {
478 sb.append('<');
479 sb.append(this.getNodeName());
480 sb.append("> tag: ");
481 if (this.childOffset < 100) {
482 sb.append('0');
483 }
484 if (this.childOffset < 10) {
485 sb.append('0');
486 }
487 sb.append(this.childOffset);
488 }
489
490 sb.append(nl);
491
492 if (this.child != null) {
493 sb.append(this.child.toString(level + 1));
494 if (this.getSibling() != null) {
495 sb.append(nl);
496 }
497
498 }
499
500 if (this.getSibling() != null) {
501 sb.append(this.getSibling().toString(level));
502 }
503
504 return sb.toString();
505 }
506 }