Promote InterningLeafSetNodeBuilder
[yangtools.git] / data / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / ImmutableMetadataNormalizedNodeStreamWriter.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.yang.data.impl.schema;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.collect.ImmutableMap;
15 import java.io.IOException;
16 import java.util.ArrayDeque;
17 import java.util.Deque;
18 import java.util.List;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
23 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedMetadata;
24 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
26 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.MetadataExtension;
27 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedMetadata.Builder;
28
29 /**
30  * A {@link NormalizedMetadata}-aware {@link ImmutableMetadataNormalizedNodeStreamWriter}. It advertizes the
31  * {@link MetadataExtension} extension.
32  */
33 @Beta
34 public class ImmutableMetadataNormalizedNodeStreamWriter extends ImmutableNormalizedNodeStreamWriter
35         implements MetadataExtension {
36     /**
37      * Snapshot of currently-open data- and metadatastate.
38      */
39     public static final class State {
40         final BuilderEntry metaBuilder;
41         final NormalizedNode.Builder dataBuilder;
42
43         State(final NormalizedNode.Builder dataBuilder, final BuilderEntry metadataBuilder) {
44             this.dataBuilder = requireNonNull(dataBuilder);
45             metaBuilder = requireNonNull(metadataBuilder);
46         }
47
48         public NormalizedNode.Builder getDataBuilder() {
49             return dataBuilder;
50         }
51
52         public Builder getMetaBuilder() {
53             return metaBuilder.builder;
54         }
55     }
56
57     @NonNullByDefault
58     private record BuilderEntry(PathArgument identifier, Builder builder) {
59         BuilderEntry {
60             requireNonNull(identifier);
61             requireNonNull(builder);
62         }
63     }
64
65     private final Deque<BuilderEntry> builders = new ArrayDeque<>();
66     private final NormalizationResultHolder holder;
67
68     protected ImmutableMetadataNormalizedNodeStreamWriter(final State state) {
69         super(state.dataBuilder);
70         builders.push(state.metaBuilder);
71         holder = null;
72     }
73
74     protected ImmutableMetadataNormalizedNodeStreamWriter(final NormalizationResultHolder holder) {
75         super(holder);
76         this.holder = requireNonNull(holder);
77     }
78
79     @Override
80     public final List<MetadataExtension> supportedExtensions() {
81         return List.of(this);
82     }
83
84     @Override
85     public final void metadata(final ImmutableMap<QName, Object> metadata) throws IOException {
86         final var current = builders.peek();
87         checkState(current != null, "Attempted to emit metadata when no metadata is open");
88         current.builder.withAnnotations(metadata);
89     }
90
91     /**
92      * Remove the currently-open builders for data and metadata from the stack.
93      *
94      * @return Builder state.
95      */
96     protected final @NonNull State popState() {
97         return new State(popBuilder(), builders.pop());
98     }
99
100     @Override
101     @SuppressWarnings("rawtypes")
102     final void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
103         super.enter(identifier, next);
104         builders.push(new BuilderEntry(identifier, ImmutableNormalizedMetadata.builder()));
105     }
106
107     @Override
108     public final void endNode() {
109         super.endNode();
110
111         final var last = builders.pop();
112         final var metadata = last.builder.build();
113         final var current = builders.peek();
114         if (current != null) {
115             if (!metadata.getAnnotations().isEmpty() || !metadata.getChildren().isEmpty()) {
116                 current.builder.withChild(last.identifier, metadata);
117             }
118         } else {
119             // All done
120             holder.setMetadata(metadata);
121         }
122     }
123 }