Reorganize AbstractNormalizedNodeDataOutput
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / stream / AbstractLithiumDataOutput.java
1 /*
2  * Copyright (c) 2014 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.node.utils.stream;
9
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.collect.ImmutableMap;
15 import java.io.DataOutput;
16 import java.io.IOException;
17 import java.math.BigDecimal;
18 import java.math.BigInteger;
19 import java.nio.charset.StandardCharsets;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Optional;
23 import java.util.Set;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.opendaylight.yangtools.yang.common.Empty;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.QNameModule;
28 import org.opendaylight.yangtools.yang.common.Revision;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30
31 /**
32  * "original" type mapping. Baseline is Lithium but it really was introduced in Oxygen, where {@code type empty} was
33  * remapped from null.
34  *
35  * <p>
36  * {@code uint8}, {@code uint16}, {@code uint32} use java.lang types with widening, hence their value types overlap with
37  * mapping of {@code int16}, {@code int32} and {@code int64}, making that difference indiscernible without YANG schema
38  * knowledge.
39  */
40 abstract class AbstractLithiumDataOutput extends AbstractNormalizedNodeDataOutput {
41     private static final ImmutableMap<Class<?>, Byte> KNOWN_TYPES = ImmutableMap.<Class<?>, Byte>builder()
42             .put(String.class, ValueTypes.STRING_TYPE)
43             .put(Byte.class, ValueTypes.BYTE_TYPE)
44             .put(Integer.class, ValueTypes.INT_TYPE)
45             .put(Long.class, ValueTypes.LONG_TYPE)
46             .put(Boolean.class, ValueTypes.BOOL_TYPE)
47             .put(QName.class, ValueTypes.QNAME_TYPE)
48             .put(Short.class, ValueTypes.SHORT_TYPE)
49             .put(BigInteger.class, ValueTypes.BIG_INTEGER_TYPE)
50             .put(BigDecimal.class, ValueTypes.BIG_DECIMAL_TYPE)
51             .put(byte[].class, ValueTypes.BINARY_TYPE)
52             .put(Empty.class, ValueTypes.EMPTY_TYPE)
53             .build();
54
55
56     private final Map<String, Integer> stringCodeMap = new HashMap<>();
57
58     AbstractLithiumDataOutput(final DataOutput output) {
59         super(output);
60     }
61
62     @Override
63     final void writeObject(final DataOutput rawOuput, final Object value) throws IOException {
64         byte type = getSerializableType(value);
65         // Write object type first
66         rawOuput.writeByte(type);
67
68         switch (type) {
69             case ValueTypes.BOOL_TYPE:
70                 rawOuput.writeBoolean((Boolean) value);
71                 break;
72             case ValueTypes.QNAME_TYPE:
73                 writeQNameInternal((QName) value);
74                 break;
75             case ValueTypes.INT_TYPE:
76                 rawOuput.writeInt((Integer) value);
77                 break;
78             case ValueTypes.BYTE_TYPE:
79                 rawOuput.writeByte((Byte) value);
80                 break;
81             case ValueTypes.LONG_TYPE:
82                 rawOuput.writeLong((Long) value);
83                 break;
84             case ValueTypes.SHORT_TYPE:
85                 rawOuput.writeShort((Short) value);
86                 break;
87             case ValueTypes.BITS_TYPE:
88                 writeObjSet((Set<?>) value);
89                 break;
90             case ValueTypes.BINARY_TYPE:
91                 byte[] bytes = (byte[]) value;
92                 rawOuput.writeInt(bytes.length);
93                 rawOuput.write(bytes);
94                 break;
95             case ValueTypes.YANG_IDENTIFIER_TYPE:
96                 writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
97                 break;
98             case ValueTypes.EMPTY_TYPE:
99                 break;
100             case ValueTypes.STRING_BYTES_TYPE:
101                 final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
102                 rawOuput.writeInt(valueBytes.length);
103                 rawOuput.write(valueBytes);
104                 break;
105             default:
106                 rawOuput.writeUTF(value.toString());
107                 break;
108         }
109     }
110
111     final void defaultWriteQName(final QName qname) throws IOException {
112         writeString(qname.getLocalName());
113         writeModule(qname.getModule());
114     }
115
116     final void defaultWriteModule(final QNameModule module) throws IOException {
117         writeString(module.getNamespace().toString());
118         final Optional<Revision> revision = module.getRevision();
119         if (revision.isPresent()) {
120             writeString(revision.get().toString());
121         } else {
122             writeByte(TokenTypes.IS_NULL_VALUE);
123         }
124     }
125
126     @Override
127     protected final void writeString(final @NonNull String string) throws IOException {
128         final Integer value = stringCodeMap.get(verifyNotNull(string));
129         if (value == null) {
130             stringCodeMap.put(string, stringCodeMap.size());
131             writeByte(TokenTypes.IS_STRING_VALUE);
132             writeUTF(string);
133         } else {
134             writeByte(TokenTypes.IS_CODE_VALUE);
135             writeInt(value);
136         }
137     }
138
139     abstract void writeModule(QNameModule module) throws IOException;
140
141     @VisibleForTesting
142     static final byte getSerializableType(final Object node) {
143         final Byte type = KNOWN_TYPES.get(requireNonNull(node).getClass());
144         if (type != null) {
145             if (type == ValueTypes.STRING_TYPE
146                     && ((String) node).length() >= ValueTypes.STRING_BYTES_LENGTH_THRESHOLD) {
147                 return ValueTypes.STRING_BYTES_TYPE;
148             }
149             return type;
150         }
151
152         if (node instanceof Set) {
153             return ValueTypes.BITS_TYPE;
154         }
155
156         if (node instanceof YangInstanceIdentifier) {
157             return ValueTypes.YANG_IDENTIFIER_TYPE;
158         }
159
160         throw new IllegalArgumentException("Unknown value type " + node.getClass().getSimpleName());
161     }
162 }