001// Copyright 2008 by Basil Vandegriend.  All rights reserved.
002
003package com.basilv.examples.jaxb;
004
005import java.io.*;
006import java.net.URL;
007import java.util.*;
008
009import javax.xml.bind.*;
010
011import org.w3c.dom.Node;
012
013/**
014 * Tools for working with the JAXB (XML Binding) library.
015 */
016public 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}