1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43:
44: import ;
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:
61:
93: public abstract class DomNode
94: implements Node, NodeList, EventTarget, DocumentEvent, Cloneable, Comparable
95: {
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106: private static final int NKIDS_DELTA = 8;
107: private static final int ANCESTORS_INIT = 20;
108: private static final int NOTIFICATIONS_INIT = 10;
109:
110:
111:
112:
113:
114: static final boolean reportMutations = true;
115:
116:
117: private static final Object lockNode = new Object();
118:
119:
120:
121:
122: private static boolean dispatchDataLock;
123: private static DomNode[] ancestors = new DomNode[ANCESTORS_INIT];
124: private static ListenerRecord[] notificationSet
125: = new ListenerRecord[NOTIFICATIONS_INIT];
126:
127:
128: private static boolean eventDataLock;
129: private static DomEvent.DomMutationEvent mutationEvent
130: = new DomEvent.DomMutationEvent(null);
131:
132:
133:
134:
135:
136: DomDocument owner;
137: DomNode parent;
138: DomNode previous;
139: DomNode next;
140: DomNode first;
141: DomNode last;
142: int index;
143: int depth;
144: int length;
145: final short nodeType;
146:
147:
148:
149: boolean readonly;
150:
151:
152: private ListenerRecord[] listeners;
153: private int nListeners;
154:
155:
156: private HashMap userData;
157: private HashMap userDataHandlers;
158:
159:
160:
161:
162:
163:
164:
165:
168: public void compact()
169: {
170: if (listeners != null && listeners.length != nListeners)
171: {
172: if (nListeners == 0)
173: {
174: listeners = null;
175: }
176: else
177: {
178: ListenerRecord[] l = new ListenerRecord[nListeners];
179: System.arraycopy(listeners, 0, l, 0, nListeners);
180: listeners = l;
181: }
182: }
183: }
184:
185:
191: protected DomNode(short nodeType, DomDocument owner)
192: {
193: this.nodeType = nodeType;
194:
195: if (owner == null)
196: {
197:
198: if (nodeType != DOCUMENT_NODE && nodeType != DOCUMENT_TYPE_NODE)
199: {
200: throw new IllegalArgumentException ("no owner!");
201: }
202: }
203: this.owner = owner;
204: }
205:
206:
207:
211: public NamedNodeMap getAttributes()
212: {
213: return null;
214: }
215:
216:
220: public boolean hasAttributes()
221: {
222: return false;
223: }
224:
225:
233: public NodeList getChildNodes()
234: {
235: return this;
236: }
237:
238:
242: public Node getFirstChild()
243: {
244: return first;
245: }
246:
247:
251: public Node getLastChild()
252: {
253: return last;
254: }
255:
256:
260: public boolean hasChildNodes()
261: {
262: return length != 0;
263: }
264:
265:
266:
271: public final boolean isReadonly()
272: {
273: return readonly;
274: }
275:
276:
282: public void makeReadonly()
283: {
284: readonly = true;
285: for (DomNode child = first; child != null; child = child.next)
286: {
287: child.makeReadonly();
288: }
289: }
290:
291:
294: void setOwner(DomDocument doc)
295: {
296: this.owner = doc;
297: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
298: {
299: ctx.setOwner(doc);
300: }
301: }
302:
303:
304:
305: private void checkMisc(DomNode child)
306: {
307: if (readonly && !owner.building)
308: {
309: throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
310: null, this, 0);
311: }
312: for (DomNode ctx = this; ctx != null; ctx = ctx.parent)
313: {
314: if (child == ctx)
315: {
316: throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
317: "can't make ancestor into a child",
318: this, 0);
319: }
320: }
321:
322: DomDocument owner = (nodeType == DOCUMENT_NODE) ? (DomDocument) this :
323: this.owner;
324: DomDocument childOwner = child.owner;
325: short childNodeType = child.nodeType;
326:
327: if (childOwner != owner)
328: {
329:
330: if (!(childNodeType == DOCUMENT_TYPE_NODE && childOwner == null))
331: {
332: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
333: null, child, 0);
334: }
335: }
336:
337:
338: switch (nodeType)
339: {
340: case DOCUMENT_NODE:
341: switch (childNodeType)
342: {
343: case ELEMENT_NODE:
344: case PROCESSING_INSTRUCTION_NODE:
345: case COMMENT_NODE:
346: case DOCUMENT_TYPE_NODE:
347: return;
348: }
349: break;
350:
351: case ATTRIBUTE_NODE:
352: switch (childNodeType)
353: {
354: case TEXT_NODE:
355: case ENTITY_REFERENCE_NODE:
356: return;
357: }
358: break;
359:
360: case DOCUMENT_FRAGMENT_NODE:
361: case ENTITY_REFERENCE_NODE:
362: case ELEMENT_NODE:
363: case ENTITY_NODE:
364: switch (childNodeType)
365: {
366: case ELEMENT_NODE:
367: case TEXT_NODE:
368: case COMMENT_NODE:
369: case PROCESSING_INSTRUCTION_NODE:
370: case CDATA_SECTION_NODE:
371: case ENTITY_REFERENCE_NODE:
372: return;
373: }
374: break;
375: }
376: if (owner.checkingWellformedness)
377: {
378: throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
379: "can't append " +
380: nodeTypeToString(childNodeType) +
381: " to node of type " +
382: nodeTypeToString(nodeType),
383: this, 0);
384: }
385: }
386:
387:
388:
389:
390:
391:
392:
393: private void insertionEvent(DomEvent.DomMutationEvent event,
394: DomNode target)
395: {
396: if (owner == null || owner.building)
397: {
398: return;
399: }
400: boolean doFree = false;
401:
402: if (event == null)
403: {
404: event = getMutationEvent();
405: }
406: if (event != null)
407: {
408: doFree = true;
409: }
410: else
411: {
412: event = new DomEvent.DomMutationEvent(null);
413: }
414: event.initMutationEvent("DOMNodeInserted",
415: true , false ,
416: this , null, null, null, (short) 0);
417: target.dispatchEvent(event);
418:
419:
420:
421:
422:
423: if (doFree)
424: {
425: event.target = null;
426: event.relatedNode = null;
427: event.currentNode = null;
428: eventDataLock = false;
429: }
430: }
431:
432: private void removalEvent(DomEvent.DomMutationEvent event,
433: DomNode target)
434: {
435: if (owner == null || owner.building)
436: {
437: return;
438: }
439: boolean doFree = false;
440:
441: if (event == null)
442: {
443: event = getMutationEvent();
444: }
445: if (event != null)
446: {
447: doFree = true;
448: }
449: else
450: {
451: event = new DomEvent.DomMutationEvent(null);
452: }
453: event.initMutationEvent("DOMNodeRemoved",
454: true , false ,
455: this , null, null, null, (short) 0);
456: target.dispatchEvent(event);
457:
458:
459:
460:
461:
462: event.target = null;
463: event.relatedNode = null;
464: event.currentNode = null;
465: if (doFree)
466: {
467: eventDataLock = false;
468: }
469:
470: }
471:
472:
473:
474:
475:
476:
477:
478:
479:
480:
481:
482: static private DomEvent.DomMutationEvent getMutationEvent()
483: {
484: synchronized (lockNode)
485: {
486: if (eventDataLock)
487: {
488: return null;
489: }
490: eventDataLock = true;
491: return mutationEvent;
492: }
493: }
494:
495:
496:
497: static private void freeMutationEvent()
498: {
499:
500: mutationEvent.clear();
501: eventDataLock = false;
502: }
503:
504: void setDepth(int depth)
505: {
506: this.depth = depth;
507: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
508: {
509: ctx.setDepth(depth + 1);
510: }
511: }
512:
513:
529: public Node appendChild(Node newChild)
530: {
531: try
532: {
533: DomNode child = (DomNode) newChild;
534:
535: if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
536: {
537:
538: for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
539: {
540: checkMisc(ctx);
541: }
542: for (DomNode ctx = child.first; ctx != null; )
543: {
544: DomNode ctxNext = ctx.next;
545: appendChild(ctx);
546: ctx = ctxNext;
547: }
548: }
549: else
550: {
551: checkMisc(child);
552: if (child.parent != null)
553: {
554: child.parent.removeChild(child);
555: }
556: child.parent = this;
557: child.index = length++;
558: child.setDepth(depth + 1);
559: child.next = null;
560: if (last == null)
561: {
562: first = child;
563: child.previous = null;
564: }
565: else
566: {
567: last.next = child;
568: child.previous = last;
569: }
570: last = child;
571:
572: if (reportMutations)
573: {
574: insertionEvent(null, child);
575: }
576: }
577:
578: return child;
579: }
580: catch (ClassCastException e)
581: {
582: throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
583: null, newChild, 0);
584: }
585: }
586:
587:
603: public Node insertBefore(Node newChild, Node refChild)
604: {
605: if (refChild == null)
606: {
607: return appendChild(newChild);
608: }
609:
610: try
611: {
612: DomNode child = (DomNode) newChild;
613: DomNode ref = (DomNode) refChild;
614:
615: if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
616: {
617:
618: for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
619: {
620: checkMisc(ctx);
621: }
622: for (DomNode ctx = child.first; ctx != null; )
623: {
624: DomNode ctxNext = ctx.next;
625: insertBefore(ctx, ref);
626: ctx = ctxNext;
627: }
628: }
629: else
630: {
631: checkMisc(child);
632: if (ref == null || ref.parent != this)
633: {
634: throw new DomDOMException(DOMException.NOT_FOUND_ERR,
635: null, ref, 0);
636: }
637: if (ref == child)
638: {
639: throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
640: "can't insert node before itself",
641: ref, 0);
642: }
643:
644: if (child.parent != null)
645: {
646: child.parent.removeChild(child);
647: }
648: child.parent = this;
649: int i = ref.index;
650: child.setDepth(depth + 1);
651: child.next = ref;
652: if (ref.previous != null)
653: {
654: ref.previous.next = child;
655: }
656: child.previous = ref.previous;
657: ref.previous = child;
658: if (first == ref)
659: {
660: first = child;
661: }
662:
663: for (DomNode ctx = child; ctx != null; ctx = ctx.next)
664: {
665: ctx.index = i++;
666: }
667:
668: if (reportMutations)
669: {
670: insertionEvent(null, child);
671: }
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: if (listeners != null && listeners.length != nListeners)
964: {
965: ListenerRecord[] newKids = new ListenerRecord[length];
966: System.arraycopy(listeners, 0, newKids, 0, nListeners);
967: listeners = newKids;
968: }
969: }
970:
971:
975: public Node getNextSibling()
976: {
977: return next;
978: }
979:
980:
984: public Node getPreviousSibling()
985: {
986: return previous;
987: }
988:
989:
993: public Node getParentNode()
994: {
995: return parent;
996: }
997:
998:
1007: public boolean isSupported(String feature, String version)
1008: {
1009: Document doc = owner;
1010: DOMImplementation impl = null;
1011:
1012: if (doc == null && nodeType == DOCUMENT_NODE)
1013: {
1014: doc = (Document) this;
1015: }
1016:
1017: if (doc == null)
1018: {
1019:
1020: throw new IllegalStateException ("unbound ownerDocument");
1021: }
1022:
1023: impl = doc.getImplementation();
1024: return impl.hasFeature(feature, version);
1025: }
1026:
1027:
1033: final public Document getOwnerDocument()
1034: {
1035: return owner;
1036: }
1037:
1038:
1043: public void setNodeValue(String value)
1044: {
1045: }
1046:
1047:
1052: public String getNodeValue()
1053: {
1054: return null;
1055: }
1056:
1057:
1060: public final short getNodeType()
1061: {
1062: return nodeType;
1063: }
1064:
1065:
1068: public abstract String getNodeName();
1069:
1070:
1075: public void setPrefix(String prefix)
1076: {
1077: }
1078:
1079:
1084: public String getPrefix()
1085: {
1086: return null;
1087: }
1088:
1089:
1094: public String getNamespaceURI()
1095: {
1096: return null;
1097: }
1098:
1099:
1104: public String getLocalName()
1105: {
1106: return null;
1107: }
1108:
1109:
1115: public Node cloneNode(boolean deep)
1116: {
1117: DomNode node = (DomNode) clone();
1118:
1119: if (deep)
1120: {
1121: DomDocument doc = (nodeType == DOCUMENT_NODE) ?
1122: (DomDocument) node : node.owner;
1123: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1124: {
1125: DomNode newChild = (DomNode) ctx.cloneNode(deep);
1126: newChild.setOwner(doc);
1127: node.appendChild(newChild);
1128: }
1129: }
1130:
1131: if (nodeType == ENTITY_REFERENCE_NODE)
1132: {
1133: node.makeReadonly();
1134: }
1135: notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, node);
1136: return node;
1137: }
1138:
1139: void notifyUserDataHandlers(short op, Node src, Node dst)
1140: {
1141: if (userDataHandlers != null)
1142: {
1143: for (Iterator i = userDataHandlers.entrySet().iterator(); i.hasNext(); )
1144: {
1145: Map.Entry entry = (Map.Entry) i.next();
1146: String key = (String) entry.getKey();
1147: UserDataHandler handler = (UserDataHandler) entry.getValue();
1148: Object data = userData.get(key);
1149: handler.handle(op, key, data, src, dst);
1150: }
1151: }
1152: }
1153:
1154:
1162: public Object clone()
1163: {
1164: try
1165: {
1166: DomNode node = (DomNode) super.clone();
1167:
1168: node.parent = null;
1169: node.depth = 0;
1170: node.index = 0;
1171: node.length = 0;
1172: node.first = null;
1173: node.last = null;
1174: node.previous = null;
1175: node.next = null;
1176:
1177: node.readonly = false;
1178: node.listeners = null;
1179: node.nListeners = 0;
1180: return node;
1181:
1182: }
1183: catch (CloneNotSupportedException x)
1184: {
1185: throw new Error("clone didn't work");
1186: }
1187: }
1188:
1189:
1190:
1191:
1192:
1193:
1199: public NodeList getElementsByTagName(String tag)
1200: {
1201: return new ShadowList(null, tag);
1202: }
1203:
1204:
1210: public NodeList getElementsByTagNameNS(String namespace, String local)
1211: {
1212: return new ShadowList(namespace, local);
1213: }
1214:
1215:
1216:
1217:
1218:
1219:
1220:
1221: final class ShadowList
1222: implements NodeList
1223: {
1224:
1225: private LiveNodeList liveList;
1226:
1227: ShadowList(String ns, String local)
1228: {
1229: liveList = new LiveNodeList(ns, local);
1230: }
1231:
1232: public void finalize()
1233: {
1234: liveList.detach();
1235: liveList = null;
1236: }
1237:
1238: public Node item(int index)
1239: {
1240: return liveList.item(index);
1241: }
1242:
1243: public int getLength()
1244: {
1245: return liveList.getLength();
1246: }
1247: }
1248:
1249: final class LiveNodeList
1250: implements NodeList, EventListener, NodeFilter
1251: {
1252:
1253: private final boolean matchAnyURI;
1254: private final boolean matchAnyName;
1255: private final String elementURI;
1256: private final String elementName;
1257:
1258: private DomIterator current;
1259: private int lastIndex;
1260:
1261: LiveNodeList(String uri, String name)
1262: {
1263: elementURI = uri;
1264: elementName = name;
1265: matchAnyURI = "*".equals(uri);
1266: matchAnyName = "*".equals(name);
1267:
1268: DomNode.this.addEventListener("DOMNodeInserted", this, true);
1269: DomNode.this.addEventListener("DOMNodeRemoved", this, true);
1270: }
1271:
1272: void detach()
1273: {
1274: if (current != null)
1275: current.detach();
1276: current = null;
1277:
1278: DomNode.this.removeEventListener("DOMNodeInserted", this, true);
1279: DomNode.this.removeEventListener("DOMNodeRemoved", this, true);
1280: }
1281:
1282: public short acceptNode(Node element)
1283: {
1284: if (element == DomNode.this)
1285: {
1286: return FILTER_SKIP;
1287: }
1288:
1289:
1290: if (elementURI != null)
1291: {
1292: if (!(matchAnyURI
1293: || elementURI.equals(element.getNamespaceURI())))
1294: {
1295: return FILTER_SKIP;
1296: }
1297: if (!(matchAnyName
1298: || elementName.equals(element.getLocalName())))
1299: {
1300: return FILTER_SKIP;
1301: }
1302:
1303:
1304: }
1305: else
1306: {
1307: if (!(matchAnyName
1308: || elementName.equals(element.getNodeName())))
1309: {
1310: return FILTER_SKIP;
1311: }
1312: }
1313: return FILTER_ACCEPT;
1314: }
1315:
1316: private DomIterator createIterator()
1317: {
1318: return new DomIterator(DomNode.this,
1319: NodeFilter.SHOW_ELEMENT,
1320: this,
1321: true
1322: );
1323: }
1324:
1325: public void handleEvent(Event e)
1326: {
1327: MutationEvent mutation = (MutationEvent) e;
1328: Node related = mutation.getRelatedNode();
1329:
1330:
1331:
1332:
1333: if (related.getNodeType() != Node.ELEMENT_NODE ||
1334: related.getNodeName() != elementName ||
1335: related.getNamespaceURI() != elementURI)
1336: {
1337: return;
1338: }
1339:
1340: current = null;
1341: }
1342:
1343: public Node item(int index)
1344: {
1345: if (current == null)
1346: {
1347: current = createIterator();
1348: lastIndex = -1;
1349: }
1350:
1351:
1352: if (index <= lastIndex) {
1353: while (index != lastIndex) {
1354: current.previousNode ();
1355: lastIndex--;
1356: }
1357: Node ret = current.previousNode ();
1358: current = null;
1359: return ret;
1360: }
1361:
1362:
1363: while (++lastIndex != index)
1364: current.nextNode ();
1365: Node ret = current.nextNode ();
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: current = null;
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: boolean equals(ListenerRecord rec)
1408: {
1409: return listener == rec.listener
1410: && useCapture == rec.useCapture
1411: && type == rec.type;
1412: }
1413:
1414: }
1415:
1416:
1433: public Event createEvent(String eventType)
1434: {
1435: eventType = eventType.toLowerCase();
1436:
1437: if ("mutationevents".equals(eventType))
1438: {
1439: return new DomEvent.DomMutationEvent(null);
1440: }
1441:
1442: if ("htmlevents".equals(eventType)
1443: || "events".equals(eventType)
1444: || "user-events".equals(eventType))
1445: {
1446: return new DomEvent(null);
1447: }
1448:
1449: if ("uievents".equals(eventType))
1450: {
1451: return new DomEvent.DomUIEvent(null);
1452: }
1453:
1454:
1455:
1456: throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR,
1457: eventType, null, 0);
1458: }
1459:
1460:
1464: public final void addEventListener(String type,
1465: EventListener listener,
1466: boolean useCapture)
1467: {
1468: if (listeners == null)
1469: {
1470: listeners = new ListenerRecord[1];
1471: }
1472: else if (nListeners == listeners.length)
1473: {
1474: ListenerRecord[] newListeners =
1475: new ListenerRecord[listeners.length + NKIDS_DELTA];
1476: System.arraycopy(listeners, 0, newListeners, 0, nListeners);
1477: listeners = newListeners;
1478: }
1479:
1480:
1481: ListenerRecord record;
1482:
1483: record = new ListenerRecord(type, listener, useCapture);
1484: for (int i = 0; i < nListeners; i++)
1485: {
1486: if (record.equals(listeners[i]))
1487: {
1488: return;
1489: }
1490: }
1491: listeners [nListeners++] = record;
1492: }
1493:
1494:
1495:
1496:
1497: static final class DomEventException
1498: extends EventException
1499: {
1500:
1501: DomEventException()
1502: {
1503: super(UNSPECIFIED_EVENT_TYPE_ERR, "unspecified event type");
1504: }
1505:
1506: }
1507:
1508:
1522: public final boolean dispatchEvent(Event event)
1523: throws EventException
1524: {
1525: DomEvent e = (DomEvent) event;
1526: DomNode[] ancestors = null;
1527: int ancestorMax = 0;
1528: boolean haveDispatchDataLock = false;
1529:
1530: if (e.type == null)
1531: {
1532: throw new DomEventException();
1533: }
1534:
1535: e.doDefault = true;
1536: e.target = this;
1537:
1538:
1539:
1540:
1541:
1542:
1543:
1544:
1545:
1546:
1547:
1548:
1549:
1550: try
1551: {
1552: DomNode current;
1553: int index;
1554: boolean haveAncestorRegistrations = false;
1555: ListenerRecord[] notificationSet;
1556: int ancestorLen;
1557:
1558: synchronized (lockNode)
1559: {
1560: if (!dispatchDataLock)
1561: {
1562: haveDispatchDataLock = dispatchDataLock = true;
1563: notificationSet = DomNode.notificationSet;
1564: ancestors = DomNode.ancestors;
1565: }
1566: else
1567: {
1568: notificationSet = new ListenerRecord[NOTIFICATIONS_INIT];
1569: ancestors = new DomNode[ANCESTORS_INIT];
1570: }
1571: ancestorLen = ancestors.length;
1572: }
1573:
1574:
1575:
1576:
1577:
1578:
1579:
1580: for (index = 0, current = parent;
1581: current != null && index < ancestorLen;
1582: index++, current = current.parent)
1583: {
1584: if (current.nListeners != 0)
1585: {
1586: haveAncestorRegistrations = true;
1587: }
1588: ancestors [index] = current;
1589: }
1590: if (current != null)
1591: {
1592: throw new RuntimeException("dispatchEvent capture stack size");
1593: }
1594:
1595: ancestorMax = index;
1596: e.stop = false;
1597:
1598: if (haveAncestorRegistrations)
1599: {
1600: e.eventPhase = Event.CAPTURING_PHASE;
1601: while (!e.stop && index-- > 0)
1602: {
1603: current = ancestors [index];
1604: if (current.nListeners != 0)
1605: {
1606: notifyNode(e, current, true, notificationSet);
1607: }
1608: }
1609: }
1610:
1611:
1612:
1613:
1614: if (!e.stop && nListeners != 0)
1615: {
1616: e.eventPhase = Event.AT_TARGET;
1617: notifyNode (e, this, false, notificationSet);
1618: }
1619: else if (!haveAncestorRegistrations)
1620: {
1621: e.stop = true;
1622: }
1623:
1624:
1625:
1626:
1627:
1628: if (!e.stop && e.bubbles)
1629: {
1630: e.eventPhase = Event.BUBBLING_PHASE;
1631: for (index = 0;
1632: !e.stop
1633: && index < ancestorMax
1634: && (current = ancestors[index]) != null;
1635: index++)
1636: {
1637: if (current.nListeners != 0)
1638: {
1639: notifyNode(e, current, false, notificationSet);
1640: }
1641: }
1642: }
1643: e.eventPhase = 0;
1644:
1645:
1646:
1647: return e.doDefault;
1648:
1649: }
1650: finally
1651: {
1652: if (haveDispatchDataLock)
1653: {
1654:
1655: synchronized (lockNode)
1656: {
1657:
1658: for (int i = 0; i < ancestorMax; i++)
1659: {
1660: ancestors [i] = null;
1661: }
1662:
1663:
1664: dispatchDataLock = false;
1665: }
1666: }
1667: }
1668: }
1669:
1670: private void notifyNode(DomEvent e,
1671: DomNode current,
1672: boolean capture,
1673: ListenerRecord[] notificationSet)
1674: {
1675: int count = 0;
1676:
1677:
1678: for (int i = 0; i < current.nListeners; i++)
1679: {
1680: ListenerRecord rec = current.listeners[i];
1681:
1682: if (rec.useCapture != capture)
1683: {
1684: continue;
1685: }
1686: if (!e.type.equals (rec.type))
1687: {
1688: continue;
1689: }
1690: if (count >= notificationSet.length)
1691: {
1692:
1693: int len = Math.max(notificationSet.length, 1);
1694: ListenerRecord[] tmp = new ListenerRecord[len * 2];
1695: System.arraycopy(notificationSet, 0, tmp, 0,
1696: notificationSet.length);
1697: notificationSet = tmp;
1698: }
1699: notificationSet[count++] = rec;
1700: }
1701:
1702:
1703: e.currentNode = current;
1704: for (int i = 0; i < count; i++)
1705: {
1706: try
1707: {
1708:
1709:
1710:
1711: for (int j = 0; j < current.nListeners; j++)
1712: {
1713: if (current.listeners[j].equals(notificationSet[i]))
1714: {
1715: notificationSet[i].listener.handleEvent(e);
1716: break;
1717: }
1718: }
1719:
1720: }
1721: catch (Exception x)
1722: {
1723:
1724: }
1725: notificationSet[i] = null;
1726: }
1727: }
1728:
1729:
1733: public final void removeEventListener(String type,
1734: EventListener listener,
1735: boolean useCapture)
1736: {
1737: for (int i = 0; i < nListeners; i++)
1738: {
1739: if (listeners[i].listener != listener)
1740: {
1741: continue;
1742: }
1743: if (listeners[i].useCapture != useCapture)
1744: {
1745: continue;
1746: }
1747: if (!listeners[i].type.equals(type))
1748: {
1749: continue;
1750: }
1751:
1752: if (nListeners == 1)
1753: {
1754: listeners = null;
1755: nListeners = 0;
1756: }
1757: else
1758: {
1759: for (int j = i + 1; j < nListeners; j++)
1760: {
1761: listeners[i++] = listeners[j++];
1762: }
1763: listeners[--nListeners] = null;
1764: }
1765: break;
1766: }
1767:
1768: }
1769:
1770:
1776: public final void normalize()
1777: {
1778:
1779: boolean saved = readonly;
1780: readonly = false;
1781: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1782: {
1783: switch (ctx.nodeType)
1784: {
1785: case TEXT_NODE:
1786: while (ctx.next != null && ctx.next.nodeType == TEXT_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: attrs.item(i).normalize();
1799: }
1800:
1801: case DOCUMENT_NODE:
1802: case DOCUMENT_FRAGMENT_NODE:
1803: case ATTRIBUTE_NODE:
1804: case ENTITY_REFERENCE_NODE:
1805: ctx.normalize();
1806: break;
1807: }
1808: }
1809: readonly = saved;
1810: }
1811:
1812:
1824: public boolean nameAndTypeEquals(Node other)
1825: {
1826: if (other == this)
1827: {
1828: return true;
1829: }
1830:
1831: if (nodeType != other.getNodeType())
1832: {
1833: return false;
1834: }
1835:
1836:
1837:
1838: String ns1 = this.getNamespaceURI();
1839: String ns2 = other.getNamespaceURI();
1840:
1841: if (ns1 != null && ns2 != null)
1842: {
1843: return ns1.equals(ns2) &&
1844: equal(getLocalName(), other.getLocalName());
1845: }
1846:
1847:
1848: if (ns1 == null && ns2 == null)
1849: {
1850: if (!getNodeName().equals(other.getNodeName()))
1851: {
1852: return false;
1853: }
1854:
1855:
1856:
1857:
1858: return true;
1859: }
1860:
1861:
1862: return false;
1863: }
1864:
1865:
1866:
1867: public String getBaseURI()
1868: {
1869: return (parent != null) ? parent.getBaseURI() : null;
1870: }
1871:
1872: public short compareDocumentPosition(Node other)
1873: throws DOMException
1874: {
1875: return (short) compareTo(other);
1876: }
1877:
1878:
1881: public final int compareTo(Object other)
1882: {
1883: if (other instanceof DomNode)
1884: {
1885: DomNode n1 = this;
1886: DomNode n2 = (DomNode) other;
1887: if (n1.owner != n2.owner)
1888: {
1889: return 0;
1890: }
1891: int d1 = n1.depth, d2 = n2.depth;
1892: int delta = d1 - d2;
1893: while (d1 > d2)
1894: {
1895: n1 = n1.parent;
1896: d1--;
1897: }
1898: while (d2 > d1)
1899: {
1900: n2 = n2.parent;
1901: d2--;
1902: }
1903: int c = compareTo2(n1, n2);
1904: return (c != 0) ? c : delta;
1905: }
1906: return 0;
1907: }
1908:
1909:
1912: final int compareTo2(DomNode n1, DomNode n2)
1913: {
1914: if (n1 == n2 || n1.depth == 0 || n2.depth == 0)
1915: {
1916: return 0;
1917: }
1918: int c = compareTo2(n1.parent, n2.parent);
1919: return (c != 0) ? c : n1.index - n2.index;
1920: }
1921:
1922: public final String getTextContent()
1923: throws DOMException
1924: {
1925: return getTextContent(true);
1926: }
1927:
1928: final String getTextContent(boolean topLevel)
1929: throws DOMException
1930: {
1931: switch (nodeType)
1932: {
1933: case ELEMENT_NODE:
1934: case ENTITY_NODE:
1935: case ENTITY_REFERENCE_NODE:
1936: case DOCUMENT_FRAGMENT_NODE:
1937: StringBuffer buffer = new StringBuffer();
1938: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
1939: {
1940: String textContent = ctx.getTextContent(false);
1941: if (textContent != null)
1942: {
1943: buffer.append(textContent);
1944: }
1945: }
1946: return buffer.toString();
1947: case TEXT_NODE:
1948: case CDATA_SECTION_NODE:
1949: if (((Text) this).isElementContentWhitespace())
1950: {
1951: return "";
1952: }
1953: return getNodeValue();
1954: case ATTRIBUTE_NODE:
1955: return getNodeValue();
1956: case COMMENT_NODE:
1957: case PROCESSING_INSTRUCTION_NODE:
1958: return topLevel ? getNodeValue() : "";
1959: default:
1960: return null;
1961: }
1962: }
1963:
1964: public void setTextContent(String textContent)
1965: throws DOMException
1966: {
1967: switch (nodeType)
1968: {
1969: case ELEMENT_NODE:
1970: case ATTRIBUTE_NODE:
1971: case ENTITY_NODE:
1972: case ENTITY_REFERENCE_NODE:
1973: case DOCUMENT_FRAGMENT_NODE:
1974: for (DomNode ctx = first; ctx != null; )
1975: {
1976: DomNode n = ctx.next;
1977: removeChild(ctx);
1978: ctx = n;
1979: }
1980: if (textContent != null)
1981: {
1982: Text text = owner.createTextNode(textContent);
1983: appendChild(text);
1984: }
1985: break;
1986: case TEXT_NODE:
1987: case CDATA_SECTION_NODE:
1988: case COMMENT_NODE:
1989: case PROCESSING_INSTRUCTION_NODE:
1990: setNodeValue(textContent);
1991: break;
1992: }
1993: }
1994:
1995: public boolean isSameNode(Node other)
1996: {
1997: return this == other;
1998: }
1999:
2000: public String lookupPrefix(String namespaceURI)
2001: {
2002: return (parent == null || parent == owner) ? null :
2003: parent.lookupPrefix(namespaceURI);
2004: }
2005:
2006: public boolean isDefaultNamespace(String namespaceURI)
2007: {
2008: return (parent == null || parent == owner) ? false :
2009: parent.isDefaultNamespace(namespaceURI);
2010: }
2011:
2012: public String lookupNamespaceURI(String prefix)
2013: {
2014: return (parent == null || parent == owner) ? null :
2015: parent.lookupNamespaceURI(prefix);
2016: }
2017:
2018: public boolean isEqualNode(Node arg)
2019: {
2020: if (this == arg)
2021: {
2022: return true;
2023: }
2024: if (arg == null)
2025: {
2026: return false;
2027: }
2028: if (nodeType != arg.getNodeType() ||
2029: !equal(getNodeName(), arg.getNodeName()) ||
2030: !equal(getLocalName(), arg.getLocalName()) ||
2031: !equal(getNamespaceURI(), arg.getNamespaceURI()) ||
2032: !equal(getPrefix(), arg.getPrefix()) ||
2033: !equal(getNodeValue(), arg.getNodeValue()))
2034: {
2035: return false;
2036: }
2037:
2038: Node argCtx = arg.getFirstChild();
2039: getFirstChild();
2040: for (DomNode ctx = first; ctx != null; ctx = ctx.next)
2041: {
2042: if (!ctx.isEqualNode(argCtx))
2043: {
2044: return false;
2045: }
2046: argCtx = argCtx.getNextSibling();
2047: }
2048: if (argCtx != null)
2049: {
2050: return false;
2051: }
2052:
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: }