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