| package com.android.hotspot2.omadm; |
| |
| import org.xml.sax.SAXException; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| public class MOTree { |
| public static final String MgmtTreeTag = "MgmtTree"; |
| |
| public static final String NodeTag = "Node"; |
| public static final String NodeNameTag = "NodeName"; |
| public static final String PathTag = "Path"; |
| public static final String ValueTag = "Value"; |
| public static final String RTPropTag = "RTProperties"; |
| public static final String TypeTag = "Type"; |
| public static final String DDFNameTag = "DDFName"; |
| |
| private final String mUrn; |
| private final String mDtdRev; |
| private final OMAConstructed mRoot; |
| |
| public MOTree(XMLNode node, String urn) throws IOException, SAXException { |
| Iterator<XMLNode> children = node.getChildren().iterator(); |
| |
| String dtdRev = null; |
| |
| while (children.hasNext()) { |
| XMLNode child = children.next(); |
| if (child.getTag().equals(OMAConstants.SyncMLVersionTag)) { |
| dtdRev = child.getText(); |
| children.remove(); |
| break; |
| } |
| } |
| |
| mUrn = urn; |
| mDtdRev = dtdRev; |
| |
| mRoot = new MgmtTreeRoot(node, dtdRev); |
| |
| for (XMLNode child : node.getChildren()) { |
| buildNode(mRoot, child); |
| } |
| } |
| |
| public MOTree(String urn, String rev, OMAConstructed root) throws IOException { |
| mUrn = urn; |
| mDtdRev = rev; |
| mRoot = root; |
| } |
| |
| public static MOTree buildMgmtTree(String urn, String rev, OMAConstructed root) |
| throws IOException { |
| OMAConstructed realRoot; |
| switch (urn) { |
| case OMAConstants.PPS_URN: |
| case OMAConstants.DevInfoURN: |
| case OMAConstants.DevDetailURN: |
| case OMAConstants.DevDetailXURN: |
| realRoot = new MgmtTreeRoot(OMAConstants.OMAVersion); |
| realRoot.addChild(root); |
| return new MOTree(urn, rev, realRoot); |
| default: |
| return new MOTree(urn, rev, root); |
| } |
| } |
| |
| public static boolean hasMgmtTreeTag(String text) { |
| for (int n = 0; n < text.length(); n++) { |
| char ch = text.charAt(n); |
| if (ch > ' ') { |
| return text.regionMatches(true, n, '<' + MgmtTreeTag + '>', |
| 0, MgmtTreeTag.length() + 2); |
| } |
| } |
| return false; |
| } |
| |
| private static class NodeData { |
| private final String mName; |
| private String mPath; |
| private String mValue; |
| |
| private NodeData(String name) { |
| mName = name; |
| } |
| |
| private void setPath(String path) { |
| mPath = path; |
| } |
| |
| private void setValue(String value) { |
| mValue = value; |
| } |
| |
| public String getName() { |
| return mName; |
| } |
| |
| public String getPath() { |
| return mPath; |
| } |
| |
| public String getValue() { |
| return mValue; |
| } |
| } |
| |
| private static void buildNode(OMANode parent, XMLNode node) throws IOException { |
| if (!node.getTag().equals(NodeTag)) |
| throw new IOException("Node is a '" + node.getTag() + "' instead of a 'Node'"); |
| |
| Map<String, XMLNode> checkMap = new HashMap<>(3); |
| String context = null; |
| List<NodeData> values = new ArrayList<>(); |
| List<XMLNode> children = new ArrayList<>(); |
| |
| NodeData curValue = null; |
| |
| for (XMLNode child : node.getChildren()) { |
| XMLNode old = checkMap.put(child.getTag(), child); |
| |
| switch (child.getTag()) { |
| case NodeNameTag: |
| if (curValue != null) |
| throw new IOException(NodeNameTag + " not expected"); |
| curValue = new NodeData(child.getText()); |
| |
| break; |
| case PathTag: |
| if (curValue == null || curValue.getPath() != null) |
| throw new IOException(PathTag + " not expected"); |
| curValue.setPath(child.getText()); |
| |
| break; |
| case ValueTag: |
| if (!children.isEmpty()) |
| throw new IOException(ValueTag + " in constructed node"); |
| if (curValue == null || curValue.getValue() != null) |
| throw new IOException(ValueTag + " not expected"); |
| curValue.setValue(child.getText()); |
| values.add(curValue); |
| curValue = null; |
| |
| break; |
| case RTPropTag: |
| if (old != null) |
| throw new IOException("Duplicate " + RTPropTag); |
| XMLNode typeNode = getNextNode(child, TypeTag); |
| XMLNode ddfName = getNextNode(typeNode, DDFNameTag); |
| context = ddfName.getText(); |
| if (context == null) |
| throw new IOException("No text in " + DDFNameTag); |
| |
| break; |
| case NodeTag: |
| if (!values.isEmpty()) |
| throw new IOException("Scalar node " + node.getText() + " has Node child"); |
| children.add(child); |
| |
| break; |
| } |
| } |
| |
| if (values.isEmpty()) { |
| if (curValue == null) |
| throw new IOException("Missing name"); |
| |
| OMANode subNode = parent.addChild(curValue.getName(), |
| context, null, curValue.getPath()); |
| |
| for (XMLNode child : children) { |
| buildNode(subNode, child); |
| } |
| } else { |
| if (!children.isEmpty()) |
| throw new IOException("Got both sub nodes and value(s)"); |
| |
| for (NodeData nodeData : values) { |
| parent.addChild(nodeData.getName(), context, |
| nodeData.getValue(), nodeData.getPath()); |
| } |
| } |
| } |
| |
| private static XMLNode getNextNode(XMLNode node, String tag) throws IOException { |
| if (node == null) |
| throw new IOException("No node for " + tag); |
| if (node.getChildren().size() != 1) |
| throw new IOException("Expected " + node.getTag() + " to have exactly one child"); |
| XMLNode child = node.getChildren().iterator().next(); |
| if (!child.getTag().equals(tag)) |
| throw new IOException("Expected " + node.getTag() + " to have child '" + tag + |
| "' instead of '" + child.getTag() + "'"); |
| return child; |
| } |
| |
| public String getUrn() { |
| return mUrn; |
| } |
| |
| public String getDtdRev() { |
| return mDtdRev; |
| } |
| |
| public OMAConstructed getRoot() { |
| return mRoot; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("MO Tree v").append(mDtdRev).append(", urn ").append(mUrn).append(")\n"); |
| sb.append(mRoot); |
| |
| return sb.toString(); |
| } |
| |
| public void marshal(OutputStream out) throws IOException { |
| out.write("tree ".getBytes(StandardCharsets.UTF_8)); |
| OMAConstants.serializeString(mDtdRev, out); |
| out.write(String.format("(%s)\n", mUrn).getBytes(StandardCharsets.UTF_8)); |
| mRoot.marshal(out, 0); |
| } |
| |
| public static MOTree unmarshal(InputStream in) throws IOException { |
| boolean strip = true; |
| StringBuilder tree = new StringBuilder(); |
| for (; ; ) { |
| int octet = in.read(); |
| if (octet < 0) { |
| return null; |
| } else if (octet > ' ') { |
| tree.append((char) octet); |
| strip = false; |
| } else if (!strip) { |
| break; |
| } |
| } |
| if (!tree.toString().equals("tree")) { |
| throw new IOException("Not a tree: " + tree); |
| } |
| |
| String version = OMAConstants.deserializeString(in); |
| int next = in.read(); |
| if (next != '(') { |
| throw new IOException("Expected URN in tree definition"); |
| } |
| String urn = OMAConstants.readURN(in); |
| |
| OMAConstructed root = OMANode.unmarshal(in); |
| |
| return new MOTree(urn, version, root); |
| } |
| |
| public String toXml() { |
| StringBuilder sb = new StringBuilder(); |
| mRoot.toXml(sb); |
| return sb.toString(); |
| } |
| } |