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.IOUtil;
31  import org.codehaus.plexus.util.xml.pull.MXParser;
32  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
33  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
34  
35  /**
36   *
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, 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 exception
51       * @throws IOException io
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          try {
65              final XmlPullParser parser = new MXParser();
66              parser.setInput(is, encoding);
67  
68              final XmlNodeImpl node = build(parser, trim);
69              is.close();
70              is = null;
71  
72              return node;
73          } finally {
74              IOUtil.close(is);
75          }
76      }
77  
78      public static XmlNodeImpl build(Reader reader, boolean trim) throws XmlPullParserException, IOException {
79          return build(reader, trim, null);
80      }
81  
82      /**
83       * @param reader the reader
84       * @param trim to trim
85       * @param locationBuilder the builder
86       * @since 3.2.0
87       * @return DOM
88       * @throws XmlPullParserException xml exception
89       * @throws IOException io
90       */
91      public static XmlNodeImpl build(Reader reader, boolean trim, InputLocationBuilder locationBuilder)
92              throws XmlPullParserException, IOException {
93          try {
94              final XmlPullParser parser = new MXParser();
95              parser.setInput(reader);
96  
97              final XmlNodeImpl node = build(parser, trim, locationBuilder);
98              reader.close();
99              reader = null;
100 
101             return node;
102         } finally {
103             IOUtil.close(reader);
104         }
105     }
106 
107     public static XmlNodeImpl build(XmlPullParser parser) throws XmlPullParserException, IOException {
108         return build(parser, DEFAULT_TRIM);
109     }
110 
111     public static XmlNodeImpl build(XmlPullParser parser, boolean trim) throws XmlPullParserException, IOException {
112         return build(parser, trim, null);
113     }
114 
115     /**
116      * @since 3.2.0
117      * @param locationBuilder builder
118      * @param parser the parser
119      * @param trim do trim
120      * @return DOM
121      * @throws XmlPullParserException xml exception
122      * @throws IOException io
123      */
124     public static XmlNodeImpl build(XmlPullParser parser, boolean trim, InputLocationBuilder locationBuilder)
125             throws XmlPullParserException, IOException {
126         boolean spacePreserve = false;
127         String name = null;
128         String value = null;
129         Object location = null;
130         Map<String, String> attrs = null;
131         List<XmlNode> children = null;
132         int eventType = parser.getEventType();
133         boolean emptyTag = false;
134         while (eventType != XmlPullParser.END_DOCUMENT) {
135             if (eventType == XmlPullParser.START_TAG) {
136                 emptyTag = parser.isEmptyElementTag();
137                 if (name == null) {
138                     name = parser.getName();
139                     location = locationBuilder != null ? locationBuilder.toInputLocation(parser) : null;
140                     int attributesSize = parser.getAttributeCount();
141                     if (attributesSize > 0) {
142                         attrs = new HashMap<>();
143                         for (int i = 0; i < attributesSize; i++) {
144                             String aname = parser.getAttributeName(i);
145                             String avalue = parser.getAttributeValue(i);
146                             attrs.put(aname, avalue);
147                             spacePreserve = spacePreserve || ("xml:space".equals(aname) && "preserve".equals(avalue));
148                         }
149                     }
150                 } else {
151                     if (children == null) {
152                         children = new ArrayList<>();
153                     }
154                     XmlNode child = build(parser, trim, locationBuilder);
155                     children.add(child);
156                 }
157             } else if (eventType == XmlPullParser.TEXT) {
158                 String text = parser.getText();
159                 if (trim && !spacePreserve) {
160                     text = text.trim();
161                 }
162                 value = value != null ? value + text : text;
163             } else if (eventType == XmlPullParser.END_TAG) {
164                 return new XmlNodeImpl(
165                         name,
166                         children == null ? (value != null ? value : emptyTag ? null : "") : null,
167                         attrs,
168                         children,
169                         location);
170             }
171             eventType = parser.next();
172         }
173         throw new IllegalStateException("End of document found before returning to 0 depth");
174     }
175 
176     /**
177      * Input location builder interface, to be implemented to choose how to store data.
178      *
179      * @since 3.2.0
180      */
181     public interface InputLocationBuilder {
182         Object toInputLocation(XmlPullParser parser);
183     }
184 }