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