summaryrefslogtreecommitdiffstats
path: root/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppOperationalDataTree.java
blob: c378365f3b6878208cf936a5633ddd8cf7b068eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.fd.honeycomb.v3po.impl.data;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.getOnlyElement;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import io.fd.honeycomb.v3po.impl.trans.r.ReaderRegistry;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * ReadableVppDataTree implementation for operational data.
 */
public final class VppOperationalDataTree implements ReadableVppDataTree {
    private static final Logger LOG = LoggerFactory.getLogger(VppOperationalDataTree.class);

    private final BindingNormalizedNodeSerializer serializer;
    private final ReaderRegistry readerRegistry;
    private final SchemaContext globalContext;

    /**
     * Creates operational data tree instance.
     *
     * @param serializer     service for serialization between Java Binding Data representation and NormalizedNode
     *                       representation.
     * @param globalContext  service for obtaining top level context data from all yang modules.
     * @param readerRegistry service responsible for translation between DataObjects and VPP APIs.
     */
    public VppOperationalDataTree(@Nonnull BindingNormalizedNodeSerializer serializer,
                                  @Nonnull final SchemaContext globalContext, @Nonnull ReaderRegistry readerRegistry) {
        this.globalContext = checkNotNull(globalContext, "serializer should not be null");
        this.serializer = checkNotNull(serializer, "serializer should not be null");
        this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null");
    }

    @Override
    public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
            @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) {

        if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
            LOG.debug("VppOperationalDataProxy.read(), yangInstanceIdentifier=ROOT");
            return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>of(readRoot()));
        }

        LOG.debug("VppOperationalDataProxy.read(), yangInstanceIdentifier={}", yangInstanceIdentifier);
        final InstanceIdentifier<?> path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier);
        if (path == null) {
            // TODO try to translate wildcarded identifiers here as a workaround if it is expected to be used that way
            // Currently its not possible to read list using wildcarded ID. SO we may not need this at all.
        }
        checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier);
        LOG.debug("VppOperationalDataProxy.read(), path={}", path);

        final List<? extends DataObject> dataObjects = readerRegistry.read(path);

        if (dataObjects.isEmpty()) {
            return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>absent());
        }

        final NormalizedNode<?, ?> value = wrapDataObjects(yangInstanceIdentifier, path, dataObjects);
        return Futures.immediateCheckedFuture(Optional.<NormalizedNode<?, ?>>fromNullable(value));
    }

    private DataSchemaNode getSchemaNode(final @Nonnull YangInstanceIdentifier yangInstanceIdentifier) {
        return globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
    }

    private NormalizedNode<?, ?> readRoot() {
        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> dataNodeBuilder =
                Builders.containerBuilder()
                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME));

        final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects =
                readerRegistry.readAll();

        for (final InstanceIdentifier<? extends DataObject> instanceIdentifier : dataObjects.keySet()) {
            final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier);
            final NormalizedNode<?, ?> node =
                    wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier));
            dataNodeBuilder.withChild((DataContainerChild<?, ?>) node);
        }

        return dataNodeBuilder.build();
    }

    private NormalizedNode<?, ?> wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier,
                                              final InstanceIdentifier<? extends DataObject> instanceIdentifier,
                                              final Collection<? extends DataObject> dataObjects) {
        final Collection<NormalizedNode<?, ?>> normalizedRootElements = Collections2
                .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier));

        final DataSchemaNode schemaNode = getSchemaNode(yangInstanceIdentifier);
        if (schemaNode instanceof ListSchemaNode) {
            // In case of a list, wrap all the values in a Mixin parent node
            final ListSchemaNode listSchema = (ListSchemaNode) schemaNode;
            return wrapListIntoMixinNode(normalizedRootElements, listSchema);
        } else {
            Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected");
            return getOnlyElement(normalizedRootElements);
        }
    }

    private static DataContainerChild<?, ?> wrapListIntoMixinNode(
        final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
        if (listSchema.getKeyDefinition().isEmpty()) {
            final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> listBuilder =
                Builders.unkeyedListBuilder();
            for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
                listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement);
            }
            return listBuilder.build();
        } else {
            final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> listBuilder =
                listSchema.isUserOrdered()
                    ? Builders.orderedMapBuilder()
                    : Builders.mapBuilder();

            for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
                listBuilder.withChild((MapEntryNode) normalizedRootElement);
            }
            return listBuilder.build();
        }
    }

    @SuppressWarnings("unchecked")
    private Function<DataObject, NormalizedNode<?, ?>> toNormalizedNodeFunction(final InstanceIdentifier path) {
        return new Function<DataObject, NormalizedNode<?, ?>>() {
            @Override
            public NormalizedNode<?, ?> apply(@Nullable final DataObject dataObject) {
                LOG.trace("VppOperationalDataProxy.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
                final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
                        serializer.toNormalizedNode(path, dataObject);

                LOG.trace("VppOperationalDataProxy.toNormalizedNode(), normalizedNodeEntry={}", entry);
                return entry.getValue();
            }
        };
    }
}