001    // Copyright 2008 by Basil Vandegriend.  All rights reserved.
002    
003    package com.basilv.examples.jaxb;
004    
005    import java.io.*;
006    import java.net.URL;
007    import java.util.*;
008    
009    import javax.xml.bind.*;
010    
011    import org.w3c.dom.Node;
012    
013    /**
014     * Tools for working with the JAXB (XML Binding) library.
015     */
016    public class XmlBindingTools
017    {
018      /**
019       * Parse the XML supplied by the reader into the
020       * corresponding tree of Java objects.
021       * 
022       * @param reader Cannot be null. The source of the XML.
023       * @param rootElementClass Cannot be null. The type of the
024       *        root element.
025       * @return the Java object that is the root of the tree,
026       *         of type rootElement.
027       * @throws JAXBException if an error occurs parsing the
028       *         XML.
029       */
030      @SuppressWarnings("unchecked")
031      public static <E extends Object> E parseXML(
032        Reader reader, Class<E> rootElementClass)
033        throws JAXBException {
034    
035        if (rootElementClass == null) 
036          throw new IllegalArgumentException("rootElementClass is null");
037        if (reader == null) 
038          throw new IllegalArgumentException("reader is null");
039    
040        JAXBContext context = JAXBContext.newInstance(rootElementClass);
041        Unmarshaller unmarshaller = context.createUnmarshaller();
042    
043        CollectingValidationEventHandler handler = 
044          new CollectingValidationEventHandler();
045        unmarshaller.setEventHandler(handler);
046    
047        E object = (E) unmarshaller.unmarshal(reader);
048        if (!handler.getMessages().isEmpty()) {
049          String errorMessage = "XML parse errors:";
050          for (String message : handler.getMessages()) {
051            errorMessage += "\n" + message;
052          }
053          throw new JAXBException(errorMessage);
054        }
055    
056        return object;
057      }
058    
059      /**
060       * Generate XML using the supplied root element as the
061       * root of the object tree and write the resulting XML to
062       * the specified writer
063       * 
064       * @param rootElement Cannot be null.
065       * @param writer Cannot be null.
066       * @throws JAXBException
067       */
068      public static void generateXML(Object rootElement,
069        Writer writer) throws JAXBException {
070    
071        if (rootElement == null) 
072          throw new IllegalArgumentException("rootElement is null");
073        if (writer == null) 
074          throw new IllegalArgumentException("writer is null");
075    
076        JAXBContext context = JAXBContext.newInstance(rootElement.getClass());
077        Marshaller marshaller = context.createMarshaller();
078        marshaller.setProperty(
079          Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
080        marshaller.marshal(rootElement, writer);
081      }
082    
083      private static class CollectingValidationEventHandler 
084        implements ValidationEventHandler
085      {
086        private List<String> messages = new ArrayList<String>();
087    
088        public List<String> getMessages() {
089          return messages;
090        }
091        
092        public boolean handleEvent(ValidationEvent event) {
093          if (event == null) 
094            throw new IllegalArgumentException("event is null");
095    
096          // calculate the severity prefix and return value
097          String severity = null;
098          boolean continueParsing = false;
099          switch (event.getSeverity()) {
100            case ValidationEvent.WARNING:
101              severity = "Warning";
102              continueParsing = true; // continue after warnings
103              break;
104            case ValidationEvent.ERROR:
105              severity = "Error";
106              continueParsing = true; // terminate after errors
107              break;
108            case ValidationEvent.FATAL_ERROR:
109              severity = "Fatal error";
110              continueParsing = false; // terminate after fatal errors
111              break;
112            default:
113              assert false : "Unknown severity.";
114          }
115    
116          String location = getLocationDescription(event);
117          String message = severity + " parsing " + location 
118            + " due to " + event.getMessage(); 
119          messages.add(message);
120          
121          return continueParsing;
122        }
123    
124        private String getLocationDescription(ValidationEvent event) {
125          ValidationEventLocator locator = event.getLocator();
126          if (locator == null) {
127            return "XML with location unavailable";
128          }
129    
130          StringBuffer msg = new StringBuffer();
131          URL url = locator.getURL();
132          Object obj = locator.getObject();
133          Node node = locator.getNode();
134          int line = locator.getLineNumber();
135    
136          if (url != null || line != -1) {
137            msg.append("line " + line);
138            if (url != null) msg.append(" of " + url);
139          } else if (obj != null) {
140            msg.append(" obj: " + obj.toString());
141          } else if (node != null) {
142            msg.append(" node: " + node.toString());
143          }
144    
145          return msg.toString();
146        }    
147      }  
148    }