Retrofit mount-point awareness into yang-data-util
[yangtools.git] / yang / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / MountPointData.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.yangtools.yang.data.util;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import java.io.IOException;
15 import java.util.ArrayList;
16 import java.util.EnumMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Optional;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
23 import org.opendaylight.yangtools.rfc8528.data.api.DynamicMountPointSchemaResolver;
24 import org.opendaylight.yangtools.rfc8528.data.api.MountPointStreamWriter;
25 import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaResolver;
26 import org.opendaylight.yangtools.rfc8528.model.api.StaticMountPointSchemaResolver;
27 import org.opendaylight.yangtools.rfc8528.model.api.YangLibraryConstants.ContainerName;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
33 import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * YANG Schema Mount-supported data attached to either a {@code list} item or a {@code container}.
39  */
40 @Beta
41 public final class MountPointData extends AbstractIdentifiable<QName> {
42     private static final Logger LOG = LoggerFactory.getLogger(MountPointData.class);
43
44     private final Map<ContainerName, MountPointChild> yangLib = new EnumMap<>(ContainerName.class);
45     private final List<MountPointChild> children = new ArrayList<>();
46
47     MountPointData(final QName label) {
48         super(label);
49     }
50
51     public void setContainer(final @NonNull ContainerName containerName, final @NonNull MountPointChild data) {
52         final MountPointChild prev = yangLib.putIfAbsent(containerName, requireNonNull(data));
53         checkState(prev == null, "Attempted to duplicate container %s data %s with %s", containerName, prev, data);
54         addChild(data);
55     }
56
57     public void addChild(final @NonNull MountPointChild data) {
58         children.add(requireNonNull(data));
59     }
60
61     void write(final @NonNull NormalizedNodeStreamWriter writer) throws IOException {
62         final MountPointStreamWriter mountWriter = writer.getExtensions().getInstance(MountPointStreamWriter.class);
63         if (mountWriter == null) {
64             LOG.debug("Writer {} does not support mount points, ignoring data in {}", writer, getIdentifier());
65             return;
66         }
67
68         final Optional<MountPointSchemaResolver> optResolver = mountWriter.findMountPoint(getIdentifier());
69         if (!optResolver.isPresent()) {
70             LOG.debug("Mount point for {} is not present, ignoring it", getIdentifier());
71             return;
72         }
73
74         final MountPointSchemaResolver resolver = optResolver.get();
75         if (resolver instanceof StaticMountPointSchemaResolver) {
76             writeStatic(mountWriter, ((StaticMountPointSchemaResolver) resolver).getSchemaContext());
77         } else if (resolver instanceof DynamicMountPointSchemaResolver) {
78             writeDynamic(mountWriter, (DynamicMountPointSchemaResolver) resolver);
79         } else {
80             throw new IOException("Unhandled resolver " + resolver);
81         }
82     }
83
84     private void writeDynamic(final @NonNull MountPointStreamWriter mountWriter,
85             final DynamicMountPointSchemaResolver resolver) throws IOException {
86         for (Entry<ContainerName, MountPointChild> entry : yangLib.entrySet()) {
87             final Optional<SchemaContext> optContext = resolver.findContainerContext(entry.getKey());
88             if (!optContext.isPresent()) {
89                 LOG.debug("YANG Library context for mount point {} container {} not found", getIdentifier(),
90                     entry.getKey());
91                 continue;
92             }
93
94             final NormalizedNode<?, ?> data = entry.getValue().normalizeTo(optContext.get());
95             if (!(data instanceof ContainerNode)) {
96                 throw new IOException("Invalid non-container " + data);
97             }
98
99             final SchemaContext context;
100             try {
101                 context = resolver.assembleSchemaContext((ContainerNode) data);
102             } catch (YangParserException e) {
103                 throw new IOException("Failed to assemble context for " + data, e);
104             }
105
106             writeStatic(mountWriter, context);
107             return;
108         }
109
110         LOG.warn("Failed to create a dynamic context for mount point {}, ignoring its data", getIdentifier());
111     }
112
113     private void writeStatic(final @NonNull MountPointStreamWriter mountWriter,
114             final @NonNull SchemaContext schemaContext) throws IOException {
115         final Optional<NormalizedNodeStreamWriter> optWriter = mountWriter.startMountPoint(getIdentifier(),
116             schemaContext);
117         if (!optWriter.isPresent()) {
118             LOG.debug("Ignoring mount point {} data due to writer decision", getIdentifier());
119             return;
120         }
121
122         try (NormalizedNodeStreamWriter writer = optWriter.get()) {
123             for (MountPointChild child : children) {
124                 child.writeTo(writer, schemaContext);
125             }
126         }
127     }
128 }