View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.xml;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.Reader;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import org.apache.maven.api.xml.Dom;
29  import org.codehaus.plexus.util.IOUtil;
30  import org.codehaus.plexus.util.xml.pull.MXParser;
31  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
32  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
33  
34  /**
35   *
36   */
37  public class Xpp3DomBuilder {
38      private static final boolean DEFAULT_TRIM = true;
39  
40      public static Xpp3Dom build(Reader reader) throws XmlPullParserException, IOException {
41          return build(reader, null);
42      }
43  
44      /**
45       * @param reader the reader
46       * @param locationBuilder the builder
47       * @since 3.2.0
48       * @return DOM
49       * @throws XmlPullParserException xml exception
50       * @throws IOException io
51       */
52      public static Xpp3Dom build(Reader reader, InputLocationBuilder locationBuilder)
53              throws XmlPullParserException, IOException {
54          return build(reader, DEFAULT_TRIM, locationBuilder);
55      }
56  
57      public static Xpp3Dom build(InputStream is, String encoding) throws XmlPullParserException, IOException {
58          return build(is, encoding, DEFAULT_TRIM);
59      }
60  
61      public static Xpp3Dom build(InputStream is, String encoding, boolean trim)
62              throws XmlPullParserException, IOException {
63          try {
64              final XmlPullParser parser = new MXParser();
65              parser.setInput(is, encoding);
66  
67              final Xpp3Dom xpp3Dom = build(parser, trim);
68              is.close();
69              is = null;
70  
71              return xpp3Dom;
72          } finally {
73              IOUtil.close(is);
74          }
75      }
76  
77      public static Xpp3Dom build(Reader reader, boolean trim) throws XmlPullParserException, IOException {
78          return build(reader, trim, null);
79      }
80  
81      /**
82       * @param reader the reader
83       * @param trim to trim
84       * @param locationBuilder the builder
85       * @since 3.2.0
86       * @return DOM
87       * @throws XmlPullParserException xml exception
88       * @throws IOException io
89       */
90      public static Xpp3Dom build(Reader reader, boolean trim, InputLocationBuilder locationBuilder)
91              throws XmlPullParserException, IOException {
92          try {
93              final XmlPullParser parser = new MXParser();
94              parser.setInput(reader);
95  
96              final Xpp3Dom xpp3Dom = build(parser, trim, locationBuilder);
97              reader.close();
98              reader = null;
99  
100             return xpp3Dom;
101         } finally {
102             IOUtil.close(reader);
103         }
104     }
105 
106     public static Xpp3Dom build(XmlPullParser parser) throws XmlPullParserException, IOException {
107         return build(parser, DEFAULT_TRIM);
108     }
109 
110     public static Xpp3Dom build(XmlPullParser parser, boolean trim) throws XmlPullParserException, IOException {
111         return build(parser, trim, null);
112     }
113 
114     /**
115      * @since 3.2.0
116      * @param locationBuilder builder
117      * @param parser the parser
118      * @param trim do trim
119      * @return DOM
120      * @throws XmlPullParserException xml exception
121      * @throws IOException io
122      */
123     public static Xpp3Dom build(XmlPullParser parser, boolean trim, InputLocationBuilder locationBuilder)
124             throws XmlPullParserException, IOException {
125         boolean spacePreserve = false;
126         String name = null;
127         String value = null;
128         Object location = null;
129         Map<String, String> attrs = null;
130         List<Dom> children = null;
131         int eventType = parser.getEventType();
132         boolean emptyTag = false;
133         while (eventType != XmlPullParser.END_DOCUMENT) {
134             if (eventType == XmlPullParser.START_TAG) {
135                 emptyTag = parser.isEmptyElementTag();
136                 if (name == null) {
137                     name = parser.getName();
138                     location = locationBuilder != null ? locationBuilder.toInputLocation(parser) : null;
139                     int attributesSize = parser.getAttributeCount();
140                     if (attributesSize > 0) {
141                         attrs = new HashMap<>();
142                         for (int i = 0; i < attributesSize; i++) {
143                             String aname = parser.getAttributeName(i);
144                             String avalue = parser.getAttributeValue(i);
145                             attrs.put(aname, avalue);
146                             spacePreserve = spacePreserve || ("xml:space".equals(aname) && "preserve".equals(avalue));
147                         }
148                     }
149                 } else {
150                     if (children == null) {
151                         children = new ArrayList<>();
152                     }
153                     Dom child = build(parser, trim, locationBuilder);
154                     children.add(child);
155                 }
156             } else if (eventType == XmlPullParser.TEXT) {
157                 String text = parser.getText();
158                 if (trim && !spacePreserve) {
159                     text = text.trim();
160                 }
161                 value = value != null ? value + text : text;
162             } else if (eventType == XmlPullParser.END_TAG) {
163                 return new Xpp3Dom(
164                         name,
165                         children == null ? (value != null ? value : emptyTag ? null : "") : null,
166                         attrs,
167                         children,
168                         location);
169             }
170             eventType = parser.next();
171         }
172         throw new IllegalStateException("End of document found before returning to 0 depth");
173     }
174 
175     /**
176      * Input location builder interface, to be implemented to choose how to store data.
177      *
178      * @since 3.2.0
179      */
180     public interface InputLocationBuilder {
181         Object toInputLocation(XmlPullParser parser);
182     }
183 }