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  
29  import org.apache.maven.api.xml.XmlNode;
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   * All methods in this class attempt to fully parse the XML.
36   * The caller is responsible for closing {@code InputStream} and {@code Reader} arguments.
37   */
38  public class XmlNodeBuilder {
39      private static final boolean DEFAULT_TRIM = true;
40  
41      public static XmlNodeImpl build(Reader reader) throws XmlPullParserException, IOException {
42          return build(reader, (InputLocationBuilder) null);
43      }
44  
45      /**
46       * @param reader the reader
47       * @param locationBuilder the builder
48       * @since 3.2.0
49       * @return DOM
50       * @throws XmlPullParserException XML well-formedness error
51       * @throws IOException I/O error reading file or stream
52       */
53      public static XmlNodeImpl build(Reader reader, InputLocationBuilder locationBuilder)
54              throws XmlPullParserException, IOException {
55          return build(reader, DEFAULT_TRIM, locationBuilder);
56      }
57  
58      public static XmlNodeImpl build(InputStream is, String encoding) throws XmlPullParserException, IOException {
59          return build(is, encoding, DEFAULT_TRIM);
60      }
61  
62      public static XmlNodeImpl build(InputStream is, String encoding, boolean trim)
63              throws XmlPullParserException, IOException {
64          XmlPullParser parser = new MXParser();
65          parser.setInput(is, encoding);
66          return build(parser, trim);
67      }
68  
69      public static XmlNodeImpl build(Reader reader, boolean trim) throws XmlPullParserException, IOException {
70          return build(reader, trim, null);
71      }
72  
73      /**
74       * @param reader the reader
75       * @param trim to trim
76       * @param locationBuilder the builder
77       * @since 3.2.0
78       * @return DOM
79       * @throws XmlPullParserException XML well-formedness error
80       * @throws IOException I/O error reading file or stream
81       */
82      public static XmlNodeImpl build(Reader reader, boolean trim, InputLocationBuilder locationBuilder)
83              throws XmlPullParserException, IOException {
84          XmlPullParser parser = new MXParser();
85          parser.setInput(reader);
86          return build(parser, trim, locationBuilder);
87      }
88  
89      public static XmlNodeImpl build(XmlPullParser parser) throws XmlPullParserException, IOException {
90          return build(parser, DEFAULT_TRIM);
91      }
92  
93      public static XmlNodeImpl build(XmlPullParser parser, boolean trim) throws XmlPullParserException, IOException {
94          return build(parser, trim, null);
95      }
96  
97      /**
98       * @since 3.2.0
99       * @param locationBuilder builder
100      * @param parser the parser
101      * @param trim do trim
102      * @return DOM
103      * @throws XmlPullParserException XML well-formedness error
104      * @throws IOException I/O error reading file or stream
105      */
106     public static XmlNodeImpl build(XmlPullParser parser, boolean trim, InputLocationBuilder locationBuilder)
107             throws XmlPullParserException, IOException {
108         boolean spacePreserve = false;
109         String name = null;
110         String value = null;
111         Object location = null;
112         Map<String, String> attrs = null;
113         List<XmlNode> children = null;
114         int eventType = parser.getEventType();
115         boolean emptyTag = false;
116         while (eventType != XmlPullParser.END_DOCUMENT) {
117             if (eventType == XmlPullParser.START_TAG) {
118                 emptyTag = parser.isEmptyElementTag();
119                 if (name == null) {
120                     name = parser.getName();
121                     location = locationBuilder != null ? locationBuilder.toInputLocation(parser) : null;
122                     int attributesSize = parser.getAttributeCount();
123                     if (attributesSize > 0) {
124                         attrs = new HashMap<>();
125                         for (int i = 0; i < attributesSize; i++) {
126                             String aname = parser.getAttributeName(i);
127                             String avalue = parser.getAttributeValue(i);
128                             attrs.put(aname, avalue);
129                             spacePreserve = spacePreserve || ("xml:space".equals(aname) && "preserve".equals(avalue));
130                         }
131                     }
132                 } else {
133                     if (children == null) {
134                         children = new ArrayList<>();
135                     }
136                     XmlNode child = build(parser, trim, locationBuilder);
137                     children.add(child);
138                 }
139             } else if (eventType == XmlPullParser.TEXT) {
140                 String text = parser.getText();
141                 if (trim && !spacePreserve) {
142                     text = text.trim();
143                 }
144                 value = value != null ? value + text : text;
145             } else if (eventType == XmlPullParser.END_TAG) {
146                 return new XmlNodeImpl(
147                         name,
148                         children == null ? (value != null ? value : emptyTag ? null : "") : null,
149                         attrs,
150                         children,
151                         location);
152             }
153             eventType = parser.next();
154         }
155         throw new IllegalStateException("End of document found before returning to 0 depth");
156     }
157 
158     /**
159      * Input location builder interface, to be implemented to choose how to store data.
160      *
161      * @since 3.2.0
162      */
163     public interface InputLocationBuilder {
164         Object toInputLocation(XmlPullParser parser);
165     }
166 }