Add RFC7952 data model extensions
[yangtools.git] / yang / rfc7952-data-util / src / main / java / org / opendaylight / yangtools / rfc7952 / data / util / NormalizedNodeStreamWriterMetadataDecorator.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.util;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.io.IOException;
13 import java.util.ArrayDeque;
14 import java.util.Deque;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
19 import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataContainer;
20 import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataStreamWriter;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.data.api.schema.stream.ForwardingNormalizedNodeStreamWriter;
28 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
29
30 /**
31  * A simple decorator on top of a NormalizedNodeStreamWriter, which attaches NormalizedMetadata to the event stream,
32  * so that the metadata is emitted along with data.
33  */
34 final class NormalizedNodeStreamWriterMetadataDecorator extends ForwardingNormalizedNodeStreamWriter {
35     private final Deque<NormalizedMetadata> stack = new ArrayDeque<>();
36     private final NormalizedMetadataStreamWriter metaWriter;
37     private final NormalizedNodeStreamWriter writer;
38     private final NormalizedMetadata metadata;
39
40     NormalizedNodeStreamWriterMetadataDecorator(final NormalizedNodeStreamWriter writer,
41             final NormalizedMetadataStreamWriter metaWriter, final NormalizedMetadata metadata) {
42         this.writer = requireNonNull(writer);
43         this.metaWriter = requireNonNull(metaWriter);
44         this.metadata = requireNonNull(metadata);
45     }
46
47     @Override
48     protected NormalizedNodeStreamWriter delegate() {
49         return writer;
50     }
51
52     @Override
53     public void startLeafNode(final NodeIdentifier name) throws IOException {
54         super.startLeafNode(name);
55         enterMetadataNode(name);
56     }
57
58     @Override
59     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
60         super.startLeafSet(name, childSizeHint);
61         enterMetadataNode(name);
62     }
63
64     @Override
65     public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
66         super.startOrderedLeafSet(name, childSizeHint);
67         enterMetadataNode(name);
68     }
69
70     @Override
71     public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
72         super.startLeafSetEntryNode(name);
73         enterMetadataNode(name);
74     }
75
76     @Override
77     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
78         super.startContainerNode(name, childSizeHint);
79         enterMetadataNode(name);
80     }
81
82     @Override
83     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
84         super.startUnkeyedList(name, childSizeHint);
85         enterMetadataNode(name);
86     }
87
88     @Override
89     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
90         super.startUnkeyedListItem(name, childSizeHint);
91         enterMetadataNode(name);
92     }
93
94     @Override
95     public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
96         super.startMapNode(name, childSizeHint);
97         enterMetadataNode(name);
98     }
99
100     @Override
101     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
102             throws IOException {
103         super.startMapEntryNode(identifier, childSizeHint);
104         enterMetadataNode(identifier);
105     }
106
107     @Override
108     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
109         super.startOrderedMapNode(name, childSizeHint);
110         enterMetadataNode(name);
111     }
112
113     @Override
114     public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
115         super.startChoiceNode(name, childSizeHint);
116         enterMetadataNode(name);
117     }
118
119     @Override
120     public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
121         super.startAugmentationNode(identifier);
122         enterMetadataNode(identifier);
123     }
124
125     @Override
126     public void startAnyxmlNode(final NodeIdentifier name) throws IOException {
127         super.startAnyxmlNode(name);
128         enterMetadataNode(name);
129     }
130
131     @Override
132     public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
133         super.startYangModeledAnyXmlNode(name, childSizeHint);
134         enterMetadataNode(name);
135     }
136
137     @Override
138     public void endNode() throws IOException {
139         super.endNode();
140         stack.pop();
141     }
142
143     private void enterMetadataNode(final PathArgument name) throws IOException {
144         final NormalizedMetadata child = findMetadata(name);
145         if (child != null) {
146             emitAnnotations(child.getAnnotations());
147         }
148         stack.push(child);
149     }
150
151     private @Nullable NormalizedMetadata findMetadata(final PathArgument name) {
152         final NormalizedMetadata current = stack.peek();
153         if (current instanceof NormalizedMetadataContainer) {
154             return ((NormalizedMetadataContainer) current).getChild(name).orElse(null);
155         }
156
157         // This may either be the first entry or unattached metadata nesting
158         return stack.isEmpty() ? metadata : null;
159     }
160
161     private void emitAnnotations(final Map<QName, Object> annotations) throws IOException {
162         if (!annotations.isEmpty()) {
163             metaWriter.startMetadata(annotations.size());
164             for (Entry<QName, Object> entry : annotations.entrySet()) {
165                 metaWriter.startMetadataEntry(entry.getKey(), null);
166                 writer.nodeValue(entry.getValue());
167                 writer.endNode();
168             }
169             writer.endNode();
170         }
171     }
172 }