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 String prefix = parser.getAttributePrefix(i);
65 String name;
66 if (prefix != null && !prefix.isEmpty()) {
67 name = prefix+":"+parser.getAttributeName(i);
68 } else {
69 name = parser.getAttributeName(i);
70 }
71 tag.setAttribute(name,parser.getAttributeValue(i));
72 }
73 int nsStart = parser.getNamespaceCount(parser.getDepth()-1);
74 int nsEnd = parser.getNamespaceCount(parser.getDepth());
75 for (int i = nsStart; i < nsEnd; i++) {
76 final var prefix = parser.getNamespacePrefix(i);
77 tag.setAttribute("xmlns" + (prefix == null ? "" : ":" + prefix), parser.getNamespaceUri(i));
78 }
79 if (xmlns != null) {
80 tag.setAttribute("xmlns", xmlns);
81 }
82 return tag;
83 } else if (parser.getEventType() == XmlPullParser.END_TAG) {
84 return Tag.end(parser.getName());
85 } else if (parser.getEventType() == XmlPullParser.TEXT) {
86 return Tag.no(parser.getText());
87 }
88 }
89
90 } catch (Throwable throwable) {
91 throw new IOException("xml parser mishandled "+throwable.getClass().getSimpleName()+"("+throwable.getMessage()+")", throwable);
92 }
93 return null;
94 }
95
96 public Element readElement(Tag currentTag) throws IOException {
97 Element element = new Element(currentTag.getName());
98 element.setAttributes(currentTag.getAttributes());
99 Tag nextTag = this.readTag();
100 if (nextTag == null) {
101 throw new IOException("interrupted mid tag");
102 }
103 while (!nextTag.isEnd(element.getName())) {
104 if (nextTag.isNo()) {
105 if (nextTag.getName() != null) element.addChild(new TextNode(nextTag.getName()));
106 } else {
107 Element child = this.readElement(nextTag);
108 element.addChild(child);
109 }
110 nextTag = this.readTag();
111 if (nextTag == null) {
112 throw new IOException("interrupted mid tag");
113 }
114 }
115 return element;
116 }
117}