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