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 OMAConstructed(null, MgmtTreeTag, urn, "xmlns", OMAConstants.SyncML);
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();
}
}