001: /*
002: * $Id: EventMatcher.java,v 1.2 2004/07/12 17:34:54 cniles Exp $
003: *
004: * Copyright (c) 2004, Christian Niles, unit12.net
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions are met:
009: *
010: * * Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * * Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: *
017: * * Neither the name of Christian Niles, Unit12, nor the names of its
018: * contributors may be used to endorse or promote products derived from
019: * this software without specific prior written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
022: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
023: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
024: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
025: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
026: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
027: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
029: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
030: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
031: * POSSIBILITY OF SUCH DAMAGE.
032: *
033: */
034: package javanet.staxutils.helpers;
035:
036: import java.util.Iterator;
037:
038: import javax.xml.stream.events.Attribute;
039: import javax.xml.stream.events.Characters;
040: import javax.xml.stream.events.Comment;
041: import javax.xml.stream.events.DTD;
042: import javax.xml.stream.events.EndDocument;
043: import javax.xml.stream.events.EndElement;
044: import javax.xml.stream.events.EntityDeclaration;
045: import javax.xml.stream.events.EntityReference;
046: import javax.xml.stream.events.Namespace;
047: import javax.xml.stream.events.NotationDeclaration;
048: import javax.xml.stream.events.ProcessingInstruction;
049: import javax.xml.stream.events.StartDocument;
050: import javax.xml.stream.events.StartElement;
051: import javax.xml.stream.events.XMLEvent;
052:
053: /**
054: * Provides utility methods useful for comparing two {@link XMLEvent} instances.
055: * These methods compare only location/type-independent information, and thus don't
056: * perform strict equality testing, which would include the event's location,
057: * schema-type and dtd-type.
058: *
059: * @author Christian Niles
060: * @version $Revision: 1.2 $
061: */
062: public final class EventMatcher {
063:
064: /**
065: * Prevent instantiation
066: */
067: private EventMatcher() {
068:
069: }
070:
071: /**
072: * Compares two {@link XMLEvent} instances. This method delegates actual
073: * matching to the appropriate overloaded method.
074: *
075: * @param a The first event.
076: * @param b The second event.
077: * @return <code>true</code> if the events match, <code>false</code> otherwise.
078: */
079: public static boolean eventsMatch(XMLEvent a, XMLEvent b) {
080:
081: if (a == b) {
082:
083: return true;
084:
085: } else if (a == null || b == null) {
086:
087: return false;
088:
089: } else if (a.getEventType() == b.getEventType()) {
090:
091: switch (a.getEventType()) {
092:
093: case XMLEvent.START_ELEMENT:
094: return eventsMatch(a.asStartElement(), b
095: .asStartElement());
096:
097: case XMLEvent.END_ELEMENT:
098: return eventsMatch(a.asEndElement(), b.asEndElement());
099:
100: case XMLEvent.CDATA:
101: case XMLEvent.SPACE:
102: case XMLEvent.CHARACTERS:
103: return eventsMatch(a.asCharacters(), b.asCharacters());
104:
105: case XMLEvent.COMMENT:
106: return eventsMatch((Comment) a, (Comment) b);
107:
108: case XMLEvent.ENTITY_REFERENCE:
109: return eventsMatch((EntityReference) a,
110: (EntityReference) b);
111:
112: case XMLEvent.ATTRIBUTE:
113: return eventsMatch((Attribute) a, (Attribute) b);
114:
115: case XMLEvent.NAMESPACE:
116: return eventsMatch((Namespace) a, (Namespace) b);
117:
118: case XMLEvent.START_DOCUMENT:
119: return eventsMatch((StartDocument) a, (StartDocument) b);
120:
121: case XMLEvent.END_DOCUMENT:
122: return eventsMatch((EndDocument) a, (EndDocument) b);
123:
124: case XMLEvent.PROCESSING_INSTRUCTION:
125: return eventsMatch((ProcessingInstruction) a,
126: (ProcessingInstruction) b);
127:
128: case XMLEvent.DTD:
129: return eventsMatch((DTD) a, (DTD) b);
130:
131: case XMLEvent.ENTITY_DECLARATION:
132: return eventsMatch((EntityDeclaration) a,
133: (EntityDeclaration) b);
134:
135: case XMLEvent.NOTATION_DECLARATION:
136: return eventsMatch((NotationDeclaration) a,
137: (NotationDeclaration) b);
138:
139: }
140:
141: }
142:
143: return false;
144:
145: }
146:
147: /**
148: * Compares two {@link Attribute}s, returning <code>true</code> if their names
149: * and values are the same.
150: *
151: * @param a The first event.
152: * @param b The second event.
153: * @return <code>true</code> if the events match, <code>false</code> otherwise.
154: */
155: public static boolean eventsMatch(Attribute a, Attribute b) {
156:
157: if (a == b) {
158:
159: return true;
160:
161: } else if (a == null || b == null) {
162:
163: return false;
164:
165: } else if (a.getName().equals(b.getName())) {
166:
167: return a.getValue().equals(b.getValue());
168:
169: } else {
170:
171: return false;
172:
173: }
174: }
175:
176: /**
177: * Compares two {@link Characters}s. This method will return <code>true</code>
178: * only if they have the same event type ({@link XMLEvent#CHARACTERS},
179: * {@link XMLEvent#CDATA}, or {@link XMLEvent#SPACE}), and their text content
180: * matches.
181: *
182: * @param a The first event.
183: * @param b The second event.
184: * @return <code>true</code> if the events match, <code>false</code> otherwise.
185: */
186: public static boolean eventsMatch(Characters a, Characters b) {
187:
188: if (a == b) {
189:
190: return true;
191:
192: } else if (a == null || b == null) {
193:
194: return false;
195:
196: } else if (a.getEventType() == b.getEventType()) {
197:
198: return a.getData().equals(b.getData());
199:
200: } else {
201:
202: return false;
203:
204: }
205:
206: }
207:
208: /**
209: * Compares two {@link Comment}s. This method will return <code>true</code>
210: * only if their text content matches.
211: *
212: * @param a The first event.
213: * @param b The second event.
214: * @return <code>true</code> if the events match, <code>false</code> otherwise.
215: */
216: public static boolean eventsMatch(Comment a, Comment b) {
217:
218: if (a == b) {
219:
220: return true;
221:
222: } else if (a == null || b == null) {
223:
224: return false;
225:
226: } else {
227:
228: return a.getText().equals(b.getText());
229: }
230:
231: }
232:
233: /**
234: * Compares two {@link DTD}s. This method will return <code>true</code>
235: * only if their declarations are identical.
236: *
237: * @param a The first event.
238: * @param b The second event.
239: * @return <code>true</code> if the events match, <code>false</code> otherwise.
240: */
241: public static boolean eventsMatch(DTD a, DTD b) {
242:
243: if (a == b) {
244:
245: return true;
246:
247: } else if (a == null || a == null) {
248:
249: return false;
250:
251: } else {
252:
253: // TODO determine the best way to compare DTD events
254: return a.getDocumentTypeDeclaration().equals(
255: b.getDocumentTypeDeclaration());
256:
257: }
258:
259: }
260:
261: /**
262: * Compares two {@link EndDocument}s. Because {@link EndDocument} events have no
263: * real state, two instances always match.
264: *
265: * @param a The first event.
266: * @param b The second event.
267: * @return <code>true</code> if the events match, <code>false</code> otherwise.
268: */
269: public static boolean eventsMatch(EndDocument a, EndDocument b) {
270:
271: return (a != null && b != null);
272:
273: }
274:
275: /**
276: * Compares two {@link EndElement}s. This method will return <code>true</code>
277: * only if their names match.
278: *
279: * @param a The first event.
280: * @param b The second event.
281: * @return <code>true</code> if the events match, <code>false</code> otherwise.
282: */
283: public static boolean eventsMatch(EndElement a, EndElement b) {
284:
285: if (a == b) {
286:
287: return true;
288:
289: } else if (a == null || b == null) {
290:
291: return false;
292:
293: } else {
294:
295: return a.getName().equals(b.getName());
296:
297: }
298:
299: }
300:
301: /**
302: * Compares two {@link EntityDeclaration}s. This method will return
303: * <code>true</code> only if the two events' names, replacement text, public IDs,
304: * system IDs, and notations are the same.
305: *
306: * @param a The first event.
307: * @param b The second event.
308: * @return <code>true</code> if the events match, <code>false</code> otherwise.
309: */
310: public static boolean eventsMatch(EntityDeclaration a,
311: EntityDeclaration b) {
312:
313: if (a == b) {
314:
315: return true;
316:
317: } else if (a == null || b == null) {
318:
319: return false;
320:
321: }
322:
323: // compare names
324: if (!a.getName().equals(b.getName())) {
325:
326: return false;
327:
328: }
329:
330: // compare base uris
331: String baseURI = a.getBaseURI();
332: if (!(baseURI == null ? b.getBaseURI() == null : baseURI
333: .equals(b.getBaseURI()))) {
334:
335: return false;
336:
337: }
338:
339: // compare replacement text
340: String text = a.getReplacementText();
341: if (!(text == null ? b.getReplacementText() == null : text
342: .equals(b.getReplacementText()))) {
343:
344: return false;
345:
346: }
347:
348: // compare public Ids
349: String publicId = a.getPublicId();
350: if (!(publicId == null ? b.getPublicId() == null : publicId
351: .equals(b.getPublicId()))) {
352:
353: return false;
354:
355: }
356:
357: // compare system ids
358: String systemId = a.getSystemId();
359: if (!(systemId == null ? b.getSystemId() == null : systemId
360: .equals(b.getSystemId()))) {
361:
362: return false;
363:
364: }
365:
366: // compare notations
367: String ndata = a.getNotationName();
368: if (!(ndata == null ? b.getNotationName() == null : ndata
369: .equals(b.getNotationName()))) {
370:
371: return false;
372:
373: }
374:
375: return true;
376:
377: }
378:
379: /**
380: * Compares two {@link EntityReference}s. This method will return
381: * <code>true</code> only if the two references have the same name, and their
382: * declarations also match.
383: *
384: * @param a The first event.
385: * @param b The second event.
386: * @return <code>true</code> if the events match, <code>false</code> otherwise.
387: */
388: public static boolean eventsMatch(EntityReference a,
389: EntityReference b) {
390:
391: if (a == b) {
392:
393: return true;
394:
395: } else if (a == null || b == null) {
396:
397: return false;
398:
399: }
400:
401: if (a.getName().equals(b.getName())) {
402:
403: return eventsMatch(a.getDeclaration(), b.getDeclaration());
404:
405: } else {
406:
407: return false;
408:
409: }
410:
411: }
412:
413: /**
414: * Compares two {@link Namespace}s. This method will return <code>true</code>
415: * only if the two namespaces have identical prefixes and namespace URIs.
416: *
417: * @param a The first event.
418: * @param b The second event.
419: * @return <code>true</code> if the events match, <code>false</code> otherwise.
420: */
421: public static boolean eventsMatch(Namespace a, Namespace b) {
422:
423: if (a == b) {
424:
425: return true;
426:
427: } else if (a == null || b == null) {
428:
429: return false;
430:
431: }
432:
433: return (a.getPrefix().equals(b.getPrefix()) && a
434: .getNamespaceURI().equals(b.getNamespaceURI()));
435:
436: }
437:
438: /**
439: * Compares two {@link NotationDeclaration}s. This method will return
440: * <code>true</code> only if the two namespaces have identical names, public IDs,
441: * and system IDs.
442: *
443: * @param a The first event.
444: * @param b The second event.
445: * @return <code>true</code> if the events match, <code>false</code> otherwise.
446: */
447: public static boolean eventsMatch(NotationDeclaration a,
448: NotationDeclaration b) {
449:
450: if (a == b) {
451:
452: return true;
453:
454: } else if (a == null || b == null) {
455:
456: return false;
457:
458: }
459:
460: // compare names
461: if (!a.getName().equals(b.getName())) {
462:
463: return false;
464:
465: }
466:
467: // compare public Ids
468: String publicId = a.getPublicId();
469: if (!(publicId == null ? b.getPublicId() == null : publicId
470: .equals(b.getPublicId()))) {
471:
472: return false;
473:
474: }
475:
476: // compare system ids
477: String systemId = a.getSystemId();
478: if (!(systemId == null ? b.getSystemId() == null : systemId
479: .equals(b.getSystemId()))) {
480:
481: return false;
482:
483: }
484:
485: return true;
486:
487: }
488:
489: /**
490: * Compares two {@link ProcessingInstruction}s. This method will return
491: * <code>true</code> only if the two events have identical targets and data.
492: *
493: * @param a The first event.
494: * @param b The second event.
495: * @return <code>true</code> if the events match, <code>false</code> otherwise.
496: */
497: public static boolean eventsMatch(ProcessingInstruction a,
498: ProcessingInstruction b) {
499:
500: if (a == b) {
501:
502: return true;
503:
504: } else if (a == null || b == null) {
505:
506: return false;
507:
508: }
509:
510: return (a.getTarget().equals(b.getTarget()) && a.getData()
511: .equals(b.getData()));
512:
513: }
514:
515: /**
516: * Compares two {@link StartDocument}s. This method will return
517: * <code>true</code> only if the two events have identical encodings, versions,
518: * and have the same standalone setting.
519: *
520: * @param a The first event.
521: * @param b The second event.
522: * @return <code>true</code> if the events match, <code>false</code> otherwise.
523: */
524: public static boolean eventsMatch(StartDocument a, StartDocument b) {
525:
526: if (a == b) {
527:
528: return true;
529:
530: } else if (a == null || b == null) {
531:
532: return false;
533:
534: }
535:
536: if (!a.getCharacterEncodingScheme().equals(
537: b.getCharacterEncodingScheme())) {
538:
539: return false;
540:
541: } else if (a.isStandalone() != b.isStandalone()) {
542:
543: return false;
544:
545: } else if (!a.getVersion().equals(b.getVersion())) {
546:
547: return false;
548:
549: } else {
550:
551: // TODO match the two system ID fields?
552: return true;
553:
554: }
555:
556: }
557:
558: /**
559: * Compares two {@link StartElement}s. This method will return
560: * <code>true</code> only if the two events have identical names, attributes, and
561: * namespaces.
562: *
563: * @param a The first event.
564: * @param b The second event.
565: * @return <code>true</code> if the events match, <code>false</code> otherwise.
566: */
567: public static boolean eventsMatch(StartElement a, StartElement b) {
568:
569: if (a == b) {
570:
571: return true;
572:
573: } else if (a == null || b == null) {
574:
575: return false;
576:
577: }
578:
579: if (!a.getName().equals(b.getName())) {
580:
581: return false;
582:
583: } else if (!matchAttributes(a.getAttributes(), b
584: .getAttributes())) {
585:
586: return false;
587:
588: } else if (!matchNamespaces(a.getNamespaces(), b
589: .getNamespaces())) {
590:
591: return false;
592:
593: } else {
594:
595: return true;
596:
597: }
598:
599: }
600:
601: /**
602: * Iterates over two sets of {@link Attribute}s and determines if both contain
603: * matching attributes.
604: *
605: * @param a The first set of {@link Attribute}s.
606: * @param b The second set of {@link Attribute}s.
607: * @return <code>true</code> if the two iterators iterate over matching
608: * attributes, <code>false</code> otherwise.
609: */
610: public static boolean matchAttributes(Iterator a, Iterator b) {
611:
612: // TODO Update attribute matching to allow attributes to appear in different
613: // order
614: if (a == b) {
615:
616: return true;
617:
618: } else if (a == null || b == null) {
619:
620: return false;
621:
622: }
623:
624: while (a.hasNext() && b.hasNext()) {
625:
626: Attribute A = (Attribute) a.next();
627: Attribute B = (Attribute) b.next();
628:
629: if (!eventsMatch(A, B)) {
630:
631: return false;
632:
633: }
634:
635: }
636:
637: return a.hasNext() == b.hasNext();
638:
639: }
640:
641: /**
642: * Iterates over two sets of {@link Namespace}s and determines if both contain
643: * matching namespaces.
644: *
645: * @param a The first set of {@link Namespace}s.
646: * @param b The second set of {@link Namespace}s.
647: * @return <code>true</code> if the two iterators iterate over matching
648: * namespaces, <code>false</code> otherwise.
649: */
650: public static boolean matchNamespaces(Iterator a, Iterator b) {
651:
652: // TODO Update namespace matching to allow attributes to appear in different
653: // order
654: if (a == b) {
655:
656: return true;
657:
658: } else if (a == null || b == null) {
659:
660: return false;
661:
662: }
663:
664: while (a.hasNext() && b.hasNext()) {
665:
666: Namespace A = (Namespace) a.next();
667: Namespace B = (Namespace) b.next();
668:
669: if (!eventsMatch(A, B)) {
670:
671: return false;
672:
673: }
674:
675: }
676:
677: return a.hasNext() == b.hasNext();
678:
679: }
680:
681: }
|