atomic-storage: remove type dependency at segment level I/O
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / utils / transformer / UintAdaptingPruner.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.controller.cluster.datastore.node.utils.transformer;
9
10 import static com.google.common.base.Verify.verify;
11
12 import com.google.common.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import com.google.common.collect.ImmutableMap;
16 import java.io.IOException;
17 import java.math.BigInteger;
18 import java.util.HashMap;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.NoSuchElementException;
22 import java.util.Set;
23 import java.util.function.Function;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.common.Uint16;
27 import org.opendaylight.yangtools.yang.common.Uint32;
28 import org.opendaylight.yangtools.yang.common.Uint64;
29 import org.opendaylight.yangtools.yang.common.Uint8;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
32 import org.opendaylight.yangtools.yang.data.impl.schema.ReusableImmutableNormalizedNodeStreamWriter;
33 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
34 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
39 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
41 import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
42 import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
43 import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 final class UintAdaptingPruner extends ReusableNormalizedNodePruner {
48     @FunctionalInterface
49     private interface NipAdapter extends Function<NodeIdentifierWithPredicates, NodeIdentifierWithPredicates> {
50
51     }
52
53     private enum ValueAdapter implements Function<Object, Object> {
54         UINT8 {
55             @Override
56             public Object apply(final Object obj) {
57                 if (obj instanceof Short) {
58                     LOG.trace("Translating legacy uint8 {}", obj);
59                     return Uint8.valueOf((Short) obj);
60                 }
61                 return obj;
62             }
63         },
64         UINT16 {
65             @Override
66             public Object apply(final Object obj) {
67                 if (obj instanceof Integer) {
68                     LOG.trace("Translating legacy uint16 {}", obj);
69                     return Uint16.valueOf((Integer) obj);
70                 }
71                 return obj;
72             }
73         },
74         UINT32 {
75             @Override
76             public Object apply(final Object obj) {
77                 if (obj instanceof Long) {
78                     LOG.trace("Translating legacy uint32 {}", obj);
79                     return Uint32.valueOf((Long) obj);
80                 }
81                 return obj;
82             }
83         },
84         UINT64 {
85             @Override
86             public Object apply(final Object obj) {
87                 if (obj instanceof BigInteger) {
88                     LOG.trace("Translating legacy uint64 {}", obj);
89                     return Uint64.valueOf((BigInteger) obj);
90                 }
91                 return obj;
92             }
93         };
94
95         private static final Logger LOG = LoggerFactory.getLogger(ValueAdapter.class);
96
97         static @Nullable ValueAdapter forType(final TypeDefinition<?> type) {
98             if (type instanceof Uint8TypeDefinition) {
99                 return UINT8;
100             } else if (type instanceof Uint16TypeDefinition) {
101                 return UINT16;
102             } else if (type instanceof Uint32TypeDefinition) {
103                 return UINT32;
104             } else if (type instanceof Uint64TypeDefinition) {
105                 return UINT64;
106             } else {
107                 return null;
108             }
109         }
110     }
111
112     private static final LoadingCache<ListSchemaNode, NipAdapter> NIP_ADAPTERS = CacheBuilder.newBuilder()
113             .weakKeys().build(new AdapterCacheLoader());
114
115     UintAdaptingPruner(final DataSchemaContextTree tree) {
116         super(tree);
117     }
118
119     @Override
120     public ReusableNormalizedNodePruner duplicate() {
121         return new UintAdaptingPruner(getTree());
122     }
123
124     @Override
125     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
126             throws IOException {
127         enter(this::adaptEntry, identifier, childSizeHint);
128     }
129
130     @Override
131     public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
132         enter(this::adaptEntry, name);
133     }
134
135     @Override
136     Object translateScalar(final DataSchemaContextNode<?> context, final Object value) throws IOException {
137         final DataSchemaNode schema = context.getDataSchemaNode();
138         return schema instanceof TypedDataSchemaNode ? adaptValue(((TypedDataSchemaNode) schema).getType(), value)
139                 : value;
140     }
141
142     private void adaptEntry(final ReusableImmutableNormalizedNodeStreamWriter writer, final NodeWithValue<?> name) {
143         final NodeWithValue<?> adapted;
144         final DataSchemaNode schema = currentSchema().getDataSchemaNode();
145         if (schema instanceof TypedDataSchemaNode) {
146             final Object oldValue = name.getValue();
147             final Object newValue = adaptValue(((TypedDataSchemaNode) schema).getType(), oldValue);
148             adapted = newValue == oldValue ? name : new NodeWithValue<>(name.getNodeType(), newValue);
149         } else {
150             adapted = name;
151         }
152
153         writer.startLeafSetEntryNode(adapted);
154     }
155
156     private void adaptEntry(final ReusableImmutableNormalizedNodeStreamWriter writer,
157             final NodeIdentifierWithPredicates name, final int size) {
158         final NodeIdentifierWithPredicates adapted;
159         final DataSchemaNode schema = currentSchema().getDataSchemaNode();
160         if (schema instanceof ListSchemaNode) {
161             adapted = NIP_ADAPTERS.getUnchecked((ListSchemaNode) schema).apply(name);
162         } else {
163             adapted = name;
164         }
165
166         writer.startMapEntryNode(adapted, size);
167     }
168
169     private static Object adaptValue(final TypeDefinition<?> type, final Object value) {
170         final ValueAdapter adapter = ValueAdapter.forType(type);
171         return adapter != null ? adapter.apply(value) : value;
172     }
173
174     private static final class AdapterCacheLoader extends CacheLoader<ListSchemaNode, NipAdapter> {
175         @Override
176         public NipAdapter load(final ListSchemaNode key) {
177             final Map<QName, ValueAdapter> adapters = new HashMap<>();
178
179             for (QName qname : key.getKeyDefinition()) {
180                 final DataSchemaNode child;
181                 try {
182                     child = key.findDataTreeChild(qname).orElseThrow();
183                 } catch (NoSuchElementException e) {
184                     throw new IllegalStateException("Failed to find child " + qname, e);
185                 }
186
187                 verify(child instanceof LeafSchemaNode, "Key references non-leaf child %s", child);
188                 final ValueAdapter adapter = ValueAdapter.forType(((LeafSchemaNode) child).getType());
189                 if (adapter != null) {
190                     adapters.put(qname, adapter);
191                 }
192             }
193
194             return adapters.isEmpty() ? name -> name : new TransformingNipAdapter(adapters);
195         }
196     }
197
198     private static final class TransformingNipAdapter implements NipAdapter {
199         private final ImmutableMap<QName, ValueAdapter> adapters;
200
201         TransformingNipAdapter(final Map<QName, ValueAdapter> toTransform) {
202             adapters = ImmutableMap.copyOf(toTransform);
203         }
204
205         @Override
206         public NodeIdentifierWithPredicates apply(final NodeIdentifierWithPredicates name) {
207             final Set<Entry<QName, Object>> entries = name.entrySet();
208             final ImmutableMap.Builder<QName, Object> newEntries = ImmutableMap.builderWithExpectedSize(entries.size());
209             for (Entry<QName, Object> e : entries) {
210                 final QName qname = e.getKey();
211                 final ValueAdapter adapter = adapters.get(qname);
212                 newEntries.put(qname, adapter != null ? adapter.apply(e.getValue()) : e.getValue());
213             }
214
215             return NodeIdentifierWithPredicates.of(name.getNodeType(), newEntries.build());
216         }
217     }
218 }