1:
37:
38: package ;
39:
40: import ;
41:
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63:
64:
96: public abstract class DomNode
97: implements Node, NodeList, EventTarget, DocumentEvent, Cloneable, Comparable
98: {
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109: private static final int NKIDS_DELTA = 8;
110: private static final int ANCESTORS_INIT = 20;
111: private static final int NOTIFICATIONS_INIT = 10;
112:
113:
114:
115:
116:
117: static final boolean reportMutations = true;
118:
119:
120: private static final Object lockNode = new Object();
121:
122:
123:
124:
125: private static boolean dispatchDataLock;
126: private static DomNode[] ancestors = new DomNode[ANCESTORS_INIT];
127: private static ListenerRecord[] notificationSet
128: = new ListenerRecord[NOTIFICATIONS_INIT];
129:
130:
131: private static boolean eventDataLock;
132: private static DomEvent.DomMutationEvent mutationEvent
133: = new DomEvent.DomMutationEvent(null);
134:
135:
136:
137:
138:
139: DomDocument owner;
140: DomNode parent;
141: DomNode previous;
142: DomNode next;
143: DomNode first;
144: DomNode last;
145: int index;
146: int depth;
147: int length;
148: final short nodeType;
149:
150:
151:
152: boolean readonly;
153:
154:
155: private HashSet listeners;
156: private int nListeners;
157:
158:
159: private HashMap userData;
160: private HashMap userDataHandlers;
161:
162:
163:
164:
165:
166:
167:
168:
171: public void compact()
172: {
173: }
174:
175:
181: protected DomNode(short nodeType, DomDocument owner)
182: {
183: this.nodeType = nodeType;
184:
185: if (owner == null)
186: {
187:
188: if (nodeType != DOCUMENT_NODE && nodeType != DOCUMENT_TYPE_NODE)
189: {
190: throw new IllegalArgumentException ("no owner!");
191: }
192: }
193: this.owner = owner;
194: this.listeners = new HashSet();
195: }
196:
197:
198:
202: public NamedNodeMap getAttributes()
203: {
204: return null;
205: }
206:
207:
211: public boolean hasAttributes()
212: {
213: return false;
214: }
215:
216:
224: public NodeList getChildNodes()
225: {
226: return this;
227: }
228:
229:
233: public Node getFirstChild()
234: {
235: return first;
236: }
237:
238:
242: public Node getLastChild()
243: {
244: return last;
245: }
246:
247:
251: public boolean hasChildNodes()
252: {
253: return length != 0;
254: }
255:
256:
257:
262: public final boolean isReadonly()
263: {
264: return readonly;
265: }
266:
267:
273: public void makeReadonly()
274: {
275: readonly = true;
276: for (DomNode child = first; child != null; child = child.next)
277: {
278: child.makeReadonly();
279: }
280: }
281:
282:
285: void setOwner(DomDocument doc)
286: {
287: this.owner = doc;
288: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
289: {
290: ctx.setOwner(doc);
291: }
292: }
293:
294:
295:
296: private void checkMisc(DomNode child)
297: {
298: if (readonly && !owner.building)
299: {
300: throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
301: null, this, 0);
302: }
303: for (DomNode ctx = this; ctx != null; ctx = ctx.parent)
304: {
305: if (child == ctx)
306: {
307: throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
308: "can't make ancestor into a child",
309: this, 0);
310: }
311: }
312:
313: DomDocument owner = (nodeType == DOCUMENT_NODE) ? (DomDocument) this :
314: this.owner;
315: DomDocument childOwner = child.owner;
316: short childNodeType = child.nodeType;
317:
318: if (childOwner != owner)
319: {
320:
321: if (!(childNodeType == DOCUMENT_TYPE_NODE && childOwner == null))
322: {
323: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
324: null, child, 0);
325: }
326: }
327:
328:
329: switch (nodeType)
330: {
331: case DOCUMENT_NODE:
332: switch (childNodeType)
333: {
334: case ELEMENT_NODE:
335: case PROCESSING_INSTRUCTION_NODE:
336: case COMMENT_NODE:
337: case DOCUMENT_TYPE_NODE:
338: return;
339: }
340: break;
341:
342: case ATTRIBUTE_NODE:
343: switch (childNodeType)
344: {
345: case TEXT_NODE:
346: case ENTITY_REFERENCE_NODE:
347: return;
348: }
349: break;
350:
351: case DOCUMENT_FRAGMENT_NODE:
352: case ENTITY_REFERENCE_NODE:
353: case ELEMENT_NODE:
354: case ENTITY_NODE:
355: switch (childNodeType)
356: {
357: case ELEMENT_NODE:
358: case TEXT_NODE:
359: case COMMENT_NODE:
360: case PROCESSING_INSTRUCTION_NODE:
361: case CDATA_SECTION_NODE:
362: case ENTITY_REFERENCE_NODE:
363: return;
364: }
365: break;
366: case DOCUMENT_TYPE_NODE:
367: if (!owner.building)
368: break;
369: switch (childNodeType)
370: {
371: case COMMENT_NODE:
372: case PROCESSING_INSTRUCTION_NODE:
373: return;
374: }
375: break;
376: }
377: if (owner.checkingWellformedness)
378: {
379: throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
380: "can't append " +
381: nodeTypeToString(childNodeType) +
382: " to node of type " +
383: nodeTypeToString(nodeType),
384: this, 0);
385: }
386: }
387:
388:
389:
390:
391:
392:
393:
394: private void insertionEvent(DomEvent.DomMutationEvent event,
395: DomNode target)
396: {
397: if (owner == null || owner.building)
398: {
399: return;
400: }
401: boolean doFree = false;
402:
403: if (event == null)
404: {
405: event = getMutationEvent();
406: }
407: if (event != null)
408: {
409: doFree = true;
410: }
411: else
412: {
413: event = new DomEvent.DomMutationEvent(null);
414: }
415: event.initMutationEvent("DOMNodeInserted",
416: true , false ,
417: this , null, null, null, (short) 0);
418: target.dispatchEvent(event);
419:
420:
421:
422:
423:
424: if (doFree)
425: {
426: event.target = null;
427: event.relatedNode = null;
428: event.currentNode = null;
429: eventDataLock = false;
430: }
431: }
432:
433: private void removalEvent(DomEvent.DomMutationEvent event,
434: DomNode target)
435: {
436: if (owner == null || owner.building)
437: {
438: return;
439: }
440: boolean doFree = false;
441:
442: if (event == null)
443: {
444: event = getMutationEvent();
445: }
446: if (event != null)
447: {
448: doFree = true;
449: }
450: else
451: {
452: event = new DomEvent.DomMutationEvent(null);
453: }
454: event.initMutationEvent("DOMNodeRemoved",
455: true , false ,
456: this , null, null, null, (short) 0);
457: target.dispatchEvent(event);
458:
459:
460:
461:
462:
463: event.target = null;
464: event.relatedNode = null;
465: event.currentNode = null;
466: if (doFree)
467: {
468: eventDataLock = false;
469: }
470:
471: }
472:
473:
474:
475:
476:
477:
478:
479:
480:
481:
482:
483: static private DomEvent.DomMutationEvent getMutationEvent()
484: {
485: synchronized (lockNode)
486: {
487: if (eventDataLock)
488: {
489: return null;
490: }
491: eventDataLock = true;
492: return mutationEvent;
493: }
494: }
495:
496:
497:
498: static private void freeMutationEvent()
499: {
500:
501: mutationEvent.clear();
502: eventDataLock = false;
503: }
504:
505: void setDepth(int depth)
506: {
507: this.depth = depth;
508: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
509: {
510: ctx.setDepth(depth + 1);
511: }
512: }
513:
514:
530: public Node appendChild(Node newChild)
531: {
532: try
533: {
534: DomNode child = (DomNode) newChild;
535:
536: if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
537: {
538:
539: for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
540: {
541: checkMisc(ctx);
542: }
543: for (DomNode ctx = child.first; ctx != null; )
544: {
545: DomNode ctxNext = ctx.next;
546: appendChild(ctx);
547: ctx = ctxNext;
548: }
549: }
550: else
551: {
552: checkMisc(child);
553: if (child.parent != null)
554: {
555: child.parent.removeChild(child);
556: }
557: child.parent = this;
558: child.index = length++;
559: child.setDepth(depth + 1);
560: child.next = null;
561: if (last == null)
562: {
563: first = child;
564: child.previous = null;
565: }
566: else
567: {
568: last.next = child;
569: child.previous = last;
570: }
571: last = child;
572:
573: if (reportMutations)
574: {
575: insertionEvent(null, child);
576: }
577: }
578:
579: return child;
580: }
581: catch (ClassCastException e)
582: {
583: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
584: null, newChild, 0);
585: }
586: }
587:
588:
604: public Node insertBefore(Node newChild, Node refChild)
605: {
606: if (refChild == null)
607: {
608: return appendChild(newChild);
609: }
610:
611: try
612: {
613: DomNode child = (DomNode) newChild;
614: DomNode ref = (DomNode) refChild;
615:
616: if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
617: {
618:
619: for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
620: {
621: checkMisc(ctx);
622: }
623: for (DomNode ctx = child.first; ctx != null; )
624: {
625: DomNode ctxNext = ctx.next;
626: insertBefore(ctx, ref);
627: ctx = ctxNext;
628: }
629: }
630: else
631: {
632: checkMisc(child);
633: if (ref == null || ref.parent != this)
634: {
635: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
636: null, ref, 0);
637: }
638: if (ref == child)
639: {
640: throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
641: "can't insert node before itself",
642: ref, 0);
643: }
644:
645: if (child.parent != null)
646: {
647: child.parent.removeChild(child);
648: }
649: child.parent = this;
650: int i = ref.index;
651: child.setDepth(depth + 1);
652: child.next = ref;
653: if (ref.previous != null)
654: {
655: ref.previous.next = child;
656: }
657: child.previous = ref.previous;
658: ref.previous = child;
659: if (first == ref)
660: {
661: first = child;
662: }
663:
664: for (DomNode ctx = child; ctx != null; ctx = ctx.next)
665: {
666: ctx.index = i++;
667: }
668:
669: if (reportMutations)
670: {
671: insertionEvent(null, child);
672: }
673: length++;
674: }
675:
676: return child;
677: }
678: catch (ClassCastException e)
679: {
680: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
681: null, newChild, 0);
682: }
683: }
684:
685:
710: public Node replaceChild(Node newChild, Node refChild)
711: {
712: try
713: {
714: DomNode child = (DomNode) newChild;
715: DomNode ref = (DomNode) refChild;
716:
717: DomEvent.DomMutationEvent event = getMutationEvent();
718: boolean doFree = (event != null);
719:
720: if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
721: {
722:
723: for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
724: {
725: checkMisc(ctx);
726: }
727: if (ref == null || ref.parent != this)
728: {
729: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
730: null, ref, 0);
731: }
732:
733: if (reportMutations)
734: {
735: removalEvent(event, ref);
736: }
737: length--;
738: length += child.length;
739:
740: if (child.length == 0)
741: {
742:
743: if (ref.previous != null)
744: {
745: ref.previous.next = ref.next;
746: }
747: if (ref.next != null)
748: {
749: ref.next.previous = ref.previous;
750: }
751: if (first == ref)
752: {
753: first = ref.next;
754: }
755: if (last == ref)
756: {
757: last = ref.previous;
758: }
759: }
760: else
761: {
762: int i = ref.index;
763: for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
764: {
765:
766: ctx.parent = this;
767: ctx.index = i++;
768: ctx.setDepth(ref.depth);
769: if (ctx == child.first)
770: {
771: ctx.previous = ref.previous;
772: }
773: if (ctx == child.last)
774: {
775: ctx.next = ref.next;
776: }
777: }
778: if (first == ref)
779: {
780: first = child.first;
781: }
782: if (last == ref)
783: {
784: last = child.last;
785: }
786: }
787: }
788: else
789: {
790: checkMisc(child);
791: if (ref == null || ref.parent != this)
792: {
793: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
794: null, ref, 0);
795: }
796:
797: if (reportMutations)
798: {
799: removalEvent(event, ref);
800: }
801:
802: if (child.parent != null)
803: {
804: child.parent.removeChild(child);
805: }
806: child.parent = this;
807: child.index = ref.index;
808: child.setDepth(ref.depth);
809: if (ref.previous != null)
810: {
811: ref.previous.next = child;
812: }
813: child.previous = ref.previous;
814: if (ref.next != null)
815: {
816: ref.next.previous = child;
817: }
818: child.next = ref.next;
819: if (first == ref)
820: {
821: first = child;
822: }
823: if (last == ref)
824: {
825: last = child;
826: }
827:
828: if (reportMutations)
829: {
830: insertionEvent(event, child);
831: }
832: if (doFree)
833: {
834: freeMutationEvent();
835: }
836: }
837: ref.parent = null;
838: ref.index = 0;
839: ref.setDepth(0);
840: ref.previous = null;
841: ref.next = null;
842:
843: return ref;
844: }
845: catch (ClassCastException e)
846: {
847: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
848: null, newChild, 0);
849: }
850: }
851:
852:
862: public Node removeChild(Node refChild)
863: {
864: try
865: {
866: DomNode ref = (DomNode) refChild;
867:
868: if (ref == null || ref.parent != this)
869: {
870: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
871: null, ref, 0);
872: }
873: if (readonly && !owner.building)
874: {
875: throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
876: null, this, 0);
877: }
878:
879: for (DomNode child = first; child != null; child = child.next)
880: {
881: if (child == ref)
882: {
883: if (reportMutations)
884: {
885: removalEvent(null, child);
886: }
887:
888: length--;
889: if (ref.previous != null)
890: {
891: ref.previous.next = ref.next;
892: }
893: if (ref.next != null)
894: {
895: ref.next.previous = ref.previous;
896: }
897: if (first == ref)
898: {
899: first = ref.next;
900: }
901: if (last == ref)
902: {
903: last = ref.previous;
904: }
905:
906: int i = 0;
907: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
908: {
909: ctx.index = i++;
910: }
911: ref.parent = null;
912: ref.setDepth(0);
913: ref.index = 0;
914: ref.previous = null;
915: ref.next = null;
916:
917: return ref;
918: }
919: }
920: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
921: "that's no child of mine", refChild, 0);
922: }
923: catch (ClassCastException e)
924: {
925: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
926: null, refChild, 0);
927: }
928: }
929:
930:
935: public Node item(int index)
936: {
937: DomNode child = first;
938: int count = 0;
939: while (child != null && count < index)
940: {
941: child = child.next;
942: count++;
943: }
944: return child;
945: }
946:
947:
954: public int getLength()
955: {
956: return length;
957: }
958:
959:
963: public void trimToSize()
964: {
965: }
966:
967:
971: public Node getNextSibling()
972: {
973: return next;
974: }
975:
976:
980: public Node getPreviousSibling()
981: {
982: return previous;
983: }
984:
985:
989: public Node getParentNode()
990: {
991: return parent;
992: }
993:
994:
1003: public boolean isSupported(String feature, String version)
1004: {
1005: Document doc = owner;
1006: DOMImplementation impl = null;
1007:
1008: if (doc == null && nodeType == DOCUMENT_NODE)
1009: {
1010: doc = (Document) this;
1011: }
1012:
1013: if (doc == null)
1014: {
1015:
1016: throw new IllegalStateException ("unbound ownerDocument");
1017: }
1018:
1019: impl = doc.getImplementation();
1020: return impl.hasFeature(feature, version);
1021: }
1022:
1023:
1029: final public Document getOwnerDocument()
1030: {
1031: return owner;
1032: }
1033:
1034:
1039: public void setNodeValue(String value)
1040: {
1041: }
1042:
1043:
1048: public String getNodeValue()
1049: {
1050: return null;
1051: }
1052:
1053:
1056: public final short getNodeType()
1057: {
1058: return nodeType;
1059: }
1060:
1061:
1064: public abstract String getNodeName();
1065:
1066:
1071: public void setPrefix(String prefix)
1072: {
1073: }
1074:
1075:
1080: public String getPrefix()
1081: {
1082: return null;
1083: }
1084:
1085:
1090: public String getNamespaceURI()
1091: {
1092: return null;
1093: }
1094:
1095:
1100: public String getLocalName()
1101: {
1102: return null;
1103: }
1104:
1105:
1111: public Node cloneNode(boolean deep)
1112: {
1113: if (deep)
1114: {
1115: return cloneNodeDeepInternal(true, null);
1116: }
1117:
1118: DomNode node = (DomNode) clone();
1119: if (nodeType == ENTITY_REFERENCE_NODE)
1120: {
1121: node.makeReadonly();
1122: }
1123: notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, node);
1124: return node;
1125: }
1126:
1127:
1130: private DomNode cloneNodeDeepInternal(boolean root, DomDocument doc)
1131: {
1132: DomNode node = (DomNode) clone();
1133: boolean building = false;
1134: if (root)
1135: {
1136: doc = (nodeType == DOCUMENT_NODE) ? (DomDocument) node : node.owner;
1137: building = doc.building;
1138: doc.building = true;
1139: }
1140: node.owner = doc;
1141: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1142: {
1143: DomNode newChild = ctx.cloneNodeDeepInternal(false, doc);
1144: node.appendChild(newChild);
1145: }
1146: if (nodeType == ENTITY_REFERENCE_NODE)
1147: {
1148: node.makeReadonly();
1149: }
1150: if (root)
1151: {
1152: doc.building = building;
1153: }
1154: notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, node);
1155: return node;
1156: }
1157:
1158: void notifyUserDataHandlers(short op, Node src, Node dst)
1159: {
1160: if (userDataHandlers != null)
1161: {
1162: for (Iterator i = userDataHandlers.entrySet().iterator(); i.hasNext(); )
1163: {
1164: Map.Entry entry = (Map.Entry) i.next();
1165: String key = (String) entry.getKey();
1166: UserDataHandler handler = (UserDataHandler) entry.getValue();
1167: Object data = userData.get(key);
1168: handler.handle(op, key, data, src, dst);
1169: }
1170: }
1171: }
1172:
1173:
1181: public Object clone()
1182: {
1183: try
1184: {
1185: DomNode node = (DomNode) super.clone();
1186:
1187: node.parent = null;
1188: node.depth = 0;
1189: node.index = 0;
1190: node.length = 0;
1191: node.first = null;
1192: node.last = null;
1193: node.previous = null;
1194: node.next = null;
1195:
1196: node.readonly = false;
1197: node.listeners = new HashSet();
1198: node.nListeners = 0;
1199: return node;
1200:
1201: }
1202: catch (CloneNotSupportedException x)
1203: {
1204: throw new Error("clone didn't work");
1205: }
1206: }
1207:
1208:
1209:
1210:
1211:
1212:
1218: public NodeList getElementsByTagName(String tag)
1219: {
1220: return new ShadowList(null, tag);
1221: }
1222:
1223:
1229: public NodeList getElementsByTagNameNS(String namespace, String local)
1230: {
1231: return new ShadowList(namespace, local);
1232: }
1233:
1234:
1235:
1236:
1237:
1238:
1239:
1240: final class ShadowList
1241: implements NodeList
1242: {
1243:
1244: private LiveNodeList liveList;
1245:
1246: ShadowList(String ns, String local)
1247: {
1248: liveList = new LiveNodeList(ns, local);
1249: }
1250:
1251: public void finalize()
1252: {
1253: liveList.detach();
1254: liveList = null;
1255: }
1256:
1257: public Node item(int index)
1258: {
1259: return liveList.item(index);
1260: }
1261:
1262: public int getLength()
1263: {
1264: return liveList.getLength();
1265: }
1266: }
1267:
1268: final class LiveNodeList
1269: implements NodeList, EventListener, NodeFilter
1270: {
1271:
1272: private final boolean matchAnyURI;
1273: private final boolean matchAnyName;
1274: private final String elementURI;
1275: private final String elementName;
1276:
1277: private DomIterator current;
1278: private int lastIndex;
1279:
1280: LiveNodeList(String uri, String name)
1281: {
1282: elementURI = uri;
1283: elementName = name;
1284: matchAnyURI = "*".equals(uri);
1285: matchAnyName = "*".equals(name);
1286:
1287: DomNode.this.addEventListener("DOMNodeInserted", this, true);
1288: DomNode.this.addEventListener("DOMNodeRemoved", this, true);
1289: }
1290:
1291: void detach()
1292: {
1293: if (current != null)
1294: current.detach();
1295: current = null;
1296:
1297: DomNode.this.removeEventListener("DOMNodeInserted", this, true);
1298: DomNode.this.removeEventListener("DOMNodeRemoved", this, true);
1299: }
1300:
1301: public short acceptNode(Node element)
1302: {
1303: if (element == DomNode.this)
1304: {
1305: return FILTER_SKIP;
1306: }
1307:
1308:
1309: if (elementURI != null)
1310: {
1311: if (!(matchAnyURI
1312: || elementURI.equals(element.getNamespaceURI())))
1313: {
1314: return FILTER_SKIP;
1315: }
1316: if (!(matchAnyName
1317: || elementName.equals(element.getLocalName())))
1318: {
1319: return FILTER_SKIP;
1320: }
1321:
1322:
1323: }
1324: else
1325: {
1326: if (!(matchAnyName
1327: || elementName.equals(element.getNodeName())))
1328: {
1329: return FILTER_SKIP;
1330: }
1331: }
1332: return FILTER_ACCEPT;
1333: }
1334:
1335: private DomIterator createIterator()
1336: {
1337: return new DomIterator(DomNode.this,
1338: NodeFilter.SHOW_ELEMENT,
1339: this,
1340: true
1341: );
1342: }
1343:
1344: public void handleEvent(Event e)
1345: {
1346: MutationEvent mutation = (MutationEvent) e;
1347: Node related = mutation.getRelatedNode();
1348:
1349:
1350:
1351:
1352: if (related.getNodeType() != Node.ELEMENT_NODE ||
1353: related.getNodeName() != elementName ||
1354: related.getNamespaceURI() != elementURI)
1355: {
1356: return;
1357: }
1358:
1359: if (current != null)
1360: current.detach();
1361: current = null;
1362: }
1363:
1364: public Node item(int index)
1365: {
1366: if (current == null)
1367: {
1368: current = createIterator();
1369: lastIndex = -1;
1370: }
1371:
1372:
1373: if (index <= lastIndex) {
1374: while (index != lastIndex) {
1375: current.previousNode ();
1376: lastIndex--;
1377: }
1378: Node ret = current.previousNode ();
1379: current.detach();
1380: current = null;
1381: return ret;
1382: }
1383:
1384:
1385: while (++lastIndex != index)
1386: current.nextNode ();
1387:
1388: Node ret = current.nextNode ();
1389: current.detach();
1390: current = null;
1391: return ret;
1392: }
1393:
1394: public int getLength()
1395: {
1396: int retval = 0;
1397: NodeIterator iter = createIterator();
1398:
1399: while (iter.nextNode() != null)
1400: {
1401: retval++;
1402: }
1403: iter.detach();
1404: return retval;
1405: }
1406:
1407: }
1408:
1409:
1410:
1411:
1412: static final class ListenerRecord
1413: {
1414:
1415: String type;
1416: EventListener listener;
1417: boolean useCapture;
1418:
1419:
1420:
1421:
1422:
1423:
1424: ListenerRecord(String type, EventListener listener, boolean useCapture)
1425: {
1426: this.type = type.intern();
1427: this.listener = listener;
1428: this.useCapture = useCapture;
1429: }
1430:
1431: public boolean equals(Object o)
1432: {
1433: ListenerRecord rec = (ListenerRecord)o;
1434: return listener == rec.listener
1435: && useCapture == rec.useCapture
1436: && type == rec.type;
1437: }
1438:
1439: public int hashCode()
1440: {
1441: return listener.hashCode() ^ type.hashCode();
1442: }
1443: }
1444:
1445:
1462: public Event createEvent(String eventType)
1463: {
1464: eventType = eventType.toLowerCase();
1465:
1466: if ("mutationevents".equals(eventType))
1467: {
1468: return new DomEvent.DomMutationEvent(null);
1469: }
1470:
1471: if ("htmlevents".equals(eventType)
1472: || "events".equals(eventType)
1473: || "user-events".equals(eventType))
1474: {
1475: return new DomEvent(null);
1476: }
1477:
1478: if ("uievents".equals(eventType))
1479: {
1480: return new DomEvent.DomUIEvent(null);
1481: }
1482:
1483:
1484:
1485: throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR,
1486: eventType, null, 0);
1487: }
1488:
1489:
1493: public final void addEventListener(String type,
1494: EventListener listener,
1495: boolean useCapture)
1496: {
1497:
1498: ListenerRecord record;
1499:
1500: record = new ListenerRecord(type, listener, useCapture);
1501: listeners.add(record);
1502: nListeners = listeners.size();
1503: }
1504:
1505:
1506:
1507:
1508: static final class DomEventException
1509: extends EventException
1510: {
1511:
1512: DomEventException()
1513: {
1514: super(UNSPECIFIED_EVENT_TYPE_ERR, "unspecified event type");
1515: }
1516:
1517: }
1518:
1519:
1533: public final boolean dispatchEvent(Event event)
1534: throws EventException
1535: {
1536: DomEvent e = (DomEvent) event;
1537: DomNode[] ancestors = null;
1538: int ancestorMax = 0;
1539: boolean haveDispatchDataLock = false;
1540:
1541: if (e.type == null)
1542: {
1543: throw new DomEventException();
1544: }
1545:
1546: e.doDefault = true;
1547: e.target = this;
1548:
1549:
1550:
1551:
1552:
1553:
1554:
1555:
1556:
1557:
1558:
1559:
1560:
1561: try
1562: {
1563: DomNode current;
1564: int index;
1565: boolean haveAncestorRegistrations = false;
1566: ListenerRecord[] notificationSet;
1567: int ancestorLen;
1568:
1569: synchronized (lockNode)
1570: {
1571: if (!dispatchDataLock)
1572: {
1573: haveDispatchDataLock = dispatchDataLock = true;
1574: notificationSet = DomNode.notificationSet;
1575: ancestors = DomNode.ancestors;
1576: }
1577: else
1578: {
1579: notificationSet = new ListenerRecord[NOTIFICATIONS_INIT];
1580: ancestors = new DomNode[ANCESTORS_INIT];
1581: }
1582: ancestorLen = ancestors.length;
1583: }
1584:
1585:
1586:
1587:
1588: current = (parent == null) ? this : parent;
1589: if (current.depth >= ANCESTORS_INIT)
1590: {
1591: DomNode[] newants = new DomNode[current.depth + 1];
1592: System.arraycopy(ancestors, 0, newants, 0, ancestors.length);
1593: ancestors = newants;
1594: ancestorLen = ancestors.length;
1595: }
1596: for (index = 0; index < ancestorLen; index++)
1597: {
1598: if (current == null || current.depth == 0)
1599: break;
1600:
1601: if (current.nListeners != 0)
1602: {
1603: haveAncestorRegistrations = true;
1604: }
1605: ancestors [index] = current;
1606: current = current.parent;
1607: }
1608: if (current.depth > 0)
1609: {
1610: throw new RuntimeException("dispatchEvent capture stack size");
1611: }
1612:
1613: ancestorMax = index;
1614: e.stop = false;
1615:
1616: if (haveAncestorRegistrations)
1617: {
1618: e.eventPhase = Event.CAPTURING_PHASE;
1619: while (!e.stop && index-- > 0)
1620: {
1621: current = ancestors [index];
1622: if (current.nListeners != 0)
1623: {
1624: notifyNode(e, current, true, notificationSet);
1625: }
1626: }
1627: }
1628:
1629:
1630:
1631:
1632: if (!e.stop && nListeners != 0)
1633: {
1634: e.eventPhase = Event.AT_TARGET;
1635: notifyNode (e, this, false, notificationSet);
1636: }
1637: else if (!haveAncestorRegistrations)
1638: {
1639: e.stop = true;
1640: }
1641:
1642:
1643:
1644:
1645:
1646: if (!e.stop && e.bubbles)
1647: {
1648: e.eventPhase = Event.BUBBLING_PHASE;
1649: for (index = 0;
1650: !e.stop
1651: && index < ancestorMax
1652: && (current = ancestors[index]) != null;
1653: index++)
1654: {
1655: if (current.nListeners != 0)
1656: {
1657: notifyNode(e, current, false, notificationSet);
1658: }
1659: }
1660: }
1661: e.eventPhase = 0;
1662:
1663:
1664:
1665: return e.doDefault;
1666:
1667: }
1668: finally
1669: {
1670: if (haveDispatchDataLock)
1671: {
1672:
1673: synchronized (lockNode)
1674: {
1675:
1676: for (int i = 0; i < ancestorMax; i++)
1677: {
1678: ancestors [i] = null;
1679: }
1680:
1681:
1682: dispatchDataLock = false;
1683: }
1684: }
1685: }
1686: }
1687:
1688: private void notifyNode(DomEvent e,
1689: DomNode current,
1690: boolean capture,
1691: ListenerRecord[] notificationSet)
1692: {
1693: int count = 0;
1694: Iterator iter;
1695:
1696: iter = current.listeners.iterator();
1697:
1698:
1699: while (iter.hasNext())
1700: {
1701: ListenerRecord rec = (ListenerRecord)iter.next();
1702:
1703: if (rec.useCapture != capture)
1704: {
1705: continue;
1706: }
1707: if (!e.type.equals (rec.type))
1708: {
1709: continue;
1710: }
1711: if (count >= notificationSet.length)
1712: {
1713:
1714: int len = Math.max(notificationSet.length, 1);
1715: ListenerRecord[] tmp = new ListenerRecord[len * 2];
1716: System.arraycopy(notificationSet, 0, tmp, 0,
1717: notificationSet.length);
1718: notificationSet = tmp;
1719: }
1720: notificationSet[count++] = rec;
1721: }
1722: iter = null;
1723:
1724:
1725: e.currentNode = current;
1726: for (int i = 0; i < count; i++)
1727: {
1728: try
1729: {
1730: iter = current.listeners.iterator();
1731:
1732:
1733:
1734: while (iter.hasNext())
1735: {
1736: ListenerRecord rec = (ListenerRecord)iter.next();
1737:
1738: if (rec.equals(notificationSet[i]))
1739: {
1740: notificationSet[i].listener.handleEvent(e);
1741: break;
1742: }
1743: }
1744: iter = null;
1745: }
1746: catch (Exception x)
1747: {
1748:
1749: }
1750: notificationSet[i] = null;
1751: }
1752: }
1753:
1754:
1758: public final void removeEventListener(String type,
1759: EventListener listener,
1760: boolean useCapture)
1761: {
1762: listeners.remove(new ListenerRecord(type, listener, useCapture));
1763: nListeners = listeners.size();
1764:
1765: }
1766:
1767:
1773: public final void normalize()
1774: {
1775:
1776: boolean saved = readonly;
1777: readonly = false;
1778: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1779: {
1780: boolean saved2 = ctx.readonly;
1781: ctx.readonly = false;
1782: switch (ctx.nodeType)
1783: {
1784: case TEXT_NODE:
1785: case CDATA_SECTION_NODE:
1786: while (ctx.next != null &&
1787: (ctx.next.nodeType == TEXT_NODE ||
1788: ctx.next.nodeType == CDATA_SECTION_NODE))
1789: {
1790: Text text = (Text) ctx;
1791: text.appendData(ctx.next.getNodeValue());
1792: removeChild(ctx.next);
1793: }
1794: break;
1795: case ELEMENT_NODE:
1796: NamedNodeMap attrs = ctx.getAttributes();
1797: int len = attrs.getLength();
1798: for (int i = 0; i < len; i++)
1799: {
1800: DomNode attr = (DomNode) attrs.item(i);
1801: boolean saved3 = attr.readonly;
1802: attr.readonly = false;
1803: attr.normalize();
1804: attr.readonly = saved3;
1805: }
1806:
1807: case DOCUMENT_NODE:
1808: case DOCUMENT_FRAGMENT_NODE:
1809: case ATTRIBUTE_NODE:
1810: case ENTITY_REFERENCE_NODE:
1811: ctx.normalize();
1812: break;
1813: }
1814: ctx.readonly = saved2;
1815: }
1816: readonly = saved;
1817: }
1818:
1819:
1831: public boolean nameAndTypeEquals(Node other)
1832: {
1833: if (other == this)
1834: {
1835: return true;
1836: }
1837:
1838: if (nodeType != other.getNodeType())
1839: {
1840: return false;
1841: }
1842:
1843:
1844:
1845: String ns1 = this.getNamespaceURI();
1846: String ns2 = other.getNamespaceURI();
1847:
1848: if (ns1 != null && ns2 != null)
1849: {
1850: return ns1.equals(ns2) &&
1851: equal(getLocalName(), other.getLocalName());
1852: }
1853:
1854:
1855: if (ns1 == null && ns2 == null)
1856: {
1857: if (!getNodeName().equals(other.getNodeName()))
1858: {
1859: return false;
1860: }
1861:
1862:
1863:
1864:
1865: return true;
1866: }
1867:
1868:
1869: return false;
1870: }
1871:
1872:
1873:
1874: public String getBaseURI()
1875: {
1876: return (parent != null) ? parent.getBaseURI() : null;
1877: }
1878:
1879: public short compareDocumentPosition(Node other)
1880: throws DOMException
1881: {
1882: return (short) compareTo(other);
1883: }
1884:
1885:
1888: public final int compareTo(Object other)
1889: {
1890: if (other instanceof DomNode)
1891: {
1892: DomNode n1 = this;
1893: DomNode n2 = (DomNode) other;
1894: if (n1.owner != n2.owner)
1895: {
1896: return 0;
1897: }
1898: int d1 = n1.depth, d2 = n2.depth;
1899: int delta = d1 - d2;
1900: while (d1 > d2)
1901: {
1902: n1 = n1.parent;
1903: d1--;
1904: }
1905: while (d2 > d1)
1906: {
1907: n2 = n2.parent;
1908: d2--;
1909: }
1910: int c = compareTo2(n1, n2);
1911: return (c != 0) ? c : delta;
1912: }
1913: return 0;
1914: }
1915:
1916:
1919: final int compareTo2(DomNode n1, DomNode n2)
1920: {
1921: if (n1 == n2 || n1.depth == 0 || n2.depth == 0)
1922: {
1923: return 0;
1924: }
1925: int c = compareTo2(n1.parent, n2.parent);
1926: return (c != 0) ? c : n1.index - n2.index;
1927: }
1928:
1929: public final String getTextContent()
1930: throws DOMException
1931: {
1932: return getTextContent(true);
1933: }
1934:
1935: final String getTextContent(boolean topLevel)
1936: throws DOMException
1937: {
1938: switch (nodeType)
1939: {
1940: case ELEMENT_NODE:
1941: case ENTITY_NODE:
1942: case ENTITY_REFERENCE_NODE:
1943: case DOCUMENT_FRAGMENT_NODE:
1944: CPStringBuilder buffer = new CPStringBuilder();
1945: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1946: {
1947: String textContent = ctx.getTextContent(false);
1948: if (textContent != null)
1949: {
1950: buffer.append(textContent);
1951: }
1952: }
1953: return buffer.toString();
1954: case TEXT_NODE:
1955: case CDATA_SECTION_NODE:
1956: if (((Text) this).isElementContentWhitespace())
1957: {
1958: return "";
1959: }
1960: return getNodeValue();
1961: case ATTRIBUTE_NODE:
1962: return getNodeValue();
1963: case COMMENT_NODE:
1964: case PROCESSING_INSTRUCTION_NODE:
1965: return topLevel ? getNodeValue() : "";
1966: default:
1967: return null;
1968: }
1969: }
1970:
1971: public void setTextContent(String textContent)
1972: throws DOMException
1973: {
1974: switch (nodeType)
1975: {
1976: case ELEMENT_NODE:
1977: case ATTRIBUTE_NODE:
1978: case ENTITY_NODE:
1979: case ENTITY_REFERENCE_NODE:
1980: case DOCUMENT_FRAGMENT_NODE:
1981: for (DomNode ctx = first; ctx != null; )
1982: {
1983: DomNode n = ctx.next;
1984: removeChild(ctx);
1985: ctx = n;
1986: }
1987: if (textContent != null)
1988: {
1989: Text text = owner.createTextNode(textContent);
1990: appendChild(text);
1991: }
1992: break;
1993: case TEXT_NODE:
1994: case CDATA_SECTION_NODE:
1995: case COMMENT_NODE:
1996: case PROCESSING_INSTRUCTION_NODE:
1997: setNodeValue(textContent);
1998: break;
1999: }
2000: }
2001:
2002: public boolean isSameNode(Node other)
2003: {
2004: return this == other;
2005: }
2006:
2007: public String lookupPrefix(String namespaceURI)
2008: {
2009: return (parent == null || parent == owner) ? null :
2010: parent.lookupPrefix(namespaceURI);
2011: }
2012:
2013: public boolean isDefaultNamespace(String namespaceURI)
2014: {
2015: return (parent == null || parent == owner) ? false :
2016: parent.isDefaultNamespace(namespaceURI);
2017: }
2018:
2019: public String lookupNamespaceURI(String prefix)
2020: {
2021: return (parent == null || parent == owner) ? null :
2022: parent.lookupNamespaceURI(prefix);
2023: }
2024:
2025: public boolean isEqualNode(Node arg)
2026: {
2027: if (this == arg)
2028: return true;
2029: if (arg == null)
2030: return false;
2031: if (nodeType != arg.getNodeType())
2032: return false;
2033: switch (nodeType)
2034: {
2035: case ELEMENT_NODE:
2036: case ATTRIBUTE_NODE:
2037: if (!equal(getLocalName(), arg.getLocalName()) ||
2038: !equal(getNamespaceURI(), arg.getNamespaceURI()))
2039: return false;
2040: break;
2041: case PROCESSING_INSTRUCTION_NODE:
2042: if (!equal(getNodeName(), arg.getNodeName()) ||
2043: !equal(getNodeValue(), arg.getNodeValue()))
2044: return false;
2045: break;
2046: case COMMENT_NODE:
2047: case TEXT_NODE:
2048: case CDATA_SECTION_NODE:
2049: if (!equal(getNodeValue(), arg.getNodeValue()))
2050: return false;
2051: break;
2052: }
2053:
2054: Node argCtx = arg.getFirstChild();
2055: getFirstChild();
2056: DomNode ctx = first;
2057: for (; ctx != null && argCtx != null; ctx = ctx.next)
2058: {
2059: if (nodeType == DOCUMENT_NODE)
2060: {
2061:
2062: while (ctx != null && ctx.nodeType == TEXT_NODE)
2063: ctx = ctx.next;
2064: while (argCtx != null && ctx.getNodeType() == TEXT_NODE)
2065: argCtx = argCtx.getNextSibling();
2066: if (ctx == null && argCtx != null)
2067: return false;
2068: else if (argCtx == null && ctx != null)
2069: return false;
2070: }
2071: if (!ctx.isEqualNode(argCtx))
2072: return false;
2073: argCtx = argCtx.getNextSibling();
2074: }
2075: if (ctx != null || argCtx != null)
2076: return false;
2077:
2078:
2079: return true;
2080: }
2081:
2082: boolean equal(String arg1, String arg2)
2083: {
2084: return ((arg1 == null && arg2 == null) ||
2085: (arg1 != null && arg1.equals(arg2)));
2086: }
2087:
2088: public Object getFeature(String feature, String version)
2089: {
2090: DOMImplementation impl = (nodeType == DOCUMENT_NODE) ?
2091: ((Document) this).getImplementation() : owner.getImplementation();
2092: if (impl.hasFeature(feature, version))
2093: {
2094: return this;
2095: }
2096: return null;
2097: }
2098:
2099: public Object setUserData(String key, Object data, UserDataHandler handler)
2100: {
2101: if (userData == null)
2102: {
2103: userData = new HashMap();
2104: }
2105: if (handler != null)
2106: {
2107: if (userDataHandlers == null)
2108: {
2109: userDataHandlers = new HashMap();
2110: }
2111: userDataHandlers.put(key, handler);
2112: }
2113: return userData.put(key, data);
2114: }
2115:
2116: public Object getUserData(String key)
2117: {
2118: if (userData == null)
2119: {
2120: return null;
2121: }
2122: return userData.get(key);
2123: }
2124:
2125: public String toString()
2126: {
2127: String nodeName = getNodeName();
2128: String nodeValue = getNodeValue();
2129: CPStringBuilder buf = new CPStringBuilder(getClass().getName());
2130: buf.append('[');
2131: if (nodeName != null)
2132: {
2133: buf.append(nodeName);
2134: }
2135: if (nodeValue != null)
2136: {
2137: if (nodeName != null)
2138: {
2139: buf.append('=');
2140: }
2141: buf.append('\'');
2142: buf.append(encode(nodeValue));
2143: buf.append('\'');
2144: }
2145: buf.append(']');
2146: return buf.toString();
2147: }
2148:
2149: String encode(String value)
2150: {
2151: CPStringBuilder buf = null;
2152: int len = value.length();
2153: for (int i = 0; i < len; i++)
2154: {
2155: char c = value.charAt(i);
2156: if (c == '\n')
2157: {
2158: if (buf == null)
2159: {
2160: buf = new CPStringBuilder(value.substring(0, i));
2161: }
2162: buf.append("\\n");
2163: }
2164: else if (c == '\r')
2165: {
2166: if (buf == null)
2167: {
2168: buf = new CPStringBuilder(value.substring(0, i));
2169: }
2170: buf.append("\\r");
2171: }
2172: else if (buf != null)
2173: {
2174: buf.append(c);
2175: }
2176: }
2177: return (buf != null) ? buf.toString() : value;
2178: }
2179:
2180: String nodeTypeToString(short nodeType)
2181: {
2182: switch (nodeType)
2183: {
2184: case ELEMENT_NODE:
2185: return "ELEMENT_NODE";
2186: case ATTRIBUTE_NODE:
2187: return "ATTRIBUTE_NODE";
2188: case TEXT_NODE:
2189: return "TEXT_NODE";
2190: case CDATA_SECTION_NODE:
2191: return "CDATA_SECTION_NODE";
2192: case DOCUMENT_NODE:
2193: return "DOCUMENT_NODE";
2194: case DOCUMENT_TYPE_NODE:
2195: return "DOCUMENT_TYPE_NODE";
2196: case COMMENT_NODE:
2197: return "COMMENT_NODE";
2198: case PROCESSING_INSTRUCTION_NODE:
2199: return "PROCESSING_INSTRUCTION_NODE";
2200: case DOCUMENT_FRAGMENT_NODE:
2201: return "DOCUMENT_FRAGMENT_NODE";
2202: case ENTITY_NODE:
2203: return "ENTITY_NODE";
2204: case ENTITY_REFERENCE_NODE:
2205: return "ENTITY_REFERENCE_NODE";
2206: case NOTATION_NODE:
2207: return "NOTATION_NODE";
2208: default:
2209: return "UNKNOWN";
2210: }
2211: }
2212:
2213: public void list(java.io.PrintStream out, int indent)
2214: {
2215: for (int i = 0; i < indent; i++)
2216: out.print(" ");
2217: out.println(toString());
2218: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
2219: ctx.list(out, indent + 1);
2220: }
2221:
2222: }