View Javadoc
1   /*
2    * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
3    * Copyright (C) 2009  Mickael Guessant
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  package davmail.exchange;
20  
21  import org.apache.log4j.Logger;
22  
23  import javax.xml.stream.XMLInputFactory;
24  import javax.xml.stream.XMLStreamConstants;
25  import javax.xml.stream.XMLStreamException;
26  import javax.xml.stream.XMLStreamReader;
27  import java.io.ByteArrayInputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.StringReader;
31  import java.util.HashMap;
32  import java.util.Map;
33  
34  /**
35   * XmlStreamReader utility methods
36   */
37  public final class XMLStreamUtil {
38      private static final Logger LOGGER = Logger.getLogger(XMLStreamUtil.class);
39  
40      private XMLStreamUtil() {
41      }
42  
43      /**
44       * Build a new XMLInputFactory.
45       *
46       * @return XML input factory
47       */
48      public static XMLInputFactory getXmlInputFactory() {
49          XMLInputFactory inputFactory = XMLInputFactory.newInstance();
50          inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
51          inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
52          inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
53          inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
54          // Woodstox 5.2.0 or later
55          if (inputFactory.isPropertySupported("com.ctc.wstx.allowXml11EscapedCharsInXml10")) {
56              inputFactory.setProperty("com.ctc.wstx.allowXml11EscapedCharsInXml10", Boolean.TRUE);
57          }
58          return inputFactory;
59      }
60  
61      /**
62       * Convert the XML stream to a map of entries.
63       * An entry is also a key/value map
64       *
65       * @param inputStream xml input stream
66       * @param rowName     xml tag name of entries
67       * @param idName      xml tag name of entry attribute used as key in the main map
68       * @return map of entries
69       * @throws IOException on error
70       */
71      public static Map<String, Map<String, String>> getElementContentsAsMap(InputStream inputStream, String rowName, String idName) throws IOException {
72          Map<String, Map<String, String>> results = new HashMap<>();
73          Map<String, String> item = null;
74          String currentElement = null;
75          XMLStreamReader reader = null;
76          try {
77              XMLInputFactory inputFactory = getXmlInputFactory();
78              reader = inputFactory.createXMLStreamReader(inputStream);
79              while (reader.hasNext()) {
80                  int event = reader.next();
81                  if (event == XMLStreamConstants.START_ELEMENT && rowName.equals(reader.getLocalName())) {
82                      item = new HashMap<>();
83                  } else if (event == XMLStreamConstants.END_ELEMENT && rowName.equals(reader.getLocalName())) {
84                      if (item != null && item.containsKey(idName)) {
85                          results.put(item.get(idName).toLowerCase(), item);
86                      }
87                      item = null;
88                  } else if (event == XMLStreamConstants.START_ELEMENT && item != null) {
89                      currentElement = reader.getLocalName();
90                  } else if (event == XMLStreamConstants.CHARACTERS && currentElement != null) {
91                      String text = reader.getText();
92                      if (item != null) {
93                          item.put(currentElement, text);
94                      }
95                      currentElement = null;
96                  }
97              }
98          } catch (XMLStreamException e) {
99              throw new IOException(e.getMessage());
100         } finally {
101             try {
102                 if (reader != null) {
103                     reader.close();
104                 }
105             } catch (XMLStreamException e) {
106                 ExchangeSession.LOGGER.error(e);
107             }
108         }
109         return results;
110     }
111 
112     /**
113      * Test if reader is on a start tag named tagLocalName.
114      *
115      * @param reader       xml stream reader
116      * @param tagLocalName tag local name
117      * @return true if reader is on a start tag named tagLocalName
118      */
119     public static boolean isStartTag(XMLStreamReader reader, String tagLocalName) {
120         return (reader.getEventType() == XMLStreamConstants.START_ELEMENT) && (reader.getLocalName().equals(tagLocalName));
121     }
122 
123     /**
124      * Test if reader is on a start tag.
125      *
126      * @param reader xml stream reader
127      * @return true if reader is on a start tag
128      */
129     public static boolean isStartTag(XMLStreamReader reader) {
130         return (reader.getEventType() == XMLStreamConstants.START_ELEMENT);
131     }
132 
133     /**
134      * Test if reader is on an end tag named tagLocalName.
135      *
136      * @param reader       xml stream reader
137      * @param tagLocalName tag local name
138      * @return true if reader is on an end tag named tagLocalName
139      */
140     public static boolean isEndTag(XMLStreamReader reader, String tagLocalName) {
141         return (reader.getEventType() == XMLStreamConstants.END_ELEMENT) && (reader.getLocalName().equals(tagLocalName));
142     }
143 
144     /**
145      * Create XML stream reader for byte array
146      *
147      * @param xmlContent xml content as byte array
148      * @return XML stream reader
149      * @throws XMLStreamException on error
150      */
151     public static XMLStreamReader createXMLStreamReader(byte[] xmlContent) throws XMLStreamException {
152         return createXMLStreamReader(new ByteArrayInputStream(xmlContent));
153     }
154 
155     /**
156      * Create XML stream reader for string
157      *
158      * @param xmlContent xml content as string
159      * @return XML stream reader
160      * @throws XMLStreamException on error
161      */
162     public static XMLStreamReader createXMLStreamReader(String xmlContent) throws XMLStreamException {
163         XMLInputFactory xmlInputFactory = XMLStreamUtil.getXmlInputFactory();
164         return xmlInputFactory.createXMLStreamReader(new StringReader(xmlContent));
165     }
166 
167     /**
168      * Create XML stream reader for inputStream
169      *
170      * @param inputStream xml content inputStream
171      * @return XML stream reader
172      * @throws XMLStreamException on error
173      */
174     public static XMLStreamReader createXMLStreamReader(InputStream inputStream) throws XMLStreamException {
175         XMLInputFactory xmlInputFactory = XMLStreamUtil.getXmlInputFactory();
176         return xmlInputFactory.createXMLStreamReader(inputStream);
177     }
178 
179     /**
180      * Get element text.
181      *
182      * @param reader stream reader
183      * @return element text
184      */
185     public static String getElementText(XMLStreamReader reader) {
186         String value = null;
187         try {
188             value = reader.getElementText();
189         } catch (XMLStreamException | RuntimeException e) {
190             // RuntimeException: probably com.ctc.wstx.exc.WstxLazyException on invalid character sequence
191             LOGGER.warn(e.getMessage());
192         }
193 
194         return value;
195     }
196 
197 }