MOTree.java revision 05d2f4e6f26834a94b53187e6121379a16749088
1package com.android.server.wifi.hotspot2.omadm;
2
3import android.util.Log;
4
5import org.xml.sax.SAXException;
6
7import java.io.IOException;
8import java.io.InputStream;
9import java.io.OutputStream;
10import java.nio.charset.StandardCharsets;
11import java.util.*;
12
13public class MOTree {
14    public static final String MgmtTreeTag = "MgmtTree";
15
16    private static final String NodeTag = "Node";
17    private static final String NodeNameTag = "NodeName";
18    private static final String PathTag = "Path";
19    private static final String ValueTag = "Value";
20    private static final String RTPropTag = "RTProperties";
21    private static final String TypeTag = "Type";
22    private static final String DDFNameTag = "DDFName";
23
24    private final String mUrn;
25    private final String mDtdRev;
26    private final OMAConstructed mRoot;
27
28    public MOTree(XMLNode node, String urn) throws IOException, SAXException {
29        Iterator<XMLNode> children = node.getChildren().iterator();
30
31        String dtdRev = null;
32
33        while (children.hasNext()) {
34            XMLNode child = children.next();
35            if (child.getTag().equals(OMAConstants.SyncMLVersionTag)) {
36                dtdRev = child.getText();
37                children.remove();
38                break;
39            }
40        }
41
42        mUrn = urn;
43        mDtdRev = dtdRev;
44
45        mRoot = new OMAConstructed(null, MgmtTreeTag, null);
46
47        for (XMLNode child : node.getChildren()) {
48            buildNode(mRoot, child);
49        }
50    }
51
52    public MOTree(String urn, String rev, OMAConstructed root) {
53        mUrn = urn;
54        mDtdRev = rev;
55        mRoot = root;
56    }
57
58    private static class NodeData {
59        private final String mName;
60        private String mPath;
61        private String mValue;
62
63        private NodeData(String name) {
64            mName = name;
65        }
66
67        private void setPath(String path) {
68            mPath = path;
69        }
70
71        private void setValue(String value) {
72            mValue = value;
73        }
74
75        public String getName() {
76            return mName;
77        }
78
79        public String getPath() {
80            return mPath;
81        }
82
83        public String getValue() {
84            return mValue;
85        }
86    }
87
88    private static void buildNode(OMANode parent, XMLNode node) throws IOException {
89        if (!node.getTag().equals(NodeTag))
90            throw new IOException("Node is a '" + node.getTag() + "' instead of a 'Node'");
91
92        Map<String, XMLNode> checkMap = new HashMap<String, XMLNode>(3);
93        String context = null;
94        List<NodeData> values = new ArrayList<NodeData>();
95        List<XMLNode> children = new ArrayList<XMLNode>();
96
97        NodeData curValue = null;
98
99        for (XMLNode child : node.getChildren()) {
100            XMLNode old = checkMap.put(child.getTag(), child);
101
102            if (child.getTag().equals(NodeNameTag)) {
103                if (curValue != null)
104                    throw new IOException(NodeNameTag + " not expected");
105                curValue = new NodeData(child.getText());
106
107            } else if (child.getTag().equals(PathTag)) {
108                if (curValue == null || curValue.getPath() != null)
109                    throw new IOException(PathTag + " not expected");
110                curValue.setPath(child.getText());
111
112            } else if (child.getTag().equals(ValueTag)) {
113                if (!children.isEmpty())
114                    throw new IOException(ValueTag + " in constructed node");
115                if (curValue == null || curValue.getValue() != null)
116                    throw new IOException(ValueTag + " not expected");
117                curValue.setValue(child.getText());
118                values.add(curValue);
119                curValue = null;
120
121            } else if (child.getTag().equals(RTPropTag)) {
122                if (old != null)
123                    throw new IOException("Duplicate " + RTPropTag);
124                XMLNode typeNode = getNextNode(child, TypeTag);
125                XMLNode ddfName = getNextNode(typeNode, DDFNameTag);
126                context = ddfName.getText();
127                if (context == null)
128                    throw new IOException("No text in " + DDFNameTag);
129
130            } else if (child.getTag().equals(NodeTag)) {
131                if (!values.isEmpty())
132                    throw new IOException("Scalar node " + node.getText() + " has Node child");
133                children.add(child);
134
135            }
136        }
137
138        if (values.isEmpty()) {
139            if (curValue == null)
140                throw new IOException("Missing name");
141
142            OMANode subNode = parent.addChild(curValue.getName(),
143                    context, null, curValue.getPath());
144
145            for (XMLNode child : children) {
146                buildNode(subNode, child);
147            }
148        } else {
149            if (!children.isEmpty())
150                throw new IOException("Got both sub nodes and value(s)");
151
152            for (NodeData nodeData : values) {
153                parent.addChild(nodeData.getName(), context,
154                        nodeData.getValue(), nodeData.getPath());
155            }
156        }
157    }
158
159    private static XMLNode getNextNode(XMLNode node, String tag) throws IOException {
160        if (node == null)
161            throw new IOException("No node for " + tag);
162        if (node.getChildren().size() != 1)
163            throw new IOException("Expected " + node.getTag() + " to have exactly one child");
164        XMLNode child = node.getChildren().iterator().next();
165        if (!child.getTag().equals(tag))
166            throw new IOException("Expected " + node.getTag() + " to have child '" + tag +
167                    "' instead of '" + child.getTag() + "'");
168        return child;
169    }
170
171    public String getUrn() {
172        return mUrn;
173    }
174
175    public String getDtdRev() {
176        return mDtdRev;
177    }
178
179    public OMAConstructed getRoot() {
180        return mRoot;
181    }
182
183    @Override
184    public String toString() {
185        StringBuilder sb = new StringBuilder();
186        sb.append("MO Tree v").append(mDtdRev).append(", urn ").append(mUrn).append(")\n");
187        sb.append(mRoot);
188
189        return sb.toString();
190    }
191
192    public void marshal(OutputStream out) throws IOException {
193        out.write("tree ".getBytes(StandardCharsets.UTF_8));
194        OMAConstants.serializeString(mDtdRev, out);
195        out.write(String.format("(%s)\n", mUrn).getBytes(StandardCharsets.UTF_8));
196        mRoot.marshal(out, 0);
197    }
198
199    public static MOTree unmarshal(InputStream in) throws IOException {
200        boolean strip = true;
201        StringBuilder tree = new StringBuilder();
202        for (; ; ) {
203            int octet = in.read();
204            if (octet < 0) {
205                return null;
206            } else if (octet > ' ') {
207                tree.append((char) octet);
208                strip = false;
209            } else if (!strip) {
210                break;
211            }
212        }
213        if (!tree.toString().equals("tree")) {
214            throw new IOException("Not a tree: " + tree);
215        }
216
217        String version = OMAConstants.deserializeString(in);
218        String urn = OMAConstants.readURN(in);
219
220        OMAConstructed root = OMANode.unmarshal(in);
221
222        return new MOTree(urn, version, root);
223    }
224}
225