View Javadoc
1   package org.codehaus.plexus.util.xml;
2   
3   /*
4    * Copyright The Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.Reader;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.codehaus.plexus.util.IOUtil;
26  import org.codehaus.plexus.util.xml.pull.MXParser;
27  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
28  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
29  
30  /**
31   *
32   */
33  public class Xpp3DomBuilder {
34      private static final boolean DEFAULT_TRIM = true;
35  
36      public static Xpp3Dom build(Reader reader) throws XmlPullParserException, IOException {
37          return build(reader, null);
38      }
39  
40      /**
41       * @param reader the reader
42       * @param locationBuilder the builder
43       * @since 3.2.0
44       * @return DOM
45       * @throws XmlPullParserException xml exception
46       * @throws IOException io
47       */
48      public static Xpp3Dom build(Reader reader, InputLocationBuilder locationBuilder)
49              throws XmlPullParserException, IOException {
50          return build(reader, DEFAULT_TRIM, locationBuilder);
51      }
52  
53      public static Xpp3Dom build(InputStream is, String encoding) throws XmlPullParserException, IOException {
54          return build(is, encoding, DEFAULT_TRIM);
55      }
56  
57      public static Xpp3Dom build(InputStream is, String encoding, boolean trim)
58              throws XmlPullParserException, IOException {
59          try {
60              final XmlPullParser parser = new MXParser();
61              parser.setInput(is, encoding);
62  
63              final Xpp3Dom xpp3Dom = build(parser, trim);
64              is.close();
65              is = null;
66  
67              return xpp3Dom;
68          } finally {
69              IOUtil.close(is);
70          }
71      }
72  
73      public static Xpp3Dom build(Reader reader, boolean trim) throws XmlPullParserException, IOException {
74          return build(reader, trim, null);
75      }
76  
77      /**
78       * @param reader the reader
79       * @param trim to trim
80       * @param locationBuilder the builder
81       * @since 3.2.0
82       * @return DOM
83       * @throws XmlPullParserException xml exception
84       * @throws IOException io
85       */
86      public static Xpp3Dom build(Reader reader, boolean trim, InputLocationBuilder locationBuilder)
87              throws XmlPullParserException, IOException {
88          try {
89              final XmlPullParser parser = new MXParser();
90              parser.setInput(reader);
91  
92              final Xpp3Dom xpp3Dom = build(parser, trim, locationBuilder);
93              reader.close();
94              reader = null;
95  
96              return xpp3Dom;
97          } finally {
98              IOUtil.close(reader);
99          }
100     }
101 
102     public static Xpp3Dom build(XmlPullParser parser) throws XmlPullParserException, IOException {
103         return build(parser, DEFAULT_TRIM);
104     }
105 
106     public static Xpp3Dom build(XmlPullParser parser, boolean trim) throws XmlPullParserException, IOException {
107         return build(parser, trim, null);
108     }
109 
110     /**
111      * @since 3.2.0
112      * @param locationBuilder builder
113      * @param parser the parser
114      * @param trim do trim
115      * @return DOM
116      * @throws XmlPullParserException xml exception
117      * @throws IOException io
118      */
119     public static Xpp3Dom build(XmlPullParser parser, boolean trim, InputLocationBuilder locationBuilder)
120             throws XmlPullParserException, IOException {
121         List<Xpp3Dom> elements = new ArrayList<>();
122 
123         List<StringBuilder> values = new ArrayList<>();
124 
125         int eventType = parser.getEventType();
126 
127         boolean spacePreserve = false;
128 
129         while (eventType != XmlPullParser.END_DOCUMENT) {
130             if (eventType == XmlPullParser.START_TAG) {
131                 spacePreserve = false;
132 
133                 String rawName = parser.getName();
134 
135                 Xpp3Dom childConfiguration = new Xpp3Dom(rawName);
136 
137                 if (locationBuilder != null) {
138                     childConfiguration.setInputLocation(locationBuilder.toInputLocation(parser));
139                 }
140 
141                 int depth = elements.size();
142 
143                 if (depth > 0) {
144                     Xpp3Dom parent = elements.get(depth - 1);
145 
146                     parent.addChild(childConfiguration);
147                 }
148 
149                 elements.add(childConfiguration);
150 
151                 if (parser.isEmptyElementTag()) {
152                     values.add(null);
153                 } else {
154                     values.add(new StringBuilder());
155                 }
156 
157                 int attributesSize = parser.getAttributeCount();
158 
159                 for (int i = 0; i < attributesSize; i++) {
160                     String name = parser.getAttributeName(i);
161 
162                     String value = parser.getAttributeValue(i);
163 
164                     childConfiguration.setAttribute(name, value);
165 
166                     spacePreserve = spacePreserve || ("xml:space".equals(name) && "preserve".equals(value));
167                 }
168             } else if (eventType == XmlPullParser.TEXT) {
169                 int depth = values.size() - 1;
170 
171                 @SuppressWarnings("MismatchedQueryAndUpdateOfStringBuilder")
172                 StringBuilder valueBuffer = values.get(depth);
173 
174                 String text = parser.getText();
175 
176                 if (trim && !spacePreserve) {
177                     text = text.trim();
178                 }
179 
180                 valueBuffer.append(text);
181             } else if (eventType == XmlPullParser.END_TAG) {
182                 int depth = elements.size() - 1;
183 
184                 Xpp3Dom finishedConfiguration = elements.remove(depth);
185 
186                 /* this Object could be null if it is a singleton tag */
187                 Object accumulatedValue = values.remove(depth);
188 
189                 if (finishedConfiguration.getChildCount() == 0) {
190                     if (accumulatedValue == null) {
191                         finishedConfiguration.setValue(null);
192                     } else {
193                         finishedConfiguration.setValue(accumulatedValue.toString());
194                     }
195                 }
196 
197                 if (depth == 0) {
198                     return finishedConfiguration;
199                 }
200             }
201 
202             eventType = parser.next();
203         }
204 
205         throw new IllegalStateException("End of document found before returning to 0 depth");
206     }
207 
208     /**
209      * Input location builder interface, to be implemented to choose how to store data.
210      *
211      * @since 3.2.0
212      */
213     public static interface InputLocationBuilder {
214         Object toInputLocation(XmlPullParser parser);
215     }
216 }