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