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