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