001: /* Copyright 2002-2004 Elliotte Rusty Harold
002:
003: This library is free software; you can redistribute it and/or modify
004: it under the terms of version 2.1 of the GNU Lesser General Public
005: License as published by the Free Software Foundation.
006:
007: This library is distributed in the hope that it will be useful,
008: but WITHOUT ANY WARRANTY; without even the implied warranty of
009: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: GNU Lesser General Public License for more details.
011:
012: You should have received a copy of the GNU Lesser General Public
013: License along with this library; if not, write to the
014: Free Software Foundation, Inc., 59 Temple Place, Suite 330,
015: Boston, MA 02111-1307 USA
016:
017: You can contact Elliotte Rusty Harold by sending e-mail to
018: elharo@metalab.unc.edu. Please include the word "XOM" in the
019: subject line. The XOM home page is located at http://www.xom.nu/
020: */
021:
022: package nu.xom.tests;
023:
024: import nu.xom.Comment;
025: import nu.xom.CycleException;
026: import nu.xom.DocType;
027: import nu.xom.Document;
028: import nu.xom.Element;
029: import nu.xom.IllegalAddException;
030: import nu.xom.MultipleParentException;
031: import nu.xom.NoSuchChildException;
032: import nu.xom.Node;
033: import nu.xom.ProcessingInstruction;
034: import nu.xom.Text;
035:
036: /**
037: * <p>
038: * Tests adding, removing, and counting children from parent nodes.
039: * </p>
040: *
041: * @author Elliotte Rusty Harold
042: * @version 1.0
043: *
044: */
045: public class ParentNodeTest extends XOMTestCase {
046:
047: public ParentNodeTest(String name) {
048: super (name);
049: }
050:
051: private Element empty;
052: private Element notEmpty;
053: private Text child;
054:
055: protected void setUp() {
056: empty = new Element("Empty");
057: notEmpty = new Element("NotEmpty");
058: child = new Text("Hello");
059: notEmpty.appendChild(child);
060: }
061:
062: public void testDetach() {
063:
064: Text text = new Text("This will be attached then detached");
065: empty.appendChild(text);
066: assertEquals(empty, text.getParent());
067: text.detach();
068: assertNull(text.getParent());
069:
070: }
071:
072: public void testAppendChild() {
073:
074: Element child = new Element("test");
075: empty.appendChild(child);
076: assertEquals(1, empty.getChildCount());
077: assertEquals(empty.getChild(0), child);
078: child.detach();
079:
080: notEmpty.appendChild(child);
081: assertTrue(!notEmpty.getChild(0).equals(child));
082: assertTrue(notEmpty.getChild(1).equals(child));
083:
084: }
085:
086: public void testAppendChildToItself() {
087:
088: Element child = new Element("test");
089: try {
090: child.appendChild(child);
091: fail("Appended node to itself");
092: } catch (CycleException success) {
093: assertNotNull(success.getMessage());
094: }
095:
096: }
097:
098: public void testCycle() {
099:
100: Element a = new Element("test");
101: Element b = new Element("test");
102: try {
103: a.appendChild(b);
104: b.appendChild(a);
105: fail("Allowed cycle");
106: } catch (CycleException success) {
107: assertNotNull(success.getMessage());
108: }
109:
110: }
111:
112: public void testInsertChild() {
113:
114: Element parent = new Element("parent");
115:
116: // Test insert into empty element
117: Element child1 = new Element("child");
118: parent.insertChild(child1, 0);
119: assertTrue(parent.getChildCount() > 0);
120: assertEquals(0, parent.indexOf(child1));
121:
122: // Test insert at beginning
123: Element child2 = new Element("child2");
124: parent.insertChild(child2, 0);
125: assertEquals(0, parent.indexOf(child2));
126: assertEquals(1, parent.indexOf(child1));
127:
128: // Test insert in middle
129: Element child3 = new Element("child3");
130: parent.insertChild(child3, 1);
131: assertEquals(0, parent.indexOf(child2));
132: assertEquals(1, parent.indexOf(child3));
133: assertEquals(2, parent.indexOf(child1));
134:
135: // Test insert at beginning with children
136: Element child4 = new Element("child4");
137: parent.insertChild(child4, 0);
138: assertEquals(0, parent.indexOf(child4));
139: assertEquals(1, parent.indexOf(child2));
140: assertEquals(2, parent.indexOf(child3));
141: assertEquals(3, parent.indexOf(child1));
142:
143: // Test insert at end with children
144: Element child5 = new Element("child5");
145: parent.insertChild(child5, 4);
146: assertEquals(0, parent.indexOf(child4));
147: assertEquals(1, parent.indexOf(child2));
148: assertEquals(2, parent.indexOf(child3));
149: assertEquals(3, parent.indexOf(child1));
150: assertEquals(4, parent.indexOf(child5));
151:
152: try {
153: parent.insertChild((Element) null, 0);
154: fail("Inserted null");
155: } catch (NullPointerException success) {
156: assertNotNull(success.getMessage());
157: }
158:
159: try {
160: parent.insertChild((Text) null, 0);
161: fail("Inserted null");
162: } catch (NullPointerException success) {
163: assertNotNull(success.getMessage());
164: }
165:
166: try {
167: parent.insertChild((Comment) null, 0);
168: fail("Inserted null");
169: } catch (NullPointerException success) {
170: assertNotNull(success.getMessage());
171: }
172:
173: try {
174: parent.insertChild((ProcessingInstruction) null, 0);
175: fail("Inserted null");
176: } catch (NullPointerException success) {
177: assertNotNull(success.getMessage());
178: }
179:
180: }
181:
182: public void testAppendChild2() {
183:
184: try {
185: empty.appendChild(new Document(notEmpty));
186: fail("appended a document to an element");
187: } catch (IllegalAddException success) {
188: assertNotNull(success.getMessage());
189: }
190:
191: try {
192: empty.appendChild(child);
193: fail("appended a child twice");
194: } catch (MultipleParentException success) {
195: assertNotNull(success.getMessage());
196: }
197:
198: }
199:
200: public void testReplaceChild() {
201:
202: Element old1 = new Element("old1");
203: Element old2 = new Element("old2");
204: Element old3 = new Element("old3");
205: Element new1 = new Element("new1");
206: Element new2 = new Element("new2");
207: Element new3 = new Element("new3");
208:
209: empty.appendChild(old1);
210: empty.appendChild(old2);
211: empty.appendChild(old3);
212:
213: empty.replaceChild(old1, new1);
214: empty.replaceChild(old3, new3);
215: empty.replaceChild(old2, new2);
216:
217: Node current1 = empty.getChild(0);
218: Node current2 = empty.getChild(1);
219: Node current3 = empty.getChild(2);
220:
221: assertEquals(new1, current1);
222: assertEquals(new2, current2);
223: assertEquals(new3, current3);
224:
225: try {
226: empty.replaceChild(new1, null);
227: } catch (NullPointerException success) {
228: assertNotNull(success.getMessage());
229: }
230:
231: try {
232: empty.replaceChild(null, old1);
233: } catch (NullPointerException success) {
234: assertNotNull(success.getMessage());
235: }
236:
237: Element new4 = new Element("new4");
238:
239: try {
240: empty.replaceChild(new4, new Element("test"));
241: fail("Replaced Nonexistent element");
242: } catch (NoSuchChildException success) {
243: assertNotNull(success.getMessage());
244: }
245:
246: // Test replacing node with itself
247: empty.replaceChild(new1, new1);
248: assertEquals(new1, empty.getChild(0));
249: assertEquals(empty, new1.getParent());
250:
251: // Test replacing node with a sibling
252: try {
253: empty.replaceChild(new1, new2);
254: fail("replaced a node with its sibling");
255: } catch (MultipleParentException success) {
256: assertNotNull(success.getMessage());
257: }
258:
259: }
260:
261: public void testIndexOf() {
262:
263: Element child1 = new Element("old1");
264: Text child2 = new Text("old2");
265: Comment child3 = new Comment("old3");
266:
267: assertEquals(-1, empty.indexOf(child1));
268:
269: empty.appendChild(child1);
270: empty.appendChild(child2);
271: empty.appendChild(child3);
272:
273: assertEquals(0, empty.indexOf(child1));
274: assertEquals(1, empty.indexOf(child2));
275: assertEquals(2, empty.indexOf(child3));
276: assertEquals(-1, empty.indexOf(empty));
277: assertEquals(-1, empty.indexOf(new Text("test")));
278:
279: }
280:
281: public void testGetChild() {
282:
283: Element old1 = new Element("old1");
284: Element old2 = new Element("old2");
285: Element old3 = new Element("old3");
286:
287: try {
288: empty.getChild(0);
289: fail("No index exception");
290: } catch (IndexOutOfBoundsException success) {
291: // success
292: assertNotNull(success.getMessage());
293: }
294:
295: empty.appendChild(old1);
296: empty.appendChild(old2);
297: empty.appendChild(old3);
298:
299: assertEquals(old1, empty.getChild(0));
300: assertEquals(old3, empty.getChild(2));
301: assertEquals(old2, empty.getChild(1));
302:
303: try {
304: empty.getChild(5);
305: fail("No index exception");
306: } catch (IndexOutOfBoundsException success) {
307: // success
308: assertNotNull(success.getMessage());
309: }
310:
311: }
312:
313: public void testRemoveChild() {
314:
315: try {
316: empty.removeChild(0);
317: fail("Removed child from empty element");
318: } catch (IndexOutOfBoundsException success) {
319: assertNotNull(success.getMessage());
320: }
321:
322: Element old1 = new Element("old1");
323: Element old2 = new Element("old2");
324: Element old3 = new Element("old3");
325:
326: try {
327: empty.removeChild(old1);
328: fail("Removed non-existent child from empty element");
329: } catch (NoSuchChildException success) {
330: assertNotNull(success.getMessage());
331: }
332:
333: empty.appendChild(old1);
334: empty.appendChild(old2);
335: empty.appendChild(old3);
336:
337: empty.removeChild(1);
338: assertEquals(old1, empty.getChild(0));
339: assertEquals(old3, empty.getChild(1));
340:
341: try {
342: empty.removeChild(5);
343: fail("No IndexOutOfBoundsException");
344: } catch (IndexOutOfBoundsException success) {
345: assertNotNull(success.getMessage());
346: }
347:
348: empty.removeChild(1);
349: empty.removeChild(0);
350: assertNull(old2.getParent());
351:
352: assertEquals(0, empty.getChildCount());
353:
354: empty.appendChild(old1);
355: empty.appendChild(old2);
356: empty.appendChild(old3);
357:
358: assertEquals(3, empty.getChildCount());
359:
360: empty.removeChild(old3);
361: empty.removeChild(old1);
362: empty.removeChild(old2);
363:
364: assertEquals(0, empty.getChildCount());
365: assertNull(old1.getParent());
366:
367: }
368:
369: public void testReplaceChildFailures() {
370:
371: Element old1 = new Element("old1");
372: Element old2 = new Element("old2");
373: Element old3 = new Element("old3");
374: Element new1 = new Element("new1");
375: Element new3 = new Element("new3");
376:
377: empty.appendChild(old1);
378: empty.appendChild(old2);
379:
380: try {
381: empty.replaceChild(old3, new3);
382: fail("Replaced non-existent child");
383: } catch (NoSuchChildException success) {
384: assertNotNull(success.getMessage());
385: }
386:
387: try {
388: empty.replaceChild(old1, null);
389: fail("Replaced child with null");
390: } catch (NullPointerException success) {
391: assertNotNull(success.getMessage());
392: }
393:
394: try {
395: empty.replaceChild(null, new1);
396: fail("Replaced null");
397: } catch (NullPointerException success) {
398: assertNotNull(success.getMessage());
399: }
400:
401: }
402:
403: public void testReplaceChildInEmptyParent() {
404:
405: Element test1 = new Element("test");
406: Element test2 = new Element("test");
407: try {
408: empty.replaceChild(test1, test2);
409: fail("Replaced element in empty parent");
410: } catch (NoSuchChildException success) {
411: assertNotNull(success.getMessage());
412: }
413:
414: }
415:
416: // Document that this behavior is intentional
417: // An element cannot be replaced by its sibling unless
418: // the sibling is first detached.
419: public void testReplaceSibling() {
420:
421: Element parent = new Element("parent");
422: Element test1 = new Element("test");
423: Element test2 = new Element("test");
424: parent.appendChild(test1);
425: parent.appendChild(test2);
426: try {
427: parent.replaceChild(test1, test2);
428: fail("Replaced element without detaching first");
429: } catch (IllegalAddException success) {
430: assertNotNull(success.getMessage());
431: }
432:
433: assertEquals(2, parent.getChildCount());
434: assertEquals(parent, test1.getParent());
435: assertEquals(parent, test2.getParent());
436:
437: }
438:
439: // Similarly, this test documents the conscious decision
440: // that you cannot insert an existing child into its own parent,
441: // even at the same position
442: public void testCantInsertExisitngChild() {
443:
444: Element parent = new Element("parent");
445: Element test1 = new Element("test");
446: Element test2 = new Element("test");
447: parent.appendChild(test1);
448: parent.appendChild(test2);
449: try {
450: parent.insertChild(test2, 0);
451: fail("Inserted element without detaching first");
452: } catch (MultipleParentException success) {
453: assertNotNull(success.getMessage());
454: }
455:
456: try {
457: parent.insertChild(test2, 1);
458: fail("Inserted element without detaching first");
459: } catch (MultipleParentException success) {
460: assertNotNull(success.getMessage());
461: }
462:
463: assertEquals(2, parent.getChildCount());
464: assertEquals(parent, test1.getParent());
465: assertEquals(parent, test2.getParent());
466:
467: }
468:
469: // can't remove when insertion is legal;
470: // succeeed or fail as unit
471: public void testReplaceChildAtomicity() {
472:
473: Element parent = new Element("parent");
474: Text child = new Text("child");
475: parent.appendChild(child);
476:
477: try {
478: parent.replaceChild(child, new DocType("root"));
479: fail("allowed doctype child of element");
480: } catch (IllegalAddException success) {
481: assertEquals(parent, child.getParent());
482: assertEquals(1, parent.getChildCount());
483: }
484:
485: Element e = new Element("e");
486: Text child2 = new Text("child2");
487: e.appendChild(child2);
488: try {
489: parent.replaceChild(child, child2);
490: fail("allowed replacement with existing parent");
491: } catch (MultipleParentException success) {
492: assertEquals(e, child2.getParent());
493: assertEquals(parent, child.getParent());
494: assertEquals(1, parent.getChildCount());
495: assertEquals(1, e.getChildCount());
496: }
497:
498: }
499:
500: }
|