Move MountPointChild to rfc8528-data-api
[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.MountPointChild;
24 import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
25 import org.opendaylight.yangtools.rfc8528.data.api.MountPointNodeFactory;
26 import org.opendaylight.yangtools.rfc8528.data.api.MountPointNodeFactoryResolver;
27 import org.opendaylight.yangtools.rfc8528.data.api.MountPointNodeFactoryResolver.Inline;
28 import org.opendaylight.yangtools.rfc8528.data.api.MountPointNodeFactoryResolver.Inline.LibraryContext;
29 import org.opendaylight.yangtools.rfc8528.data.api.MountPointNodeFactoryResolver.SharedSchema;
30 import org.opendaylight.yangtools.rfc8528.data.api.MountPointStreamWriter;
31 import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
36 import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * YANG Schema Mount-supported data attached to either a {@code list} item or a {@code container}.
42  */
43 @Beta
44 public final class MountPointData extends AbstractIdentifiable<MountPointIdentifier> {
45     private static final Logger LOG = LoggerFactory.getLogger(MountPointData.class);
46
47     private final Map<ContainerName, MountPointChild> yangLib = new EnumMap<>(ContainerName.class);
48     private final List<MountPointChild> children = new ArrayList<>();
49
50     private MountPointChild schemaMounts;
51
52     MountPointData(final QName label) {
53         super(MountPointIdentifier.of(label));
54     }
55
56     public void setContainer(final @NonNull ContainerName containerName, final @NonNull MountPointChild data) {
57         final MountPointChild prev = yangLib.putIfAbsent(containerName, requireNonNull(data));
58         checkState(prev == null, "Attempted to duplicate container %s data %s with %s", containerName, prev, data);
59         addChild(data);
60     }
61
62     public void setSchemaMounts(final @NonNull MountPointChild data) {
63         checkState(schemaMounts == null, "Attempted to reset schema-mounts from %s to %s", schemaMounts, data);
64         schemaMounts = requireNonNull(data);
65         addChild(data);
66     }
67
68     public void addChild(final @NonNull MountPointChild data) {
69         children.add(requireNonNull(data));
70     }
71
72     void write(final @NonNull NormalizedNodeStreamWriter writer) throws IOException {
73         final MountPointStreamWriter mountWriter = writer.getExtensions().getInstance(MountPointStreamWriter.class);
74         if (mountWriter == null) {
75             LOG.debug("Writer {} does not support mount points, ignoring data in {}", writer, getIdentifier());
76             return;
77         }
78
79         final Optional<MountPointNodeFactoryResolver> optResolver = mountWriter.findMountPoint(getIdentifier());
80         if (!optResolver.isPresent()) {
81             LOG.debug("Mount point for {} is not present, ignoring it", getIdentifier());
82             return;
83         }
84
85         final MountPointNodeFactoryResolver resolver = optResolver.get();
86         if (resolver instanceof SharedSchema) {
87             writeTo(mountWriter, ((SharedSchema) resolver).getSchema());
88         } else if (resolver instanceof Inline) {
89             writeInline(mountWriter, (Inline) resolver);
90         } else {
91             throw new IOException("Unhandled resolver " + resolver);
92         }
93     }
94
95     private void writeInline(final @NonNull MountPointStreamWriter mountWriter, final Inline resolver)
96             throws IOException {
97         for (Entry<ContainerName, MountPointChild> entry : yangLib.entrySet()) {
98             final Optional<LibraryContext> optLibContext = resolver.findSchemaForLibrary(entry.getKey());
99             if (!optLibContext.isPresent()) {
100                 LOG.debug("YANG Library context for mount point {} container {} not found", getIdentifier(),
101                     entry.getKey());
102                 continue;
103             }
104
105             final LibraryContext libContext = optLibContext.get();
106             final NormalizedNode<?, ?> data = entry.getValue().normalizeTo(libContext.getLibraryContainerSchema());
107             if (!(data instanceof ContainerNode)) {
108                 throw new IOException("Invalid non-container " + data);
109             }
110
111             final MountPointNodeFactory factory;
112             try {
113                 factory = libContext.bindTo((ContainerNode) data);
114             } catch (YangParserException e) {
115                 throw new IOException("Failed to assemble context for " + data, e);
116             }
117
118             writeTo(mountWriter, factory);
119             return;
120         }
121
122         LOG.warn("Failed to create a dynamic context for mount point {}, ignoring its data", getIdentifier());
123     }
124
125     private void writeTo(final @NonNull MountPointStreamWriter mountWriter,
126             final @NonNull MountPointNodeFactory mountMeta) throws IOException {
127         try (NormalizedNodeStreamWriter writer = mountWriter.startMountPoint(mountMeta)) {
128             for (MountPointChild child : children) {
129                 child.writeTo(writer, mountMeta.getSchemaContext());
130             }
131         }
132     }
133 }