]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/java/org/opensrf/util/XMLFlattener.java
adding a generic xml flattening utility with test
[OpenSRF.git] / src / java / org / opensrf / util / XMLFlattener.java
1 package org.opensrf.util;
2
3 import javax.xml.stream.*;
4 import javax.xml.stream.events.* ;
5 import javax.xml.namespace.QName;
6 import java.util.Map;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.ArrayList;
10 import java.util.ListIterator;
11 import java.io.InputStream;
12 import org.opensrf.util.JSONWriter;
13 import org.opensrf.util.JSONReader;
14
15 import com.ctc.wstx.stax.WstxInputFactory;
16
17 /**
18  * Flattens an XML file into a properties map.  Values are stored as JSON strings or arrays.
19  * An array is created if more than one value resides at the same key.
20  * e.g. html.head.script = "alert('hello');"
21  */
22 public class XMLFlattener {
23
24     /** Flattened properties map */
25     private Map<String, String> props;
26     /** Incoming XML stream */
27     private InputStream inStream;
28     /** Runtime list of encountered elements */
29     private List<String> elementList;
30
31     /**
32      * Creates a new reader. Initializes the message queue.
33      * Sets the stream state to disconnected, and the xml
34      * state to in_nothing.
35      * @param inStream the inbound XML stream
36      */
37     public XMLFlattener(InputStream inStream) {
38         props = new HashMap<String, String>();
39         this.inStream = inStream;
40         elementList = new ArrayList<String>();
41     }
42
43     /** Turns an array of strings into a dot-separated key string */
44     private String listToString() {
45         ListIterator itr = elementList.listIterator();
46         StringBuffer sb = new StringBuffer();
47         while(itr.hasNext()) {
48             sb.append(itr.next());
49             if(itr.hasNext())
50                 sb.append(".");
51         }
52         return sb.toString();
53     }
54
55     /**
56      * Parses XML data from the provided stream.
57      */
58     public Map read() throws javax.xml.stream.XMLStreamException {
59
60         //XMLInputFactory factory = XMLInputFactory.newInstance();
61         XMLInputFactory factory = new com.ctc.wstx.stax.WstxInputFactory();
62
63         /** disable as many unused features as possible to speed up the parsing */
64         factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
65         factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
66         factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
67         factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
68         factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
69
70         /** create the stream reader */
71         XMLStreamReader reader = factory.createXMLStreamReader(inStream);
72         int eventType;
73
74         while(reader.hasNext()) {
75             /** cycle through the XML events */
76
77             eventType = reader.next();
78             if(reader.isWhiteSpace()) continue;
79
80             switch(eventType) {
81
82                 case XMLEvent.START_ELEMENT:
83                     elementList.add(reader.getName().toString());
84                     break;
85
86                 case XMLEvent.CHARACTERS:
87                     String text = reader.getText();
88                     String key = listToString();
89
90                     if(props.containsKey(key)) {
91
92                         /* something in the map already has this key */
93
94                         Object o = null;
95                         try {
96                             o = new JSONReader(props.get(key)).read();
97                         } catch(org.opensrf.util.JSONException e){}
98
99                         if(o instanceof List) {
100                             /* if the map contains a list, append to the list and re-encode */
101                             ((List) o).add(text);
102
103                         } else {
104                             /* if the map just contains a string, start building a new list
105                              * with the old string and append the new string */
106                             List<String> arr = new ArrayList<String>();
107                             arr.add((String) o);
108                             arr.add(text);
109                             o = arr;
110                         }
111
112                         props.put(key, new JSONWriter(o).write());
113
114                     } else {
115                         props.put(key, new JSONWriter(text).write());
116                     }
117                     break;
118
119                 case XMLEvent.END_ELEMENT: 
120                     elementList.remove(elementList.size()-1);
121                     break;
122             }
123         }
124
125         return props;
126     }
127 }
128
129
130
131