1package com.android.hotspot2.omadm;
2
3import java.io.IOException;
4import java.io.OutputStream;
5import java.nio.charset.StandardCharsets;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.Iterator;
9import java.util.Map;
10
11public class OMAConstructed extends OMANode {
12    private final MultiValueMap<OMANode> mChildren;
13
14    public OMAConstructed(OMAConstructed parent, String name, String context, String... avps) {
15        this(parent, name, context, new MultiValueMap<OMANode>(), buildAttributes(avps));
16    }
17
18    protected OMAConstructed(OMAConstructed parent, String name, String context,
19                             MultiValueMap<OMANode> children, Map<String, String> avps) {
20        super(parent, name, context, avps);
21        mChildren = children;
22    }
23
24    @Override
25    public OMANode addChild(String name, String context, String value, String pathString)
26            throws IOException {
27        if (pathString == null) {
28            OMANode child = value != null ?
29                    new OMAScalar(this, name, context, value) :
30                    new OMAConstructed(this, name, context);
31            mChildren.put(name, child);
32            return child;
33        } else {
34            OMANode target = this;
35            while (target.getParent() != null)
36                target = target.getParent();
37
38            for (String element : pathString.split("/")) {
39                target = target.getChild(element);
40                if (target == null)
41                    throw new IOException("No child node '" + element + "' in " + getPathString());
42                else if (target.isLeaf())
43                    throw new IOException("Cannot add child to leaf node: " + getPathString());
44            }
45            return target.addChild(name, context, value, null);
46        }
47    }
48
49    @Override
50    public OMAConstructed reparent(OMAConstructed parent) {
51        return new OMAConstructed(parent, getName(), getContext(), mChildren, getAttributes());
52    }
53
54    public void addChild(OMANode child) {
55        mChildren.put(child.getName(), child.reparent(this));
56    }
57
58    public String getScalarValue(Iterator<String> path) throws OMAException {
59        if (!path.hasNext()) {
60            throw new OMAException("Path too short for " + getPathString());
61        }
62        String tag = path.next();
63        OMANode child = mChildren.get(tag);
64        if (child != null) {
65            return child.getScalarValue(path);
66        } else {
67            return null;
68        }
69    }
70
71    @Override
72    public OMANode getListValue(Iterator<String> path) throws OMAException {
73        if (!path.hasNext()) {
74            return null;
75        }
76        String tag = path.next();
77        OMANode child;
78        if (tag.equals("?")) {
79            child = mChildren.getSingletonValue();
80        } else {
81            child = mChildren.get(tag);
82        }
83
84        if (child == null) {
85            return null;
86        } else if (path.hasNext()) {
87            return child.getListValue(path);
88        } else {
89            return child;
90        }
91    }
92
93    @Override
94    public boolean isLeaf() {
95        return false;
96    }
97
98    @Override
99    public Collection<OMANode> getChildren() {
100        return Collections.unmodifiableCollection(mChildren.values());
101    }
102
103    public OMANode getChild(String name) {
104        return mChildren.get(name);
105    }
106
107    public OMANode replaceNode(OMANode oldNode, OMANode newNode) {
108        return mChildren.replace(oldNode.getName(), oldNode, newNode);
109    }
110
111    public OMANode removeNode(String key, OMANode node) {
112        if (key.equals("?")) {
113            return mChildren.remove(node);
114        } else {
115            return mChildren.remove(key, node);
116        }
117    }
118
119    @Override
120    public String getValue() {
121        throw new UnsupportedOperationException();
122    }
123
124    @Override
125    public void toString(StringBuilder sb, int level) {
126        sb.append(getPathString());
127        if (getContext() != null) {
128            sb.append(" (").append(getContext()).append(')');
129        }
130        sb.append('\n');
131
132        for (OMANode node : mChildren.values()) {
133            node.toString(sb, level + 1);
134        }
135    }
136
137    @Override
138    public void marshal(OutputStream out, int level) throws IOException {
139        OMAConstants.indent(level, out);
140        OMAConstants.serializeString(getName(), out);
141        if (getContext() != null) {
142            out.write(String.format("(%s)", getContext()).getBytes(StandardCharsets.UTF_8));
143        }
144        out.write(new byte[]{'+', '\n'});
145
146        for (OMANode child : mChildren.values()) {
147            child.marshal(out, level + 1);
148        }
149        OMAConstants.indent(level, out);
150        out.write(".\n".getBytes(StandardCharsets.UTF_8));
151    }
152
153    @Override
154    public void fillPayload(StringBuilder sb) {
155        if (getContext() != null) {
156            sb.append('<').append(MOTree.RTPropTag).append(">\n");
157            sb.append('<').append(MOTree.TypeTag).append(">\n");
158            sb.append('<').append(MOTree.DDFNameTag).append(">");
159            sb.append(getContext());
160            sb.append("</").append(MOTree.DDFNameTag).append(">\n");
161            sb.append("</").append(MOTree.TypeTag).append(">\n");
162            sb.append("</").append(MOTree.RTPropTag).append(">\n");
163        }
164
165        for (OMANode child : getChildren()) {
166            child.toXml(sb);
167        }
168    }
169}
170