1package eu.siacs.conversations.xml;
2
3import android.util.Log;
4import android.util.Xml;
5
6import org.xmlpull.v1.XmlPullParser;
7import org.xmlpull.v1.XmlPullParserException;
8
9import java.io.Closeable;
10import java.io.IOException;
11import java.io.InputStream;
12import java.io.InputStreamReader;
13
14import eu.siacs.conversations.Config;
15
16public class XmlReader implements Closeable {
17 private final XmlPullParser parser;
18 private InputStream is;
19
20 public XmlReader() {
21 this.parser = Xml.newPullParser();
22 try {
23 this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
24 } catch (XmlPullParserException e) {
25 Log.d(Config.LOGTAG, "error setting namespace feature on parser");
26 }
27 }
28
29 public void setInputStream(InputStream inputStream) throws IOException {
30 if (inputStream == null) {
31 throw new IOException();
32 }
33 this.is = inputStream;
34 try {
35 parser.setInput(new InputStreamReader(this.is));
36 } catch (XmlPullParserException e) {
37 throw new IOException("error resetting parser");
38 }
39 }
40
41 public void reset() throws IOException {
42 if (this.is == null) {
43 throw new IOException();
44 }
45 try {
46 parser.setInput(new InputStreamReader(this.is));
47 } catch (XmlPullParserException e) {
48 throw new IOException("error resetting parser");
49 }
50 }
51
52 @Override
53 public void close() {
54 this.is = null;
55 }
56
57 public Tag readTag() throws IOException {
58 try {
59 while (this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) {
60 if (parser.getEventType() == XmlPullParser.START_TAG) {
61 Tag tag = Tag.start(parser.getName());
62 final String xmlns = parser.getNamespace();
63 for (int i = 0; i < parser.getAttributeCount(); ++i) {
64 final var prefix = parser.getAttributePrefix(i);
65 final var ns = parser.getAttributeNamespace(i);
66 String name;
67 if ("xml".equals(prefix)) {
68 name = "xml:" + parser.getAttributeName(i);
69 } else if (ns != null && !ns.isEmpty()) {
70 name = "{" + ns + "}" + parser.getAttributeName(i);
71 } else {
72 name = parser.getAttributeName(i);
73 }
74 tag.setAttribute(name,parser.getAttributeValue(i));
75 }
76 if (xmlns != null) {
77 tag.setAttribute("xmlns", xmlns);
78 }
79 return tag;
80 } else if (parser.getEventType() == XmlPullParser.END_TAG) {
81 return Tag.end(parser.getName());
82 } else if (parser.getEventType() == XmlPullParser.TEXT) {
83 return Tag.no(parser.getText());
84 }
85 }
86
87 } catch (Throwable throwable) {
88 throw new IOException("xml parser mishandled "+throwable.getClass().getSimpleName()+"("+throwable.getMessage()+")", throwable);
89 }
90 return null;
91 }
92
93 public Element readElement(Tag currentTag) throws IOException {
94 Element element = new Element(currentTag.getName());
95 element.setAttributes(currentTag.getAttributes());
96 Tag nextTag = this.readTag();
97 if (nextTag == null) {
98 throw new IOException("interrupted mid tag");
99 }
100 while (!nextTag.isEnd(element.getName())) {
101 if (nextTag.isNo()) {
102 if (nextTag.getName() != null) element.addChild(new TextNode(nextTag.getName()));
103 } else {
104 Element child = this.readElement(nextTag);
105 element.addChild(child);
106 }
107 nextTag = this.readTag();
108 if (nextTag == null) {
109 throw new IOException("interrupted mid tag");
110 }
111 }
112 return element;
113 }
114}