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