View Javadoc
1   package ejava.projects.edmv.xml;
2   
3   import java.io.InputStream;
4   
5   import java.util.HashMap;
6   import java.util.concurrent.Callable;
7   
8   import javax.xml.XMLConstants;
9   import javax.xml.bind.JAXBContext;
10  import javax.xml.bind.JAXBElement;
11  import javax.xml.bind.JAXBException;
12  import javax.xml.bind.Unmarshaller;
13  import javax.xml.stream.XMLInputFactory;
14  import javax.xml.stream.XMLStreamException;
15  import javax.xml.stream.XMLStreamReader;
16  import javax.xml.transform.stream.StreamSource;
17  import javax.xml.validation.Schema;
18  import javax.xml.validation.SchemaFactory;
19  
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  import org.xml.sax.SAXException;
23  
24  import com.sun.xml.bind.IDResolver;
25  
26  /**
27   * This class will read in Java objects from a specified XML file. These 
28   * objects can be used to create ingest data for projects.
29   *
30   */
31  public class EDmvParser {
32      @SuppressWarnings("unused")
33      private Logger log = LoggerFactory.getLogger(EDmvParser.class);
34      protected XMLInputFactory xmlif;
35      protected Unmarshaller um;
36      protected XMLStreamReader xmlr;
37     
38      /**
39       * Pass in the JAXB class that represents the root node of the document
40       * and an InputStream for the document to parse.
41       * 
42       * @param rootType - the class of the root type
43       * @param is - am input stream with document to parse
44       * @throws JAXBException
45       * @throws XMLStreamException
46       */
47      public EDmvParser(Class<?> rootType, InputStream is) 
48          throws JAXBException, XMLStreamException {
49          JAXBContext jaxbContext = JAXBContext.newInstance(rootType);
50          um = jaxbContext.createUnmarshaller();
51          xmlif = XMLInputFactory.newInstance();
52          xmlr = xmlif.createXMLStreamReader(is);
53  
54          //This (anonymous) class is a near replicate of sun's DefaultIDResolver
55          //except that they added a clear() of the idmap within startDocument()
56          //that prevents the unmarshaller from being called multiple times.
57          IDResolver idResolver = new IDResolver() {
58              private HashMap<String,Object> idmap = null;
59  
60  			@Override
61  			public Callable<?> resolve(final String id, 
62  					@SuppressWarnings("rawtypes") Class targetType) 
63  					throws SAXException {
64                  return new Callable<Object>() {
65                      public Object call() throws Exception {
66                          if(idmap==null)     return null;
67                          return idmap.get(id);
68                      }
69                  };
70  			}
71  			
72  			@Override
73  			public void bind(String id, Object obj) throws SAXException {
74                  if(idmap==null)     idmap = new HashMap<String,Object>();
75                  idmap.put(id,obj);
76  			};
77  		};
78          um.setProperty(IDResolver.class.getName(), idResolver);
79      }
80      public EDmvParser(Class<?> rootTypes[], InputStream is) 
81          throws JAXBException, XMLStreamException {
82          JAXBContext jaxbContext = JAXBContext.newInstance(rootTypes);
83          um = jaxbContext.createUnmarshaller();
84          xmlif = XMLInputFactory.newInstance();
85          xmlr = xmlif.createXMLStreamReader(is);
86      }
87      
88      public void setSchema(InputStream schema) throws SAXException {
89          SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
90          Schema schemaObject = sf.newSchema(new StreamSource(schema));
91          um.setSchema(schemaObject);
92      }
93      
94      private boolean contains(String elements[], String localName) {
95          for(String element: elements) {
96              if (element.equalsIgnoreCase(localName)) {
97                  return true;
98              }
99          }
100         return false;
101     }
102     
103     /**
104      * This method will return either the object or null if we hit the end
105      * of stream before getting another instance. Note that only the local-name
106      * is being used. That won't work to great when two namespaces declare 
107      * a common local-name. Should be easily fixable when needed.
108      * 
109      * @param elements
110      * @return
111      * @throws XMLStreamException
112      * @throws JAXBException
113      */
114     @SuppressWarnings("rawtypes")
115 	public Object getObject(String...elements) 
116         throws XMLStreamException, JAXBException {
117         xmlr.next();
118         while (xmlr.hasNext()) {
119             if (xmlr.isStartElement() && 
120                     contains(elements, xmlr.getName().getLocalPart())) {
121                 Object object = um.unmarshal(xmlr);
122                 return (object instanceof JAXBElement) ?
123                     ((JAXBElement)object).getValue() : object;
124             }
125             xmlr.next();
126         }
127         return null;        
128     }    
129 }