1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.internal.xml;
20
21 import javax.xml.stream.XMLStreamException;
22 import javax.xml.stream.XMLStreamReader;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.Reader;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31
32 import com.ctc.wstx.stax.WstxInputFactory;
33 import org.apache.maven.api.xml.XmlNode;
34 import org.codehaus.plexus.util.xml.pull.MXParser;
35 import org.codehaus.plexus.util.xml.pull.XmlPullParser;
36 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
37
38
39
40
41
42 public class XmlNodeBuilder {
43 private static final boolean DEFAULT_TRIM = true;
44
45 public static XmlNodeImpl build(Reader reader) throws XmlPullParserException, IOException {
46 return build(reader, (InputLocationBuilder) null);
47 }
48
49
50
51
52
53
54
55
56
57 public static XmlNodeImpl build(Reader reader, InputLocationBuilder locationBuilder)
58 throws XmlPullParserException, IOException {
59 return build(reader, DEFAULT_TRIM, locationBuilder);
60 }
61
62 public static XmlNodeImpl build(InputStream is, String encoding) throws XmlPullParserException, IOException {
63 return build(is, encoding, DEFAULT_TRIM);
64 }
65
66 public static XmlNodeImpl build(InputStream is, String encoding, boolean trim)
67 throws XmlPullParserException, IOException {
68 XmlPullParser parser = new MXParser();
69 parser.setInput(is, encoding);
70 return build(parser, trim);
71 }
72
73 public static XmlNodeImpl build(Reader reader, boolean trim) throws XmlPullParserException, IOException {
74 return build(reader, trim, null);
75 }
76
77
78
79
80
81
82
83
84
85
86 public static XmlNodeImpl build(Reader reader, boolean trim, InputLocationBuilder locationBuilder)
87 throws XmlPullParserException, IOException {
88 XmlPullParser parser = new MXParser();
89 parser.setInput(reader);
90 return build(parser, trim, locationBuilder);
91 }
92
93 public static XmlNodeImpl build(XmlPullParser parser) throws XmlPullParserException, IOException {
94 return build(parser, DEFAULT_TRIM);
95 }
96
97 public static XmlNodeImpl build(XmlPullParser parser, boolean trim) throws XmlPullParserException, IOException {
98 return build(parser, trim, null);
99 }
100
101
102
103
104
105
106
107
108
109
110 public static XmlNodeImpl build(XmlPullParser parser, boolean trim, InputLocationBuilder locationBuilder)
111 throws XmlPullParserException, IOException {
112 boolean spacePreserve = false;
113 String name = null;
114 String value = null;
115 Object location = null;
116 Map<String, String> attrs = null;
117 List<XmlNode> children = null;
118 int eventType = parser.getEventType();
119 boolean emptyTag = false;
120 while (eventType != XmlPullParser.END_DOCUMENT) {
121 if (eventType == XmlPullParser.START_TAG) {
122 emptyTag = parser.isEmptyElementTag();
123 if (name == null) {
124 name = parser.getName();
125 location = locationBuilder != null ? locationBuilder.toInputLocation(parser) : null;
126 int attributesSize = parser.getAttributeCount();
127 if (attributesSize > 0) {
128 attrs = new HashMap<>();
129 for (int i = 0; i < attributesSize; i++) {
130 String aname = parser.getAttributeName(i);
131 String avalue = parser.getAttributeValue(i);
132 attrs.put(aname, avalue);
133 spacePreserve = spacePreserve || ("xml:space".equals(aname) && "preserve".equals(avalue));
134 }
135 }
136 } else {
137 if (children == null) {
138 children = new ArrayList<>();
139 }
140 XmlNode child = build(parser, trim, locationBuilder);
141 children.add(child);
142 }
143 } else if (eventType == XmlPullParser.TEXT) {
144 String text = parser.getText();
145 if (trim && !spacePreserve) {
146 text = text.trim();
147 }
148 value = value != null ? value + text : text;
149 } else if (eventType == XmlPullParser.END_TAG) {
150 return new XmlNodeImpl(
151 name,
152 children == null ? (value != null ? value : emptyTag ? null : "") : null,
153 attrs,
154 children,
155 location);
156 }
157 eventType = parser.next();
158 }
159 throw new IllegalStateException("End of document found before returning to 0 depth");
160 }
161
162 public static XmlNodeImpl build(Reader reader, InputLocationBuilderStax locationBuilder) throws XMLStreamException {
163 XMLStreamReader parser = WstxInputFactory.newFactory().createXMLStreamReader(reader);
164 return build(parser, DEFAULT_TRIM, locationBuilder);
165 }
166
167 public static XmlNodeImpl build(XMLStreamReader parser) throws XMLStreamException {
168 return build(parser, DEFAULT_TRIM, null);
169 }
170
171 public static XmlNodeImpl build(XMLStreamReader parser, InputLocationBuilderStax locationBuilder)
172 throws XMLStreamException {
173 return build(parser, DEFAULT_TRIM, locationBuilder);
174 }
175
176 public static XmlNodeImpl build(XMLStreamReader parser, boolean trim, InputLocationBuilderStax locationBuilder)
177 throws XMLStreamException {
178 boolean spacePreserve = false;
179 String name = null;
180 String value = null;
181 Object location = null;
182 Map<String, String> attrs = null;
183 List<XmlNode> children = null;
184 int eventType = parser.getEventType();
185 int lastStartTag = -1;
186 while (eventType != XMLStreamReader.END_DOCUMENT) {
187 if (eventType == XMLStreamReader.START_ELEMENT) {
188 lastStartTag = parser.getLocation().getLineNumber() * 1000
189 + parser.getLocation().getColumnNumber();
190 if (name == null) {
191 int namespacesSize = parser.getNamespaceCount();
192 name = parser.getLocalName();
193 String pfx = parser.getPrefix();
194 if (pfx != null && !pfx.isEmpty()) {
195 name = pfx + ":" + name;
196 }
197 location = locationBuilder != null ? locationBuilder.toInputLocation(parser) : null;
198 int attributesSize = parser.getAttributeCount();
199 if (attributesSize > 0 || namespacesSize > 0) {
200 attrs = new HashMap<>();
201 for (int i = 0; i < namespacesSize; i++) {
202 String prefix = parser.getNamespacePrefix(i);
203 String namespace = parser.getNamespaceURI(i);
204 attrs.put("xmlns:" + prefix, namespace);
205 }
206 for (int i = 0; i < attributesSize; i++) {
207 String aname = parser.getAttributeLocalName(i);
208 String avalue = parser.getAttributeValue(i);
209 String apfx = parser.getAttributePrefix(i);
210 if (apfx != null && !apfx.isEmpty()) {
211 aname = apfx + ":" + aname;
212 }
213 attrs.put(aname, avalue);
214 spacePreserve = spacePreserve || ("xml:space".equals(aname) && "preserve".equals(avalue));
215 }
216 }
217 } else {
218 if (children == null) {
219 children = new ArrayList<>();
220 }
221 XmlNode child = build(parser, trim, locationBuilder);
222 children.add(child);
223 }
224 } else if (eventType == XMLStreamReader.CHARACTERS || eventType == XMLStreamReader.CDATA) {
225 String text = parser.getText();
226 value = value != null ? value + text : text;
227 } else if (eventType == XMLStreamReader.END_ELEMENT) {
228 boolean emptyTag = lastStartTag
229 == parser.getLocation().getLineNumber() * 1000
230 + parser.getLocation().getColumnNumber();
231 if (value != null && trim && !spacePreserve) {
232 value = value.trim();
233 }
234 return new XmlNodeImpl(
235 name,
236 children == null ? (value != null ? value : emptyTag ? null : "") : null,
237 attrs,
238 children,
239 location);
240 }
241 eventType = parser.next();
242 }
243 throw new IllegalStateException("End of document found before returning to 0 depth");
244 }
245
246
247
248
249
250
251 public interface InputLocationBuilder {
252 Object toInputLocation(XmlPullParser parser);
253 }
254
255 public interface InputLocationBuilderStax {
256 Object toInputLocation(XMLStreamReader parser);
257 }
258 }