1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 package org.osjava.jardiff;
40
41
42
43
44 import javax.xml.parsers.DocumentBuilderFactory;
45 import javax.xml.parsers.DocumentBuilder;
46 import javax.xml.parsers.ParserConfigurationException;
47 import javax.xml.transform.TransformerConfigurationException;
48 import javax.xml.transform.TransformerFactory;
49 import javax.xml.transform.Transformer;
50 import javax.xml.transform.TransformerException;
51 import javax.xml.transform.dom.DOMSource;
52 import javax.xml.transform.stream.StreamResult;
53 import javax.xml.transform.Result;
54 import org.w3c.dom.*;
55
56 import org.objectweb.asm.Type;
57
58 /***
59 * A specific type of DiffHandler which uses DOM to create an XML document
60 * describing the changes in the diff.
61 *
62 * @author <a href="mailto:antony@cyberiantiger.org">Antony Riley</a>
63 */
64 public class DOMDiffHandler implements DiffHandler
65 {
66 /***
67 * The XML namespace used.
68 */
69 public static final String XML_URI = "http://www.osjava.org/jardiff/0.1";
70
71 /***
72 * The javax.xml.transform.sax.Transformer used to convert
73 * the DOM to text.
74 */
75 private final Transformer transformer;
76
77 /***
78 * Where we write the result to.
79 */
80 private final Result result;
81
82 /***
83 * The document object we're building
84 */
85 private final Document doc;
86
87 /***
88 * The current Node.
89 */
90 private Node currentNode;
91
92 /***
93 * Create a new DOMDiffHandler which writes to System.out
94 *
95 * @throws DiffException when there is an underlying exception, e.g.
96 * writing to a file caused an IOException
97 */
98 public DOMDiffHandler() throws DiffException {
99 try {
100 TransformerFactory tf = TransformerFactory.newInstance();
101 this.transformer = tf.newTransformer();
102 this.result = new StreamResult(System.out);
103 this.currentNode = null;
104 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
105 dbf.setNamespaceAware(true);
106 DocumentBuilder db = dbf.newDocumentBuilder();
107 this.doc = db.newDocument();
108 } catch (TransformerConfigurationException tce) {
109 throw new DiffException(tce);
110 } catch (ParserConfigurationException pce) {
111 throw new DiffException(pce);
112 }
113 }
114
115 /***
116 * Create a new DOMDiffHandler with the specified Transformer and Result.
117 * This method allows the user to choose what they are going to do with
118 * the output in a flexible manner, allowing a stylesheet to be specified
119 * and some result object.
120 *
121 * @param transformer The transformer to transform the output with.
122 * @param result Where to put the result.
123 */
124 public DOMDiffHandler(Transformer transformer, Result result)
125 throws DiffException
126 {
127 try {
128 this.transformer = transformer;
129 this.result = result;
130 this.currentNode = null;
131 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
132 dbf.setNamespaceAware(true);
133 DocumentBuilder db = dbf.newDocumentBuilder();
134 this.doc = db.newDocument();
135 } catch (ParserConfigurationException pce) {
136 throw new DiffException(pce);
137 }
138 }
139
140 /***
141 * Start the diff.
142 * This writes out the start of a <diff> node.
143 *
144 * @param oldJar ignored
145 * @param newJar ignored
146 * @throws DiffException when there is an underlying exception, e.g.
147 * writing to a file caused an IOException
148 */
149 public void startDiff(String oldJar, String newJar) throws DiffException {
150 Element tmp = doc.createElementNS(XML_URI, "diff");
151 tmp.setAttribute( "old", oldJar);
152 tmp.setAttribute( "new", newJar);
153 doc.appendChild(tmp);
154 currentNode = tmp;
155 }
156
157 /***
158 * Start the list of old contents.
159 *
160 * @throws DiffException when there is an underlying exception, e.g.
161 * writing to a file caused an IOException
162 */
163 public void startOldContents() throws DiffException {
164 Element tmp = doc.createElementNS(XML_URI, "oldcontents");
165 currentNode.appendChild(tmp);
166 currentNode = tmp;
167 }
168
169 /***
170 * Start the list of old contents.
171 *
172 * @throws DiffException when there is an underlying exception, e.g.
173 * writing to a file caused an IOException
174 */
175 public void startNewContents() throws DiffException {
176 Element tmp = doc.createElementNS(XML_URI, "newcontents");
177 currentNode.appendChild(tmp);
178 currentNode = tmp;
179 }
180
181 /***
182 * Add a contained class.
183 *
184 * @param info information about a class
185 * @throws DiffException when there is an underlying exception, e.g.
186 * writing to a file caused an IOException
187 */
188 public void contains(ClassInfo info) throws DiffException {
189 Element tmp = doc.createElementNS(XML_URI, "class");
190 tmp.setAttribute("name", info.getName());
191 currentNode.appendChild(tmp);
192 }
193
194 /***
195 * End the list of old contents.
196 *
197 * @throws DiffException when there is an underlying exception, e.g.
198 * writing to a file caused an IOException
199 */
200 public void endOldContents() throws DiffException {
201 currentNode = currentNode.getParentNode();
202 }
203
204 /***
205 * End the list of new contents.
206 *
207 * @throws DiffException when there is an underlying exception, e.g.
208 * writing to a file caused an IOException
209 */
210 public void endNewContents() throws DiffException {
211 currentNode = currentNode.getParentNode();
212 }
213
214 /***
215 * Start the removed node.
216 * This writes out a <removed> node.
217 *
218 * @throws DiffException when there is an underlying exception, e.g.
219 * writing to a file caused an IOException
220 */
221 public void startRemoved() throws DiffException {
222 Element tmp = doc.createElementNS(XML_URI, "removed");
223 currentNode.appendChild(tmp);
224 currentNode = tmp;
225 }
226
227 /***
228 * Write out class info for a removed class.
229 * This writes out the nodes describing a class
230 *
231 * @param info The info to write out.
232 * @throws DiffException when there is an underlying exception, e.g.
233 * writing to a file caused an IOException
234 */
235 public void classRemoved(ClassInfo info) throws DiffException {
236 writeClassInfo(info);
237 }
238
239 /***
240 * End the removed section.
241 * This closes the <removed> tag.
242 *
243 * @throws DiffException when there is an underlying exception, e.g.
244 * writing to a file caused an IOException
245 */
246 public void endRemoved() throws DiffException {
247 currentNode = currentNode.getParentNode();
248 }
249
250 /***
251 * Start the added section.
252 * This opens the <added> tag.
253 *
254 * @throws DiffException when there is an underlying exception, e.g.
255 * writing to a file caused an IOException
256 */
257 public void startAdded() throws DiffException {
258 Element tmp = doc.createElementNS(XML_URI, "added");
259 currentNode.appendChild(tmp);
260 currentNode = tmp;
261 }
262
263 /***
264 * Write out the class info for an added class.
265 * This writes out the nodes describing an added class.
266 *
267 * @param info The class info describing the added class.
268 * @throws DiffException when there is an underlying exception, e.g.
269 * writing to a file caused an IOException
270 */
271 public void classAdded(ClassInfo info) throws DiffException {
272 writeClassInfo(info);
273 }
274
275 /***
276 * End the added section.
277 * This closes the <added> tag.
278 *
279 * @throws DiffException when there is an underlying exception, e.g.
280 * writing to a file caused an IOException
281 */
282 public void endAdded() throws DiffException {
283 currentNode = currentNode.getParentNode();
284 }
285
286 /***
287 * Start the changed section.
288 * This writes out the <changed> node.
289 *
290 * @throws DiffException when there is an underlying exception, e.g.
291 * writing to a file caused an IOException
292 */
293 public void startChanged() throws DiffException {
294 Element tmp = doc.createElementNS(XML_URI, "changed");
295 currentNode.appendChild(tmp);
296 currentNode = tmp;
297 }
298
299 /***
300 * Start a changed section for an individual class.
301 * This writes out an <classchanged> node with the real class
302 * name as the name attribute.
303 *
304 * @param internalName the internal name of the class that has changed.
305 * @throws DiffException when there is an underlying exception, e.g.
306 * writing to a file caused an IOException
307 */
308 public void startClassChanged(String internalName) throws DiffException
309 {
310 Element tmp = doc.createElementNS(XML_URI, "classchanged");
311 tmp.setAttribute( "name", internalName);
312 currentNode.appendChild(tmp);
313 currentNode = tmp;
314 }
315
316 /***
317 * Write out info about a removed field.
318 * This just writes out the field info, it will be inside a start/end
319 * removed section.
320 *
321 * @param info Info about the field that's been removed.
322 * @throws DiffException when there is an underlying exception, e.g.
323 * writing to a file caused an IOException
324 */
325 public void fieldRemoved(FieldInfo info) throws DiffException {
326 writeFieldInfo(info);
327 }
328
329 /***
330 * Write out info about a removed method.
331 * This just writes out the method info, it will be inside a start/end
332 * removed section.
333 *
334 * @param info Info about the method that's been removed.
335 * @throws DiffException when there is an underlying exception, e.g.
336 * writing to a file caused an IOException
337 */
338 public void methodRemoved(MethodInfo info) throws DiffException {
339 writeMethodInfo(info);
340 }
341
342 /***
343 * Write out info about an added field.
344 * This just writes out the field info, it will be inside a start/end
345 * added section.
346 *
347 * @param info Info about the added field.
348 * @throws DiffException when there is an underlying exception, e.g.
349 * writing to a file caused an IOException
350 */
351 public void fieldAdded(FieldInfo info) throws DiffException {
352 writeFieldInfo(info);
353 }
354
355 /***
356 * Write out info about a added method.
357 * This just writes out the method info, it will be inside a start/end
358 * added section.
359 *
360 * @param info Info about the added method.
361 * @throws DiffException when there is an underlying exception, e.g.
362 * writing to a file caused an IOException
363 */
364 public void methodAdded(MethodInfo info) throws DiffException {
365 writeMethodInfo(info);
366 }
367
368 /***
369 * Write out info aboout a changed class.
370 * This writes out a <classchange> node, followed by a
371 * <from> node, with the old information about the class
372 * followed by a <to> node with the new information about the
373 * class.
374 *
375 * @param oldInfo Info about the old class.
376 * @param newInfo Info about the new class.
377 * @throws DiffException when there is an underlying exception, e.g.
378 * writing to a file caused an IOException
379 */
380 public void classChanged(ClassInfo oldInfo, ClassInfo newInfo)
381 throws DiffException
382 {
383 Node currentNode = this.currentNode;
384 Element tmp = doc.createElementNS(XML_URI, "classchange");
385 Element from = doc.createElementNS(XML_URI, "from");
386 Element to = doc.createElementNS(XML_URI, "to");
387 tmp.appendChild(from);
388 tmp.appendChild(to);
389 currentNode.appendChild(tmp);
390 this.currentNode = from;
391 writeClassInfo(oldInfo);
392 this.currentNode = to;
393 writeClassInfo(newInfo);
394 this.currentNode = currentNode;
395 }
396
397 /***
398 * Write out info aboout a changed field.
399 * This writes out a <fieldchange> node, followed by a
400 * <from> node, with the old information about the field
401 * followed by a <to> node with the new information about the
402 * field.
403 *
404 * @param oldInfo Info about the old field.
405 * @param newInfo Info about the new field.
406 * @throws DiffException when there is an underlying exception, e.g.
407 * writing to a file caused an IOException
408 */
409 public void fieldChanged(FieldInfo oldInfo, FieldInfo newInfo)
410 throws DiffException
411 {
412 Node currentNode = this.currentNode;
413 Element tmp = doc.createElementNS(XML_URI, "fieldchange");
414 Element from = doc.createElementNS(XML_URI, "from");
415 Element to = doc.createElementNS(XML_URI, "to");
416 tmp.appendChild(from);
417 tmp.appendChild(to);
418 currentNode.appendChild(tmp);
419 this.currentNode = from;
420 writeFieldInfo(oldInfo);
421 this.currentNode = to;
422 writeFieldInfo(newInfo);
423 this.currentNode = currentNode;
424 }
425
426 /***
427 * Write out info aboout a changed method.
428 * This writes out a <methodchange> node, followed by a
429 * <from> node, with the old information about the method
430 * followed by a <to> node with the new information about the
431 * method.
432 *
433 * @param oldInfo Info about the old method.
434 * @param newInfo Info about the new method.
435 * @throws DiffException when there is an underlying exception, e.g.
436 * writing to a file caused an IOException
437 */
438 public void methodChanged(MethodInfo oldInfo, MethodInfo newInfo)
439 throws DiffException
440 {
441 Node currentNode = this.currentNode;
442 Element tmp = doc.createElementNS(XML_URI, "methodchange");
443 Element from = doc.createElementNS(XML_URI, "from");
444 Element to = doc.createElementNS(XML_URI, "to");
445 tmp.appendChild(from);
446 tmp.appendChild(to);
447 currentNode.appendChild(tmp);
448 this.currentNode = from;
449 writeMethodInfo(oldInfo);
450 this.currentNode = to;
451 writeMethodInfo(newInfo);
452 this.currentNode = currentNode;
453 }
454
455 /***
456 * End the changed section for an individual class.
457 * This closes the <classchanged> node.
458 *
459 * @throws DiffException when there is an underlying exception, e.g.
460 * writing to a file caused an IOException
461 */
462 public void endClassChanged() throws DiffException {
463 currentNode = currentNode.getParentNode();
464 }
465
466 /***
467 * End the changed section.
468 * This closes the <changed> node.
469 *
470 * @throws DiffException when there is an underlying exception, e.g.
471 * writing to a file caused an IOException
472 */
473 public void endChanged() throws DiffException {
474 currentNode = currentNode.getParentNode();
475 }
476
477 /***
478 * End the diff.
479 * This closes the <diff> node.
480 *
481 * @throws DiffException when there is an underlying exception, e.g.
482 * writing to a file caused an IOException
483 */
484 public void endDiff() throws DiffException {
485 DOMSource source = new DOMSource(doc);
486 try {
487 transformer.transform(source, result);
488 } catch (TransformerException te) {
489 throw new DiffException(te);
490 }
491 }
492
493 /***
494 * Write out information about a class.
495 * This writes out a <class> node, which contains information about
496 * what interfaces are implemented each in a <implements> node.
497 *
498 * @param info Info about the class to write out.
499 */
500 protected void writeClassInfo(ClassInfo info) {
501 Node currentNode = this.currentNode;
502 Element tmp = doc.createElementNS(XML_URI, "class");
503 currentNode.appendChild(tmp);
504 this.currentNode = tmp;
505 addAccessFlags(info);
506 if (info.getName() != null)
507 tmp.setAttribute( "name",
508 info.getName());
509 if (info.getSignature() != null)
510 tmp.setAttribute( "signature",
511 info.getSignature());
512 if (info.getSupername() != null)
513 tmp.setAttribute( "superclass",
514 info.getSupername());
515 String[] interfaces = info.getInterfaces();
516 for (int i = 0; i < interfaces.length; i++) {
517 Element iface = doc.createElementNS(XML_URI, "implements");
518 tmp.appendChild(iface);
519 iface.setAttribute( "name",
520 interfaces[i]);
521 }
522 this.currentNode = currentNode;
523 }
524
525 /***
526 * Write out information about a method.
527 * This writes out a <method> node which contains information about
528 * the arguments, the return type, and the exceptions thrown by the
529 * method.
530 *
531 * @param info Info about the method.
532 */
533 protected void writeMethodInfo(MethodInfo info) {
534 Node currentNode = this.currentNode;
535 Element tmp = doc.createElementNS(XML_URI, "method");
536 currentNode.appendChild(tmp);
537 this.currentNode = tmp;
538 addAccessFlags(info);
539
540 if (info.getName() != null)
541 tmp.setAttribute( "name", info.getName());
542 if (info.getSignature() != null)
543 tmp.setAttribute( "signature", info.getSignature());
544 if (info.getDesc() != null)
545 addMethodNodes(info.getDesc());
546 String[] exceptions = info.getExceptions();
547 if (exceptions != null) {
548 for (int i = 0; i < exceptions.length; i++) {
549 Element excep = doc.createElementNS(XML_URI, "exception");
550 excep.setAttribute( "name", exceptions[i]);
551 tmp.appendChild(excep);
552 }
553 }
554 this.currentNode = currentNode;
555 }
556
557 /***
558 * Write out information about a field.
559 * This writes out a <field> node with attributes describing the
560 * field.
561 *
562 * @param info Info about the field.
563 */
564 protected void writeFieldInfo(FieldInfo info) {
565 Node currentNode = this.currentNode;
566 Element tmp = doc.createElementNS(XML_URI, "field");
567 currentNode.appendChild(tmp);
568 this.currentNode = tmp;
569 addAccessFlags(info);
570
571 if (info.getName() != null)
572 tmp.setAttribute( "name",
573 info.getName());
574 if (info.getSignature() != null)
575 tmp.setAttribute( "signature",
576 info.getSignature());
577 if (info.getValue() != null)
578 tmp.setAttribute( "value",
579 info.getValue().toString());
580 if (info.getDesc() != null)
581 addTypeNode(info.getDesc());
582 this.currentNode = currentNode;
583 }
584
585 /***
586 * Add attributes describing some access flags.
587 * This adds the attributes to the attr field.
588 *
589 * @see #attr
590 * @param info Info describing the access flags.
591 */
592 protected void addAccessFlags(AbstractInfo info) {
593 Element currentNode = (Element) this.currentNode;
594 currentNode.setAttribute( "access", info.getAccessType());
595 if (info.isAbstract())
596 currentNode.setAttribute( "abstract", "yes");
597 if (info.isAnnotation())
598 currentNode.setAttribute( "annotation", "yes");
599 if (info.isBridge())
600 currentNode.setAttribute( "bridge", "yes");
601 if (info.isDeprecated())
602 currentNode.setAttribute( "deprecated", "yes");
603 if (info.isEnum())
604 currentNode.setAttribute( "enum", "yes");
605 if (info.isFinal())
606 currentNode.setAttribute( "final", "yes");
607 if (info.isInterface())
608 currentNode.setAttribute( "interface", "yes");
609 if (info.isNative())
610 currentNode.setAttribute( "native", "yes");
611 if (info.isStatic())
612 currentNode.setAttribute( "static", "yes");
613 if (info.isStrict())
614 currentNode.setAttribute( "strict", "yes");
615 if (info.isSuper())
616 currentNode.setAttribute( "super", "yes");
617 if (info.isSynchronized())
618 currentNode.setAttribute( "synchronized", "yes");
619 if (info.isSynthetic())
620 currentNode.setAttribute( "synthetic", "yes");
621 if (info.isTransient())
622 currentNode.setAttribute( "transient", "yes");
623 if (info.isVarargs())
624 currentNode.setAttribute( "varargs", "yes");
625 if (info.isVolatile())
626 currentNode.setAttribute( "volatile", "yes");
627 }
628
629 /***
630 * Add the method nodes for the method descriptor.
631 * This writes out an <arguments> node containing the
632 * argument types for the method, followed by a <return> node
633 * containing the return type.
634 *
635 * @param desc The descriptor for the method to write out.
636 */
637 protected void addMethodNodes(String desc) {
638 Type[] args = Type.getArgumentTypes(desc);
639 Type ret = Type.getReturnType(desc);
640 Node currentNode = this.currentNode;
641 Element tmp = doc.createElementNS(XML_URI,"arguments");
642 currentNode.appendChild(tmp);
643 this.currentNode = tmp;
644 for (int i = 0; i < args.length; i++)
645 addTypeNode(args[i]);
646 tmp = doc.createElementNS(XML_URI,"return");
647 currentNode.appendChild(tmp);
648 this.currentNode = tmp;
649 addTypeNode(ret);
650 this.currentNode = currentNode;
651 }
652
653 /***
654 * Add a type node for the specified descriptor.
655 *
656 * @param desc A type descriptor.
657 */
658 protected void addTypeNode(String desc) {
659 addTypeNode(Type.getType(desc));
660 }
661
662 /***
663 * Add a type node for the specified type.
664 * This writes out a <type> node with attributes describing
665 * the type.
666 *
667 * @param type The type to describe.
668 */
669 protected void addTypeNode(Type type) {
670 Element tmp = doc.createElementNS(XML_URI, "type");
671 currentNode.appendChild(tmp);
672 int i = type.getSort();
673 if (i == Type.ARRAY) {
674 tmp.setAttribute( "array", "yes");
675 tmp.setAttribute( "dimensions",
676 "" + type.getDimensions());
677 type = type.getElementType();
678 i = type.getSort();
679 }
680 switch (i) {
681 case Type.BOOLEAN:
682 tmp.setAttribute( "primitive", "yes");
683 tmp.setAttribute( "name", "boolean");
684 break;
685 case Type.BYTE:
686 tmp.setAttribute( "primitive", "yes");
687 tmp.setAttribute( "name", "byte");
688 break;
689 case Type.CHAR:
690 tmp.setAttribute( "primitive", "yes");
691 tmp.setAttribute( "name", "char");
692 break;
693 case Type.DOUBLE:
694 tmp.setAttribute( "primitive", "yes");
695 tmp.setAttribute( "name", "double");
696 break;
697 case Type.FLOAT:
698 tmp.setAttribute( "primitive", "yes");
699 tmp.setAttribute( "name", "float");
700 break;
701 case Type.INT:
702 tmp.setAttribute( "primitive", "yes");
703 tmp.setAttribute( "name", "int");
704 break;
705 case Type.LONG:
706 tmp.setAttribute( "primitive", "yes");
707 tmp.setAttribute( "name", "long");
708 break;
709 case Type.OBJECT:
710 tmp.setAttribute( "name", type.getInternalName());
711 break;
712 case Type.SHORT:
713 tmp.setAttribute( "primitive", "yes");
714 tmp.setAttribute( "name", "short");
715 break;
716 case Type.VOID:
717 tmp.setAttribute( "primitive", "yes");
718 tmp.setAttribute( "name", "void");
719 break;
720 }
721 }
722 }