Remove JournalWriter.getLastEntry()
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / persisted / DataTreeCandidateInputOutput.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.controller.cluster.datastore.persisted;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.annotations.VisibleForTesting;
14 import java.io.DataInput;
15 import java.io.DataOutput;
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.yangtools.concepts.Immutable;
22 import org.opendaylight.yangtools.yang.data.api.schema.stream.ReusableStreamReceiver;
23 import org.opendaylight.yangtools.yang.data.codec.binfmt.NormalizedNodeDataInput;
24 import org.opendaylight.yangtools.yang.data.codec.binfmt.NormalizedNodeDataOutput;
25 import org.opendaylight.yangtools.yang.data.codec.binfmt.NormalizedNodeStreamVersion;
26 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
27 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
28 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
29 import org.opendaylight.yangtools.yang.data.tree.spi.DataTreeCandidateNodes;
30 import org.opendaylight.yangtools.yang.data.tree.spi.DataTreeCandidates;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * Utility serialization/deserialization for {@link DataTreeCandidate}. Note that this utility does not maintain
36  * before-image information across serialization.
37  */
38 @Beta
39 public final class DataTreeCandidateInputOutput {
40     public record DataTreeCandidateWithVersion(
41             @NonNull DataTreeCandidate candidate,
42             @NonNull NormalizedNodeStreamVersion version) implements Immutable {
43         public DataTreeCandidateWithVersion {
44             requireNonNull(candidate);
45             requireNonNull(version);
46         }
47     }
48
49     private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidateInputOutput.class);
50     private static final byte DELETE = 0;
51     private static final byte SUBTREE_MODIFIED = 1;
52     private static final byte UNMODIFIED = 2;
53     private static final byte WRITE = 3;
54     private static final byte APPEARED = 4;
55     private static final byte DISAPPEARED = 5;
56
57     private DataTreeCandidateInputOutput() {
58         throw new UnsupportedOperationException();
59     }
60
61     private static DataTreeCandidateNode readModifiedNode(final ModificationType type, final NormalizedNodeDataInput in,
62             final ReusableStreamReceiver receiver) throws IOException {
63         final var pathArg = in.readPathArgument();
64         final var children = readChildren(in, receiver);
65         if (children.isEmpty()) {
66             LOG.debug("Modified node {} does not have any children, not instantiating it", pathArg);
67             return null;
68         }
69
70         return ModifiedDataTreeCandidateNode.create(pathArg, type, children);
71     }
72
73     private static List<DataTreeCandidateNode> readChildren(final NormalizedNodeDataInput in,
74             final ReusableStreamReceiver receiver) throws IOException {
75         final int size = in.readInt();
76         if (size == 0) {
77             return List.of();
78         }
79
80         final var ret = new ArrayList<DataTreeCandidateNode>(size);
81         for (int i = 0; i < size; ++i) {
82             final var child = readNode(in, receiver);
83             if (child != null) {
84                 ret.add(child);
85             }
86         }
87         return ret;
88     }
89
90     private static DataTreeCandidateNode readNode(final NormalizedNodeDataInput in,
91             final ReusableStreamReceiver receiver) throws IOException {
92         final byte type = in.readByte();
93         return switch (type) {
94             case APPEARED -> readModifiedNode(ModificationType.APPEARED, in, receiver);
95             case DELETE -> DeletedDataTreeCandidateNode.create(in.readPathArgument());
96             case DISAPPEARED -> readModifiedNode(ModificationType.DISAPPEARED, in, receiver);
97             case SUBTREE_MODIFIED -> readModifiedNode(ModificationType.SUBTREE_MODIFIED, in, receiver);
98             case UNMODIFIED -> null;
99             case WRITE -> DataTreeCandidateNodes.written(in.readNormalizedNode(receiver));
100             default -> throw new IllegalArgumentException("Unhandled node type " + type);
101         };
102     }
103
104     public static DataTreeCandidateWithVersion readDataTreeCandidate(final DataInput in,
105             final ReusableStreamReceiver receiver) throws IOException {
106         final var reader = NormalizedNodeDataInput.newDataInput(in);
107         final var rootPath = reader.readYangInstanceIdentifier();
108         final byte type = reader.readByte();
109
110         final DataTreeCandidateNode rootNode = switch (type) {
111             case APPEARED -> ModifiedDataTreeCandidateNode.create(ModificationType.APPEARED,
112                 readChildren(reader, receiver));
113             case DELETE -> DeletedDataTreeCandidateNode.create();
114             case DISAPPEARED -> ModifiedDataTreeCandidateNode.create(ModificationType.DISAPPEARED,
115                 readChildren(reader, receiver));
116             case SUBTREE_MODIFIED -> ModifiedDataTreeCandidateNode.create(ModificationType.SUBTREE_MODIFIED,
117                 readChildren(reader, receiver));
118             case WRITE -> DataTreeCandidateNodes.written(reader.readNormalizedNode(receiver));
119             case UNMODIFIED -> AbstractDataTreeCandidateNode.createUnmodified();
120             default -> throw new IllegalArgumentException("Unhandled node type " + type);
121         };
122         return new DataTreeCandidateWithVersion(DataTreeCandidates.newDataTreeCandidate(rootPath, rootNode),
123             reader.getVersion());
124     }
125
126     private static void writeChildren(final NormalizedNodeDataOutput out,
127             final Collection<DataTreeCandidateNode> children) throws IOException {
128         out.writeInt(children.size());
129         for (var child : children) {
130             writeNode(out, child);
131         }
132     }
133
134     private static void writeNode(final NormalizedNodeDataOutput out, final DataTreeCandidateNode node)
135             throws IOException {
136         switch (node.modificationType()) {
137             case APPEARED -> {
138                 out.writeByte(APPEARED);
139                 out.writePathArgument(node.name());
140                 writeChildren(out, node.childNodes());
141             }
142             case DELETE -> {
143                 out.writeByte(DELETE);
144                 out.writePathArgument(node.name());
145             }
146             case DISAPPEARED -> {
147                 out.writeByte(DISAPPEARED);
148                 out.writePathArgument(node.name());
149                 writeChildren(out, node.childNodes());
150             }
151             case SUBTREE_MODIFIED -> {
152                 out.writeByte(SUBTREE_MODIFIED);
153                 out.writePathArgument(node.name());
154                 writeChildren(out, node.childNodes());
155             }
156             case WRITE -> {
157                 out.writeByte(WRITE);
158                 out.writeNormalizedNode(node.getDataAfter());
159             }
160             case UNMODIFIED -> out.writeByte(UNMODIFIED);
161             default -> throwUnhandledNodeType(node);
162         }
163     }
164
165     @VisibleForTesting
166     public static void writeDataTreeCandidate(final DataOutput out, final PayloadVersion version,
167             final DataTreeCandidate candidate) throws IOException {
168         try (var writer = version.getStreamVersion().newDataOutput(out)) {
169             writer.writeYangInstanceIdentifier(candidate.getRootPath());
170
171             final var node = candidate.getRootNode();
172             switch (node.modificationType()) {
173                 case APPEARED -> {
174                     writer.writeByte(APPEARED);
175                     writeChildren(writer, node.childNodes());
176                 }
177                 case DELETE -> writer.writeByte(DELETE);
178                 case DISAPPEARED -> {
179                     writer.writeByte(DISAPPEARED);
180                     writeChildren(writer, node.childNodes());
181                 }
182                 case SUBTREE_MODIFIED -> {
183                     writer.writeByte(SUBTREE_MODIFIED);
184                     writeChildren(writer, node.childNodes());
185                 }
186                 case UNMODIFIED -> writer.writeByte(UNMODIFIED);
187                 case WRITE -> {
188                     writer.writeByte(WRITE);
189                     writer.writeNormalizedNode(node.getDataAfter());
190                 }
191                 default -> throwUnhandledNodeType(node);
192             }
193         }
194     }
195
196     public static void writeDataTreeCandidate(final DataOutput out, final DataTreeCandidate candidate)
197             throws IOException {
198         writeDataTreeCandidate(out, PayloadVersion.current(), candidate);
199     }
200
201     private static void throwUnhandledNodeType(final DataTreeCandidateNode node) {
202         throw new IllegalArgumentException("Unhandled node type " + node.modificationType());
203     }
204 }