1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.model.transform.pull;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.Reader;
24 import java.util.ArrayDeque;
25 import java.util.Deque;
26 import java.util.Objects;
27 import java.util.regex.Pattern;
28
29 import org.codehaus.plexus.util.xml.pull.XmlPullParser;
30 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
31
32
33
34
35
36
37
38 public class BufferingParser implements XmlPullParser {
39
40 private static final Pattern WHITESPACE_REGEX = Pattern.compile("[ \r\t\n]+");
41
42 protected XmlPullParser xmlPullParser;
43 protected Deque<Event> events;
44 protected Event current;
45 protected boolean bypass;
46
47 @SuppressWarnings("checkstyle:VisibilityModifier")
48 public static class Event {
49 public int event;
50 public String name;
51 public String prefix;
52 public String namespace;
53 public boolean empty;
54 public String text;
55 public Attribute[] attributes;
56 public Namespace[] namespaces;
57 }
58
59 @SuppressWarnings("checkstyle:VisibilityModifier")
60 public static class Namespace {
61 public String prefix;
62 public String uri;
63 }
64
65 @SuppressWarnings("checkstyle:VisibilityModifier")
66 public static class Attribute {
67 public String name;
68 public String prefix;
69 public String namespace;
70 public String type;
71 public String value;
72 public boolean isDefault;
73 }
74
75 public BufferingParser(XmlPullParser xmlPullParser) {
76 this.xmlPullParser = xmlPullParser;
77 }
78
79 @Override
80 public void setFeature(String name, boolean state) throws XmlPullParserException {
81 xmlPullParser.setFeature(name, state);
82 }
83
84 @Override
85 public boolean getFeature(String name) {
86 return xmlPullParser.getFeature(name);
87 }
88
89 @Override
90 public void setProperty(String name, Object value) throws XmlPullParserException {
91 xmlPullParser.setProperty(name, value);
92 }
93
94 @Override
95 public Object getProperty(String name) {
96 return xmlPullParser.getProperty(name);
97 }
98
99 @Override
100 public void setInput(Reader in) throws XmlPullParserException {
101 xmlPullParser.setInput(in);
102 }
103
104 @Override
105 public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {
106 xmlPullParser.setInput(inputStream, inputEncoding);
107 }
108
109 @Override
110 public String getInputEncoding() {
111 return xmlPullParser.getInputEncoding();
112 }
113
114 @Override
115 public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
116 xmlPullParser.defineEntityReplacementText(entityName, replacementText);
117 }
118
119 @Override
120 public int getNamespaceCount(int depth) throws XmlPullParserException {
121
122 return xmlPullParser.getNamespaceCount(depth);
123 }
124
125 @Override
126 public String getNamespacePrefix(int pos) throws XmlPullParserException {
127
128 return xmlPullParser.getNamespacePrefix(pos);
129 }
130
131 @Override
132 public String getNamespaceUri(int pos) throws XmlPullParserException {
133
134 return xmlPullParser.getNamespaceUri(pos);
135 }
136
137 @Override
138 public String getNamespace(String prefix) {
139
140 return xmlPullParser.getNamespace(prefix);
141 }
142
143 @Override
144 public int getDepth() {
145
146 return xmlPullParser.getDepth();
147 }
148
149 @Override
150 public String getPositionDescription() {
151 if (current != null) {
152 throw new IllegalStateException("Not supported during events replay");
153 }
154 return xmlPullParser.getPositionDescription();
155 }
156
157 @Override
158 public int getLineNumber() {
159 if (current != null) {
160 throw new IllegalStateException("Not supported during events replay");
161 }
162 return xmlPullParser.getLineNumber();
163 }
164
165 @Override
166 public int getColumnNumber() {
167 if (current != null) {
168 throw new IllegalStateException("Not supported during events replay");
169 }
170 return xmlPullParser.getColumnNumber();
171 }
172
173 @Override
174 public boolean isWhitespace() throws XmlPullParserException {
175 if (current != null) {
176 if (current.event == TEXT || current.event == CDSECT) {
177 return WHITESPACE_REGEX.matcher(current.text).matches();
178 } else if (current.event == IGNORABLE_WHITESPACE) {
179 return true;
180 } else {
181 throw new XmlPullParserException("no content available to check for whitespaces");
182 }
183 }
184 return xmlPullParser.isWhitespace();
185 }
186
187 @Override
188 public String getText() {
189 return current != null ? current.text : xmlPullParser.getText();
190 }
191
192 @Override
193 public char[] getTextCharacters(int[] holderForStartAndLength) {
194 if (current != null) {
195 throw new IllegalStateException("Not supported during events replay");
196 }
197 return xmlPullParser.getTextCharacters(holderForStartAndLength);
198 }
199
200 @Override
201 public String getNamespace() {
202 return current != null ? current.namespace : xmlPullParser.getNamespace();
203 }
204
205 @Override
206 public String getName() {
207 return current != null ? current.name : xmlPullParser.getName();
208 }
209
210 @Override
211 public String getPrefix() {
212 return current != null ? current.prefix : xmlPullParser.getPrefix();
213 }
214
215 @Override
216 public boolean isEmptyElementTag() throws XmlPullParserException {
217 return current != null ? current.empty : xmlPullParser.isEmptyElementTag();
218 }
219
220 @Override
221 public int getAttributeCount() {
222 if (current != null) {
223 return current.attributes != null ? current.attributes.length : 0;
224 } else {
225 return xmlPullParser.getAttributeCount();
226 }
227 }
228
229 @Override
230 public String getAttributeNamespace(int index) {
231 if (current != null) {
232 return current.attributes[index].namespace;
233 } else {
234 return xmlPullParser.getAttributeNamespace(index);
235 }
236 }
237
238 @Override
239 public String getAttributeName(int index) {
240 if (current != null) {
241 return current.attributes[index].name;
242 } else {
243 return xmlPullParser.getAttributeName(index);
244 }
245 }
246
247 @Override
248 public String getAttributePrefix(int index) {
249 if (current != null) {
250 return current.attributes[index].prefix;
251 } else {
252 return xmlPullParser.getAttributePrefix(index);
253 }
254 }
255
256 @Override
257 public String getAttributeType(int index) {
258 if (current != null) {
259 return current.attributes[index].type;
260 } else {
261 return xmlPullParser.getAttributeType(index);
262 }
263 }
264
265 @Override
266 public boolean isAttributeDefault(int index) {
267 if (current != null) {
268 return current.attributes[index].isDefault;
269 } else {
270 return xmlPullParser.isAttributeDefault(index);
271 }
272 }
273
274 @Override
275 public String getAttributeValue(int index) {
276 if (current != null) {
277 return current.attributes[index].value;
278 } else {
279 return xmlPullParser.getAttributeValue(index);
280 }
281 }
282
283 @Override
284 public String getAttributeValue(String namespace, String name) {
285 if (current != null) {
286 if (current.attributes != null) {
287 for (Attribute attr : current.attributes) {
288 if (Objects.equals(namespace, attr.namespace) && Objects.equals(name, attr.name)) {
289 return attr.value;
290 }
291 }
292 }
293 return null;
294 } else {
295 return xmlPullParser.getAttributeValue(namespace, name);
296 }
297 }
298
299 @Override
300 public void require(int type, String namespace, String name) throws XmlPullParserException, IOException {
301 if (current != null) {
302 throw new IllegalStateException("Not supported during events replay");
303 }
304 xmlPullParser.require(type, namespace, name);
305 }
306
307 @Override
308 public int getEventType() throws XmlPullParserException {
309 return current != null ? current.event : xmlPullParser.getEventType();
310 }
311
312 @Override
313 public int next() throws XmlPullParserException, IOException {
314 while (true) {
315 if (events != null && !events.isEmpty()) {
316 current = events.removeFirst();
317 return current.event;
318 } else {
319 current = null;
320 }
321 if (getEventType() == END_DOCUMENT) {
322 throw new XmlPullParserException("already reached end of XML input", this, null);
323 }
324 int currentEvent = xmlPullParser.next();
325 if (bypass() || accept()) {
326 return currentEvent;
327 }
328 }
329 }
330
331 @Override
332 public int nextToken() throws XmlPullParserException, IOException {
333 while (true) {
334 if (events != null && !events.isEmpty()) {
335 current = events.removeFirst();
336 return current.event;
337 } else {
338 current = null;
339 }
340 if (getEventType() == END_DOCUMENT) {
341 throw new XmlPullParserException("already reached end of XML input", this, null);
342 }
343 int currentEvent = xmlPullParser.nextToken();
344 if (bypass() || accept()) {
345 return currentEvent;
346 }
347 }
348 }
349
350 @Override
351 public int nextTag() throws XmlPullParserException, IOException {
352 int eventType = next();
353 if (eventType == TEXT && isWhitespace()) {
354 eventType = next();
355 }
356 if (eventType != START_TAG && eventType != END_TAG) {
357 throw new XmlPullParserException("expected START_TAG or END_TAG not " + TYPES[getEventType()], this, null);
358 }
359 return eventType;
360 }
361
362 @Override
363 public String nextText() throws XmlPullParserException, IOException {
364 int eventType = getEventType();
365 if (eventType != START_TAG) {
366 throw new XmlPullParserException("parser must be on START_TAG to read next text", this, null);
367 }
368 eventType = next();
369 if (eventType == TEXT) {
370 final String result = getText();
371 eventType = next();
372 if (eventType != END_TAG) {
373 throw new XmlPullParserException(
374 "TEXT must be immediately followed by END_TAG and not " + TYPES[getEventType()], this, null);
375 }
376 return result;
377 } else if (eventType == END_TAG) {
378 return "";
379 } else {
380 throw new XmlPullParserException("parser must be on START_TAG or TEXT to read text", this, null);
381 }
382 }
383
384 protected Event bufferEvent() throws XmlPullParserException {
385 Event event = new Event();
386 XmlPullParser pp = xmlPullParser;
387 event.event = xmlPullParser.getEventType();
388 switch (event.event) {
389 case START_DOCUMENT:
390 case END_DOCUMENT:
391 break;
392 case START_TAG:
393 event.name = pp.getName();
394 event.namespace = pp.getNamespace();
395 event.prefix = pp.getPrefix();
396 event.empty = pp.isEmptyElementTag();
397 event.text = pp.getText();
398 break;
399 case END_TAG:
400 event.name = pp.getName();
401 event.namespace = pp.getNamespace();
402 event.prefix = pp.getPrefix();
403 event.text = pp.getText();
404 break;
405 case TEXT:
406 case COMMENT:
407 case IGNORABLE_WHITESPACE:
408 event.text = pp.getText();
409 break;
410 default:
411 break;
412 }
413 return event;
414 }
415
416 protected void pushEvent(Event event) {
417 if (events == null) {
418 events = new ArrayDeque<>();
419 }
420 events.add(event);
421 }
422
423 protected boolean accept() throws XmlPullParserException, IOException {
424 return true;
425 }
426
427 public void bypass(boolean bypass) {
428 if (bypass && events != null && !events.isEmpty()) {
429 throw new IllegalStateException("Can not disable filter while processing");
430 }
431 this.bypass = bypass;
432 }
433
434 public boolean bypass() {
435 return bypass || (xmlPullParser instanceof BufferingParser && ((BufferingParser) xmlPullParser).bypass());
436 }
437
438 protected static String nullSafeAppend(String originalValue, String charSegment) {
439 if (originalValue == null) {
440 return charSegment;
441 } else {
442 return originalValue + charSegment;
443 }
444 }
445 }