1package com.android.hotspot2.omadm;
2
3import java.io.IOException;
4import java.io.InputStream;
5import java.io.OutputStream;
6import java.util.Collection;
7import java.util.HashMap;
8import java.util.Iterator;
9import java.util.LinkedList;
10import java.util.List;
11import java.util.Map;
12
13public abstract class OMANode {
14    private final OMAConstructed mParent;
15    private final String mName;
16    private final String mContext;
17    private final Map<String, String> mAttributes;
18
19    protected OMANode(OMAConstructed parent, String name, String context, Map<String, String> avps) {
20        mParent = parent;
21        mName = name;
22        mContext = context;
23        mAttributes = avps;
24    }
25
26    protected static Map<String, String> buildAttributes(String[] avps) {
27        if (avps == null) {
28            return null;
29        }
30        Map<String, String> attributes = new HashMap<>();
31        for (int n = 0; n < avps.length; n += 2) {
32            attributes.put(avps[n], avps[n + 1]);
33        }
34        return attributes;
35    }
36
37    protected Map<String, String> getAttributes() {
38        return mAttributes;
39    }
40
41    public OMAConstructed getParent() {
42        return mParent;
43    }
44
45    public String getName() {
46        return mName;
47    }
48
49    public String getContext() {
50        return mContext;
51    }
52
53    public List<String> getPath() {
54        LinkedList<String> path = new LinkedList<>();
55        for (OMANode node = this; node != null; node = node.getParent()) {
56            path.addFirst(node.getName());
57        }
58        return path;
59    }
60
61    public String getPathString() {
62        StringBuilder sb = new StringBuilder();
63        for (String element : getPath()) {
64            sb.append('/').append(element);
65        }
66        return sb.toString();
67    }
68
69    public abstract OMANode reparent(OMAConstructed parent);
70
71    public abstract String getScalarValue(Iterator<String> path) throws OMAException;
72
73    public abstract OMANode getListValue(Iterator<String> path) throws OMAException;
74
75    public abstract boolean isLeaf();
76
77    public abstract Collection<OMANode> getChildren();
78
79    public abstract OMANode getChild(String name) throws OMAException;
80
81    public abstract String getValue();
82
83    public abstract OMANode addChild(String name, String context, String value, String path)
84            throws IOException;
85
86    public abstract void marshal(OutputStream out, int level) throws IOException;
87
88    public abstract void toString(StringBuilder sb, int level);
89
90    public abstract void fillPayload(StringBuilder sb);
91
92    public void toXml(StringBuilder sb) {
93        sb.append('<').append(MOTree.NodeTag);
94        if (mAttributes != null && !mAttributes.isEmpty()) {
95            for (Map.Entry<String, String> avp : mAttributes.entrySet()) {
96                sb.append(' ').append(avp.getKey()).append("=\"").append(avp.getValue()).append('"');
97            }
98        }
99        sb.append(">\n");
100
101        sb.append('<').append(MOTree.NodeNameTag).append('>');
102        sb.append(getName());
103        sb.append("</").append(MOTree.NodeNameTag).append(">\n");
104
105        fillPayload(sb);
106
107        sb.append("</").append(MOTree.NodeTag).append(">\n");
108    }
109
110    @Override
111    public String toString() {
112        StringBuilder sb = new StringBuilder();
113        toString(sb, 0);
114        return sb.toString();
115    }
116
117    public static OMAConstructed unmarshal(InputStream in) throws IOException {
118        OMANode node = buildNode(in, null);
119        if (node == null || node.isLeaf()) {
120            throw new IOException("Bad OMA tree");
121        }
122        unmarshal(in, (OMAConstructed) node);
123        return (OMAConstructed) node;
124    }
125
126    private static void unmarshal(InputStream in, OMAConstructed parent) throws IOException {
127        for (; ; ) {
128            OMANode node = buildNode(in, parent);
129            if (node == null) {
130                return;
131            } else if (!node.isLeaf()) {
132                unmarshal(in, (OMAConstructed) node);
133            }
134        }
135    }
136
137    private static OMANode buildNode(InputStream in, OMAConstructed parent) throws IOException {
138        String name = OMAConstants.deserializeString(in);
139        if (name == null) {
140            return null;
141        }
142
143        String urn = null;
144        int next = in.read();
145        if (next == '(') {
146            urn = OMAConstants.readURN(in);
147            next = in.read();
148        }
149
150        if (next == '=') {
151            String value = OMAConstants.deserializeString(in);
152            return parent.addChild(name, urn, value, null);
153        } else if (next == '+') {
154            if (parent != null) {
155                return parent.addChild(name, urn, null, null);
156            } else {
157                return new OMAConstructed(null, name, urn);
158            }
159        } else {
160            throw new IOException("Parse error: expected = or + after node name");
161        }
162    }
163}
164