Improve ListEntryNodeDataWithSchema
[yangtools.git] / yang / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / ListEntryNodeDataWithSchema.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.util;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static com.google.common.base.Verify.verify;
12
13 import java.io.IOException;
14 import java.util.Collection;
15 import java.util.HashMap;
16 import java.util.Map;
17 import org.opendaylight.yangtools.util.ImmutableMapTemplate;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
20 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
21 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
22 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
25
26 /**
27  * Utility class used for tracking parser state as needed by a StAX-like parser.
28  * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
29  *
30  * <p>
31  * Represents a YANG list entry node.
32  */
33 public class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema {
34     // This template results in Maps in schema definition order
35     private final ImmutableMapTemplate<QName> predicateTemplate;
36     private final Map<QName, SimpleNodeDataWithSchema> keyValues;
37
38     // FIXME: 3.0.0: require ListSchemaNode
39     // FIXME: 3.0.0: hide this constructor and provide specialized keyed/unkeyed classes
40     public ListEntryNodeDataWithSchema(final DataSchemaNode schema) {
41         super(schema);
42
43         final Collection<QName> keyDef = ((ListSchemaNode) getSchema()).getKeyDefinition();
44         if (keyDef.isEmpty()) {
45             predicateTemplate = null;
46             keyValues = null;
47         } else {
48             predicateTemplate = ImmutableMapTemplate.ordered(keyDef);
49             keyValues = new HashMap<>();
50         }
51     }
52
53     @Override
54     public void addChild(final AbstractNodeDataWithSchema newChild) {
55         if (predicateTemplate != null) {
56             final DataSchemaNode childSchema = newChild.getSchema();
57             if (childSchema instanceof LeafSchemaNode) {
58                 populateKeyValue(childSchema.getQName(), newChild);
59             }
60         }
61         super.addChild(newChild);
62     }
63
64     private void populateKeyValue(final QName childName, final AbstractNodeDataWithSchema child) {
65         if (predicateTemplate.keySet().contains(childName)) {
66             verify(child instanceof SimpleNodeDataWithSchema);
67             keyValues.put(childName, (SimpleNodeDataWithSchema)child);
68         }
69     }
70
71     @Override
72     public void write(final NormalizedNodeStreamWriter writer) throws IOException {
73         writer.nextDataSchemaNode(getSchema());
74         if (predicateTemplate != null) {
75             writeKeyedListItem(writer);
76         } else {
77             writer.startUnkeyedListItem(provideNodeIdentifier(), childSizeHint());
78         }
79
80         super.write(writer);
81         writer.endNode();
82     }
83
84     private void writeKeyedListItem(final NormalizedNodeStreamWriter writer) throws IOException {
85         // FIXME: 3.0.0: remove this check? predicateTemplate will throw an IllegalArgumentException if anything
86         //               goes wrong -- which is a change of behavior, as now we're throwing an ISE. Do we want that?
87         final Collection<QName> keySet = predicateTemplate.keySet();
88         checkState(keySet.size() == keyValues.size(),
89                 "Map entry corresponding to %s is missing some of required keys %s", getSchema().getQName(), keySet);
90
91         final NodeIdentifierWithPredicates identifier = new NodeIdentifierWithPredicates(getSchema().getQName(),
92             predicateTemplate.instantiateTransformed(keyValues, (key, node) -> node.getValue()));
93
94         if (writer instanceof NormalizedNodeStreamAttributeWriter && getAttributes() != null) {
95             ((NormalizedNodeStreamAttributeWriter) writer).startMapEntryNode(identifier, childSizeHint(),
96                 getAttributes());
97         } else {
98             writer.startMapEntryNode(identifier, childSizeHint());
99         }
100     }
101 }