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: }
672:
673: return child;
674: }
675: catch (ClassCastException e)
676: {
677: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
678: null, newChild, 0);
679: }
680: }
681:
682:
707: public Node replaceChild(Node newChild, Node refChild)
708: {
709: try
710: {
711: DomNode child = (DomNode) newChild;
712: DomNode ref = (DomNode) refChild;
713:
714: DomEvent.DomMutationEvent event = getMutationEvent();
715: boolean doFree = (event != null);
716:
717: if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
718: {
719:
720: for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
721: {
722: checkMisc(ctx);
723: }
724: if (ref == null || ref.parent != this)
725: {
726: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
727: null, ref, 0);
728: }
729:
730: if (reportMutations)
731: {
732: removalEvent(event, ref);
733: }
734: length--;
735: length += child.length;
736:
737: if (child.length == 0)
738: {
739:
740: if (ref.previous != null)
741: {
742: ref.previous.next = ref.next;
743: }
744: if (ref.next != null)
745: {
746: ref.next.previous = ref.previous;
747: }
748: if (first == ref)
749: {
750: first = ref.next;
751: }
752: if (last == ref)
753: {
754: last = ref.previous;
755: }
756: }
757: else
758: {
759: int i = ref.index;
760: for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
761: {
762:
763: ctx.parent = this;
764: ctx.index = i++;
765: ctx.setDepth(ref.depth);
766: if (ctx == child.first)
767: {
768: ctx.previous = ref.previous;
769: }
770: if (ctx == child.last)
771: {
772: ctx.next = ref.next;
773: }
774: }
775: if (first == ref)
776: {
777: first = child.first;
778: }
779: if (last == ref)
780: {
781: last = child.last;
782: }
783: }
784: }
785: else
786: {
787: checkMisc(child);
788: if (ref == null || ref.parent != this)
789: {
790: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
791: null, ref, 0);
792: }
793:
794: if (reportMutations)
795: {
796: removalEvent(event, ref);
797: }
798:
799: if (child.parent != null)
800: {
801: child.parent.removeChild(child);
802: }
803: child.parent = this;
804: child.index = ref.index;
805: child.setDepth(ref.depth);
806: if (ref.previous != null)
807: {
808: ref.previous.next = child;
809: }
810: child.previous = ref.previous;
811: if (ref.next != null)
812: {
813: ref.next.previous = child;
814: }
815: child.next = ref.next;
816: if (first == ref)
817: {
818: first = child;
819: }
820: if (last == ref)
821: {
822: last = child;
823: }
824:
825: if (reportMutations)
826: {
827: insertionEvent(event, child);
828: }
829: if (doFree)
830: {
831: freeMutationEvent();
832: }
833: }
834: ref.parent = null;
835: ref.index = 0;
836: ref.setDepth(0);
837: ref.previous = null;
838: ref.next = null;
839:
840: return ref;
841: }
842: catch (ClassCastException e)
843: {
844: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
845: null, newChild, 0);
846: }
847: }
848:
849:
859: public Node removeChild(Node refChild)
860: {
861: try
862: {
863: DomNode ref = (DomNode) refChild;
864:
865: if (ref == null || ref.parent != this)
866: {
867: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
868: null, ref, 0);
869: }
870: if (readonly && !owner.building)
871: {
872: throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
873: null, this, 0);
874: }
875:
876: for (DomNode child = first; child != null; child = child.next)
877: {
878: if (child == ref)
879: {
880: if (reportMutations)
881: {
882: removalEvent(null, child);
883: }
884:
885: length--;
886: if (ref.previous != null)
887: {
888: ref.previous.next = ref.next;
889: }
890: if (ref.next != null)
891: {
892: ref.next.previous = ref.previous;
893: }
894: if (first == ref)
895: {
896: first = ref.next;
897: }
898: if (last == ref)
899: {
900: last = ref.previous;
901: }
902:
903: int i = 0;
904: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
905: {
906: ctx.index = i++;
907: }
908: ref.parent = null;
909: ref.setDepth(0);
910: ref.index = 0;
911: ref.previous = null;
912: ref.next = null;
913:
914: return ref;
915: }
916: }
917: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
918: "that's no child of mine", refChild, 0);
919: }
920: catch (ClassCastException e)
921: {
922: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
923: null, refChild, 0);
924: }
925: }
926:
927:
932: public Node item(int index)
933: {
934: DomNode child = first;
935: int count = 0;
936: while (child != null && count < index)
937: {
938: child = child.next;
939: count++;
940: }
941: return child;
942: }
943:
944:
951: public int getLength()
952: {
953: return length;
954: }
955:
956:
960: public void trimToSize()
961: {
962: }
963:
964:
968: public Node getNextSibling()
969: {
970: return next;
971: }
972:
973:
977: public Node getPreviousSibling()
978: {
979: return previous;
980: }
981:
982:
986: public Node getParentNode()
987: {
988: return parent;
989: }
990:
991:
1000: public boolean isSupported(String feature, String version)
1001: {
1002: Document doc = owner;
1003: DOMImplementation impl = null;
1004:
1005: if (doc == null && nodeType == DOCUMENT_NODE)
1006: {
1007: doc = (Document) this;
1008: }
1009:
1010: if (doc == null)
1011: {
1012:
1013: throw new IllegalStateException ("unbound ownerDocument");
1014: }
1015:
1016: impl = doc.getImplementation();
1017: return impl.hasFeature(feature, version);
1018: }
1019:
1020:
1026: final public Document getOwnerDocument()
1027: {
1028: return owner;
1029: }
1030:
1031:
1036: public void setNodeValue(String value)
1037: {
1038: }
1039:
1040:
1045: public String getNodeValue()
1046: {
1047: return null;
1048: }
1049:
1050:
1053: public final short getNodeType()
1054: {
1055: return nodeType;
1056: }
1057:
1058:
1061: public abstract String getNodeName();
1062:
1063:
1068: public void setPrefix(String prefix)
1069: {
1070: }
1071:
1072:
1077: public String getPrefix()
1078: {
1079: return null;
1080: }
1081:
1082:
1087: public String getNamespaceURI()
1088: {
1089: return null;
1090: }
1091:
1092:
1097: public String getLocalName()
1098: {
1099: return null;
1100: }
1101:
1102:
1108: public Node cloneNode(boolean deep)
1109: {
1110: DomNode node = (DomNode) clone();
1111:
1112: if (deep)
1113: {
1114: DomDocument doc = (nodeType == DOCUMENT_NODE) ?
1115: (DomDocument) node : node.owner;
1116: boolean building = doc.building;
1117: doc.building = true;
1118: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1119: {
1120: DomNode newChild = (DomNode) ctx.cloneNode(deep);
1121: newChild.setOwner(doc);
1122: node.appendChild(newChild);
1123: }
1124: doc.building = building;
1125: }
1126: if (nodeType == ENTITY_REFERENCE_NODE)
1127: {
1128: node.makeReadonly();
1129: }
1130: notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, node);
1131: return node;
1132: }
1133:
1134: void notifyUserDataHandlers(short op, Node src, Node dst)
1135: {
1136: if (userDataHandlers != null)
1137: {
1138: for (Iterator i = userDataHandlers.entrySet().iterator(); i.hasNext(); )
1139: {
1140: Map.Entry entry = (Map.Entry) i.next();
1141: String key = (String) entry.getKey();
1142: UserDataHandler handler = (UserDataHandler) entry.getValue();
1143: Object data = userData.get(key);
1144: handler.handle(op, key, data, src, dst);
1145: }
1146: }
1147: }
1148:
1149:
1157: public Object clone()
1158: {
1159: try
1160: {
1161: DomNode node = (DomNode) super.clone();
1162:
1163: node.parent = null;
1164: node.depth = 0;
1165: node.index = 0;
1166: node.length = 0;
1167: node.first = null;
1168: node.last = null;
1169: node.previous = null;
1170: node.next = null;
1171:
1172: node.readonly = false;
1173: node.listeners = new HashSet();
1174: node.nListeners = 0;
1175: return node;
1176:
1177: }
1178: catch (CloneNotSupportedException x)
1179: {
1180: throw new Error("clone didn't work");
1181: }
1182: }
1183:
1184:
1185:
1186:
1187:
1188:
1194: public NodeList getElementsByTagName(String tag)
1195: {
1196: return new ShadowList(null, tag);
1197: }
1198:
1199:
1205: public NodeList getElementsByTagNameNS(String namespace, String local)
1206: {
1207: return new ShadowList(namespace, local);
1208: }
1209:
1210:
1211:
1212:
1213:
1214:
1215:
1216: final class ShadowList
1217: implements NodeList
1218: {
1219:
1220: private LiveNodeList liveList;
1221:
1222: ShadowList(String ns, String local)
1223: {
1224: liveList = new LiveNodeList(ns, local);
1225: }
1226:
1227: public void finalize()
1228: {
1229: liveList.detach();
1230: liveList = null;
1231: }
1232:
1233: public Node item(int index)
1234: {
1235: return liveList.item(index);
1236: }
1237:
1238: public int getLength()
1239: {
1240: return liveList.getLength();
1241: }
1242: }
1243:
1244: final class LiveNodeList
1245: implements NodeList, EventListener, NodeFilter
1246: {
1247:
1248: private final boolean matchAnyURI;
1249: private final boolean matchAnyName;
1250: private final String elementURI;
1251: private final String elementName;
1252:
1253: private DomIterator current;
1254: private int lastIndex;
1255:
1256: LiveNodeList(String uri, String name)
1257: {
1258: elementURI = uri;
1259: elementName = name;
1260: matchAnyURI = "*".equals(uri);
1261: matchAnyName = "*".equals(name);
1262:
1263: DomNode.this.addEventListener("DOMNodeInserted", this, true);
1264: DomNode.this.addEventListener("DOMNodeRemoved", this, true);
1265: }
1266:
1267: void detach()
1268: {
1269: if (current != null)
1270: current.detach();
1271: current = null;
1272:
1273: DomNode.this.removeEventListener("DOMNodeInserted", this, true);
1274: DomNode.this.removeEventListener("DOMNodeRemoved", this, true);
1275: }
1276:
1277: public short acceptNode(Node element)
1278: {
1279: if (element == DomNode.this)
1280: {
1281: return FILTER_SKIP;
1282: }
1283:
1284:
1285: if (elementURI != null)
1286: {
1287: if (!(matchAnyURI
1288: || elementURI.equals(element.getNamespaceURI())))
1289: {
1290: return FILTER_SKIP;
1291: }
1292: if (!(matchAnyName
1293: || elementName.equals(element.getLocalName())))
1294: {
1295: return FILTER_SKIP;
1296: }
1297:
1298:
1299: }
1300: else
1301: {
1302: if (!(matchAnyName
1303: || elementName.equals(element.getNodeName())))
1304: {
1305: return FILTER_SKIP;
1306: }
1307: }
1308: return FILTER_ACCEPT;
1309: }
1310:
1311: private DomIterator createIterator()
1312: {
1313: return new DomIterator(DomNode.this,
1314: NodeFilter.SHOW_ELEMENT,
1315: this,
1316: true
1317: );
1318: }
1319:
1320: public void handleEvent(Event e)
1321: {
1322: MutationEvent mutation = (MutationEvent) e;
1323: Node related = mutation.getRelatedNode();
1324:
1325:
1326:
1327:
1328: if (related.getNodeType() != Node.ELEMENT_NODE ||
1329: related.getNodeName() != elementName ||
1330: related.getNamespaceURI() != elementURI)
1331: {
1332: return;
1333: }
1334:
1335: if (current != null)
1336: current.detach();
1337: current = null;
1338: }
1339:
1340: public Node item(int index)
1341: {
1342: if (current == null)
1343: {
1344: current = createIterator();
1345: lastIndex = -1;
1346: }
1347:
1348:
1349: if (index <= lastIndex) {
1350: while (index != lastIndex) {
1351: current.previousNode ();
1352: lastIndex--;
1353: }
1354: Node ret = current.previousNode ();
1355: current.detach();
1356: current = null;
1357: return ret;
1358: }
1359:
1360:
1361: while (++lastIndex != index)
1362: current.nextNode ();
1363:
1364: Node ret = current.nextNode ();
1365: current.detach();
1366: current = null;
1367: return ret;
1368: }
1369:
1370: public int getLength()
1371: {
1372: int retval = 0;
1373: NodeIterator iter = createIterator();
1374:
1375: while (iter.nextNode() != null)
1376: {
1377: retval++;
1378: }
1379: iter.detach();
1380: return retval;
1381: }
1382:
1383: }
1384:
1385:
1386:
1387:
1388: static final class ListenerRecord
1389: {
1390:
1391: String type;
1392: EventListener listener;
1393: boolean useCapture;
1394:
1395:
1396:
1397:
1398:
1399:
1400: ListenerRecord(String type, EventListener listener, boolean useCapture)
1401: {
1402: this.type = type.intern();
1403: this.listener = listener;
1404: this.useCapture = useCapture;
1405: }
1406:
1407: public boolean equals(Object o)
1408: {
1409: ListenerRecord rec = (ListenerRecord)o;
1410: return listener == rec.listener
1411: && useCapture == rec.useCapture
1412: && type == rec.type;
1413: }
1414:
1415: public int hashCode()
1416: {
1417: return listener.hashCode() ^ type.hashCode();
1418: }
1419: }
1420:
1421:
1438: public Event createEvent(String eventType)
1439: {
1440: eventType = eventType.toLowerCase();
1441:
1442: if ("mutationevents".equals(eventType))
1443: {
1444: return new DomEvent.DomMutationEvent(null);
1445: }
1446:
1447: if ("htmlevents".equals(eventType)
1448: || "events".equals(eventType)
1449: || "user-events".equals(eventType))
1450: {
1451: return new DomEvent(null);
1452: }
1453:
1454: if ("uievents".equals(eventType))
1455: {
1456: return new DomEvent.DomUIEvent(null);
1457: }
1458:
1459:
1460:
1461: throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR,
1462: eventType, null, 0);
1463: }
1464:
1465:
1469: public final void addEventListener(String type,
1470: EventListener listener,
1471: boolean useCapture)
1472: {
1473:
1474: ListenerRecord record;
1475:
1476: record = new ListenerRecord(type, listener, useCapture);
1477: listeners.add(record);
1478: nListeners = listeners.size();
1479: }
1480:
1481:
1482:
1483:
1484: static final class DomEventException
1485: extends EventException
1486: {
1487:
1488: DomEventException()
1489: {
1490: super(UNSPECIFIED_EVENT_TYPE_ERR, "unspecified event type");
1491: }
1492:
1493: }
1494:
1495:
1509: public final boolean dispatchEvent(Event event)
1510: throws EventException
1511: {
1512: DomEvent e = (DomEvent) event;
1513: DomNode[] ancestors = null;
1514: int ancestorMax = 0;
1515: boolean haveDispatchDataLock = false;
1516:
1517: if (e.type == null)
1518: {
1519: throw new DomEventException();
1520: }
1521:
1522: e.doDefault = true;
1523: e.target = this;
1524:
1525:
1526:
1527:
1528:
1529:
1530:
1531:
1532:
1533:
1534:
1535:
1536:
1537: try
1538: {
1539: DomNode current;
1540: int index;
1541: boolean haveAncestorRegistrations = false;
1542: ListenerRecord[] notificationSet;
1543: int ancestorLen;
1544:
1545: synchronized (lockNode)
1546: {
1547: if (!dispatchDataLock)
1548: {
1549: haveDispatchDataLock = dispatchDataLock = true;
1550: notificationSet = DomNode.notificationSet;
1551: ancestors = DomNode.ancestors;
1552: }
1553: else
1554: {
1555: notificationSet = new ListenerRecord[NOTIFICATIONS_INIT];
1556: ancestors = new DomNode[ANCESTORS_INIT];
1557: }
1558: ancestorLen = ancestors.length;
1559: }
1560:
1561:
1562:
1563:
1564: current = parent;
1565: if (current.depth >= ANCESTORS_INIT)
1566: {
1567: DomNode[] newants = new DomNode[current.depth + 1];
1568: System.arraycopy(ancestors, 0, newants, 0, ancestors.length);
1569: ancestors = newants;
1570: ancestorLen = ancestors.length;
1571: }
1572: for (index = 0; index < ancestorLen; index++)
1573: {
1574: if (current == null || current.depth == 0)
1575: break;
1576:
1577: if (current.nListeners != 0)
1578: {
1579: haveAncestorRegistrations = true;
1580: }
1581: ancestors [index] = current;
1582: current = current.parent;
1583: }
1584: if (current.depth > 0)
1585: {
1586: throw new RuntimeException("dispatchEvent capture stack size");
1587: }
1588:
1589: ancestorMax = index;
1590: e.stop = false;
1591:
1592: if (haveAncestorRegistrations)
1593: {
1594: e.eventPhase = Event.CAPTURING_PHASE;
1595: while (!e.stop && index-- > 0)
1596: {
1597: current = ancestors [index];
1598: if (current.nListeners != 0)
1599: {
1600: notifyNode(e, current, true, notificationSet);
1601: }
1602: }
1603: }
1604:
1605:
1606:
1607:
1608: if (!e.stop && nListeners != 0)
1609: {
1610: e.eventPhase = Event.AT_TARGET;
1611: notifyNode (e, this, false, notificationSet);
1612: }
1613: else if (!haveAncestorRegistrations)
1614: {
1615: e.stop = true;
1616: }
1617:
1618:
1619:
1620:
1621:
1622: if (!e.stop && e.bubbles)
1623: {
1624: e.eventPhase = Event.BUBBLING_PHASE;
1625: for (index = 0;
1626: !e.stop
1627: && index < ancestorMax
1628: && (current = ancestors[index]) != null;
1629: index++)
1630: {
1631: if (current.nListeners != 0)
1632: {
1633: notifyNode(e, current, false, notificationSet);
1634: }
1635: }
1636: }
1637: e.eventPhase = 0;
1638:
1639:
1640:
1641: return e.doDefault;
1642:
1643: }
1644: finally
1645: {
1646: if (haveDispatchDataLock)
1647: {
1648:
1649: synchronized (lockNode)
1650: {
1651:
1652: for (int i = 0; i < ancestorMax; i++)
1653: {
1654: ancestors [i] = null;
1655: }
1656:
1657:
1658: dispatchDataLock = false;
1659: }
1660: }
1661: }
1662: }
1663:
1664: private void notifyNode(DomEvent e,
1665: DomNode current,
1666: boolean capture,
1667: ListenerRecord[] notificationSet)
1668: {
1669: int count = 0;
1670: Iterator iter;
1671:
1672: iter = current.listeners.iterator();
1673:
1674:
1675: while (iter.hasNext())
1676: {
1677: ListenerRecord rec = (ListenerRecord)iter.next();
1678:
1679: if (rec.useCapture != capture)
1680: {
1681: continue;
1682: }
1683: if (!e.type.equals (rec.type))
1684: {
1685: continue;
1686: }
1687: if (count >= notificationSet.length)
1688: {
1689:
1690: int len = Math.max(notificationSet.length, 1);
1691: ListenerRecord[] tmp = new ListenerRecord[len * 2];
1692: System.arraycopy(notificationSet, 0, tmp, 0,
1693: notificationSet.length);
1694: notificationSet = tmp;
1695: }
1696: notificationSet[count++] = rec;
1697: }
1698: iter = null;
1699:
1700:
1701: e.currentNode = current;
1702: for (int i = 0; i < count; i++)
1703: {
1704: try
1705: {
1706: iter = current.listeners.iterator();
1707:
1708:
1709:
1710: while (iter.hasNext())
1711: {
1712: ListenerRecord rec = (ListenerRecord)iter.next();
1713:
1714: if (rec.equals(notificationSet[i]))
1715: {
1716: notificationSet[i].listener.handleEvent(e);
1717: break;
1718: }
1719: }
1720: iter = null;
1721: }
1722: catch (Exception x)
1723: {
1724:
1725: }
1726: notificationSet[i] = null;
1727: }
1728: }
1729:
1730:
1734: public final void removeEventListener(String type,
1735: EventListener listener,
1736: boolean useCapture)
1737: {
1738: listeners.remove(new ListenerRecord(type, listener, useCapture));
1739: nListeners = listeners.size();
1740:
1741: }
1742:
1743:
1749: public final void normalize()
1750: {
1751:
1752: boolean saved = readonly;
1753: readonly = false;
1754: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1755: {
1756: boolean saved2 = ctx.readonly;
1757: ctx.readonly = false;
1758: switch (ctx.nodeType)
1759: {
1760: case TEXT_NODE:
1761: case CDATA_SECTION_NODE:
1762: while (ctx.next != null &&
1763: (ctx.next.nodeType == TEXT_NODE ||
1764: ctx.next.nodeType == CDATA_SECTION_NODE))
1765: {
1766: Text text = (Text) ctx;
1767: text.appendData(ctx.next.getNodeValue());
1768: removeChild(ctx.next);
1769: }
1770: break;
1771: case ELEMENT_NODE:
1772: NamedNodeMap attrs = ctx.getAttributes();
1773: int len = attrs.getLength();
1774: for (int i = 0; i < len; i++)
1775: {
1776: DomNode attr = (DomNode) attrs.item(i);
1777: boolean saved3 = attr.readonly;
1778: attr.readonly = false;
1779: attr.normalize();
1780: attr.readonly = saved3;
1781: }
1782:
1783: case DOCUMENT_NODE:
1784: case DOCUMENT_FRAGMENT_NODE:
1785: case ATTRIBUTE_NODE:
1786: case ENTITY_REFERENCE_NODE:
1787: ctx.normalize();
1788: break;
1789: }
1790: ctx.readonly = saved2;
1791: }
1792: readonly = saved;
1793: }
1794:
1795:
1807: public boolean nameAndTypeEquals(Node other)
1808: {
1809: if (other == this)
1810: {
1811: return true;
1812: }
1813:
1814: if (nodeType != other.getNodeType())
1815: {
1816: return false;
1817: }
1818:
1819:
1820:
1821: String ns1 = this.getNamespaceURI();
1822: String ns2 = other.getNamespaceURI();
1823:
1824: if (ns1 != null && ns2 != null)
1825: {
1826: return ns1.equals(ns2) &&
1827: equal(getLocalName(), other.getLocalName());
1828: }
1829:
1830:
1831: if (ns1 == null && ns2 == null)
1832: {
1833: if (!getNodeName().equals(other.getNodeName()))
1834: {
1835: return false;
1836: }
1837:
1838:
1839:
1840:
1841: return true;
1842: }
1843:
1844:
1845: return false;
1846: }
1847:
1848:
1849:
1850: public String getBaseURI()
1851: {
1852: return (parent != null) ? parent.getBaseURI() : null;
1853: }
1854:
1855: public short compareDocumentPosition(Node other)
1856: throws DOMException
1857: {
1858: return (short) compareTo(other);
1859: }
1860:
1861:
1864: public final int compareTo(Object other)
1865: {
1866: if (other instanceof DomNode)
1867: {
1868: DomNode n1 = this;
1869: DomNode n2 = (DomNode) other;
1870: if (n1.owner != n2.owner)
1871: {
1872: return 0;
1873: }
1874: int d1 = n1.depth, d2 = n2.depth;
1875: int delta = d1 - d2;
1876: while (d1 > d2)
1877: {
1878: n1 = n1.parent;
1879: d1--;
1880: }
1881: while (d2 > d1)
1882: {
1883: n2 = n2.parent;
1884: d2--;
1885: }
1886: int c = compareTo2(n1, n2);
1887: return (c != 0) ? c : delta;
1888: }
1889: return 0;
1890: }
1891:
1892:
1895: final int compareTo2(DomNode n1, DomNode n2)
1896: {
1897: if (n1 == n2 || n1.depth == 0 || n2.depth == 0)
1898: {
1899: return 0;
1900: }
1901: int c = compareTo2(n1.parent, n2.parent);
1902: return (c != 0) ? c : n1.index - n2.index;
1903: }
1904:
1905: public final String getTextContent()
1906: throws DOMException
1907: {
1908: return getTextContent(true);
1909: }
1910:
1911: final String getTextContent(boolean topLevel)
1912: throws DOMException
1913: {
1914: switch (nodeType)
1915: {
1916: case ELEMENT_NODE:
1917: case ENTITY_NODE:
1918: case ENTITY_REFERENCE_NODE:
1919: case DOCUMENT_FRAGMENT_NODE:
1920: StringBuffer buffer = new StringBuffer();
1921: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1922: {
1923: String textContent = ctx.getTextContent(false);
1924: if (textContent != null)
1925: {
1926: buffer.append(textContent);
1927: }
1928: }
1929: return buffer.toString();
1930: case TEXT_NODE:
1931: case CDATA_SECTION_NODE:
1932: if (((Text) this).isElementContentWhitespace())
1933: {
1934: return "";
1935: }
1936: return getNodeValue();
1937: case ATTRIBUTE_NODE:
1938: return getNodeValue();
1939: case COMMENT_NODE:
1940: case PROCESSING_INSTRUCTION_NODE:
1941: return topLevel ? getNodeValue() : "";
1942: default:
1943: return null;
1944: }
1945: }
1946:
1947: public void setTextContent(String textContent)
1948: throws DOMException
1949: {
1950: switch (nodeType)
1951: {
1952: case ELEMENT_NODE:
1953: case ATTRIBUTE_NODE:
1954: case ENTITY_NODE:
1955: case ENTITY_REFERENCE_NODE:
1956: case DOCUMENT_FRAGMENT_NODE:
1957: for (DomNode ctx = first; ctx != null; )
1958: {
1959: DomNode n = ctx.next;
1960: removeChild(ctx);
1961: ctx = n;
1962: }
1963: if (textContent != null)
1964: {
1965: Text text = owner.createTextNode(textContent);
1966: appendChild(text);
1967: }
1968: break;
1969: case TEXT_NODE:
1970: case CDATA_SECTION_NODE:
1971: case COMMENT_NODE:
1972: case PROCESSING_INSTRUCTION_NODE:
1973: setNodeValue(textContent);
1974: break;
1975: }
1976: }
1977:
1978: public boolean isSameNode(Node other)
1979: {
1980: return this == other;
1981: }
1982:
1983: public String lookupPrefix(String namespaceURI)
1984: {
1985: return (parent == null || parent == owner) ? null :
1986: parent.lookupPrefix(namespaceURI);
1987: }
1988:
1989: public boolean isDefaultNamespace(String namespaceURI)
1990: {
1991: return (parent == null || parent == owner) ? false :
1992: parent.isDefaultNamespace(namespaceURI);
1993: }
1994:
1995: public String lookupNamespaceURI(String prefix)
1996: {
1997: return (parent == null || parent == owner) ? null :
1998: parent.lookupNamespaceURI(prefix);
1999: }
2000:
2001: public boolean isEqualNode(Node arg)
2002: {
2003: if (this == arg)
2004: return true;
2005: if (arg == null)
2006: return false;
2007: if (nodeType != arg.getNodeType())
2008: return false;
2009: switch (nodeType)
2010: {
2011: case ELEMENT_NODE:
2012: case ATTRIBUTE_NODE:
2013: if (!equal(getLocalName(), arg.getLocalName()) ||
2014: !equal(getNamespaceURI(), arg.getNamespaceURI()))
2015: return false;
2016: break;
2017: case PROCESSING_INSTRUCTION_NODE:
2018: if (!equal(getNodeName(), arg.getNodeName()) ||
2019: !equal(getNodeValue(), arg.getNodeValue()))
2020: return false;
2021: break;
2022: case COMMENT_NODE:
2023: case TEXT_NODE:
2024: case CDATA_SECTION_NODE:
2025: if (!equal(getNodeValue(), arg.getNodeValue()))
2026: return false;
2027: break;
2028: }
2029:
2030: Node argCtx = arg.getFirstChild();
2031: getFirstChild();
2032: DomNode ctx = first;
2033: for (; ctx != null && argCtx != null; ctx = ctx.next)
2034: {
2035: if (nodeType == DOCUMENT_NODE)
2036: {
2037:
2038: while (ctx != null && ctx.nodeType == TEXT_NODE)
2039: ctx = ctx.next;
2040: while (argCtx != null && ctx.getNodeType() == TEXT_NODE)
2041: argCtx = argCtx.getNextSibling();
2042: if (ctx == null && argCtx != null)
2043: return false;
2044: else if (argCtx == null && ctx != null)
2045: return false;
2046: }
2047: if (!ctx.isEqualNode(argCtx))
2048: return false;
2049: argCtx = argCtx.getNextSibling();
2050: }
2051: if (ctx != null || argCtx != null)
2052: return false;
2053:
2054:
2055: return true;
2056: }
2057:
2058: boolean equal(String arg1, String arg2)
2059: {
2060: return ((arg1 == null && arg2 == null) ||
2061: (arg1 != null && arg1.equals(arg2)));
2062: }
2063:
2064: public Object getFeature(String feature, String version)
2065: {
2066: DOMImplementation impl = (nodeType == DOCUMENT_NODE) ?
2067: ((Document) this).getImplementation() : owner.getImplementation();
2068: if (impl.hasFeature(feature, version))
2069: {
2070: return this;
2071: }
2072: return null;
2073: }
2074:
2075: public Object setUserData(String key, Object data, UserDataHandler handler)
2076: {
2077: if (userData == null)
2078: {
2079: userData = new HashMap();
2080: }
2081: if (handler != null)
2082: {
2083: if (userDataHandlers == null)
2084: {
2085: userDataHandlers = new HashMap();
2086: }
2087: userDataHandlers.put(key, handler);
2088: }
2089: return userData.put(key, data);
2090: }
2091:
2092: public Object getUserData(String key)
2093: {
2094: if (userData == null)
2095: {
2096: return null;
2097: }
2098: return userData.get(key);
2099: }
2100:
2101: public String toString()
2102: {
2103: String nodeName = getNodeName();
2104: String nodeValue = getNodeValue();
2105: StringBuffer buf = new StringBuffer(getClass().getName());
2106: buf.append('[');
2107: if (nodeName != null)
2108: {
2109: buf.append(nodeName);
2110: }
2111: if (nodeValue != null)
2112: {
2113: if (nodeName != null)
2114: {
2115: buf.append('=');
2116: }
2117: buf.append('\'');
2118: buf.append(encode(nodeValue));
2119: buf.append('\'');
2120: }
2121: buf.append(']');
2122: return buf.toString();
2123: }
2124:
2125: String encode(String value)
2126: {
2127: StringBuffer buf = null;
2128: int len = value.length();
2129: for (int i = 0; i < len; i++)
2130: {
2131: char c = value.charAt(i);
2132: if (c == '\n')
2133: {
2134: if (buf == null)
2135: {
2136: buf = new StringBuffer(value.substring(0, i));
2137: }
2138: buf.append("\\n");
2139: }
2140: else if (c == '\r')
2141: {
2142: if (buf == null)
2143: {
2144: buf = new StringBuffer(value.substring(0, i));
2145: }
2146: buf.append("\\r");
2147: }
2148: else if (buf != null)
2149: {
2150: buf.append(c);
2151: }
2152: }
2153: return (buf != null) ? buf.toString() : value;
2154: }
2155:
2156: String nodeTypeToString(short nodeType)
2157: {
2158: switch (nodeType)
2159: {
2160: case ELEMENT_NODE:
2161: return "ELEMENT_NODE";
2162: case ATTRIBUTE_NODE:
2163: return "ATTRIBUTE_NODE";
2164: case TEXT_NODE:
2165: return "TEXT_NODE";
2166: case CDATA_SECTION_NODE:
2167: return "CDATA_SECTION_NODE";
2168: case DOCUMENT_NODE:
2169: return "DOCUMENT_NODE";
2170: case DOCUMENT_TYPE_NODE:
2171: return "DOCUMENT_TYPE_NODE";
2172: case COMMENT_NODE:
2173: return "COMMENT_NODE";
2174: case PROCESSING_INSTRUCTION_NODE:
2175: return "PROCESSING_INSTRUCTION_NODE";
2176: case DOCUMENT_FRAGMENT_NODE:
2177: return "DOCUMENT_FRAGMENT_NODE";
2178: case ENTITY_NODE:
2179: return "ENTITY_NODE";
2180: case ENTITY_REFERENCE_NODE:
2181: return "ENTITY_REFERENCE_NODE";
2182: case NOTATION_NODE:
2183: return "NOTATION_NODE";
2184: default:
2185: return "UNKNOWN";
2186: }
2187: }
2188:
2189: public void list(java.io.PrintStream out, int indent)
2190: {
2191: for (int i = 0; i < indent; i++)
2192: out.print(" ");
2193: out.println(toString());
2194: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
2195: ctx.list(out, indent + 1);
2196: }
2197:
2198: }