Merge branch 'master' of ../controller
[yangtools.git] / yang / rfc8528-data-util / src / main / java / org / opendaylight / yangtools / rcf8528 / data / util / AbstractMountPointContextFactory.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.rcf8528.data.util;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.MoreObjects.ToStringHelper;
15 import com.google.common.collect.ImmutableSet;
16 import java.util.Iterator;
17 import java.util.Optional;
18 import java.util.stream.Collectors;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
21 import org.opendaylight.yangtools.concepts.Immutable;
22 import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
23 import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
24 import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
25 import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.QNameModule;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
32 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
34 import org.opendaylight.yangtools.yang.model.api.Module;
35 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * Abstract base class for MountPointContextFactory implementations, which can process RFC8525 mount point definitions.
41  */
42 @Beta
43 @NonNullByDefault
44 public abstract class AbstractMountPointContextFactory extends AbstractDynamicMountPointContextFactory {
45     /**
46      * Definition of a MountPoint, as known to RFC8528.
47      */
48     protected static final class MountPointDefinition extends AbstractIdentifiable<MountPointIdentifier>
49             implements Immutable {
50         private final ImmutableSet<String> parentReferences;
51         private final boolean config;
52
53         MountPointDefinition(final MountPointIdentifier identifier, final boolean config,
54                 final ImmutableSet<String> parentReferences) {
55             super(identifier);
56             this.config = config;
57             this.parentReferences = requireNonNull(parentReferences);
58         }
59
60         public boolean getConfig() {
61             return config;
62         }
63
64         // FIXME: 5.0.0: make this return a set of XPath expressions
65         public ImmutableSet<String> getParentReferences() {
66             return parentReferences;
67         }
68
69         @Override
70         protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
71             return super.addToStringAttributes(toStringHelper)
72                     .add("config", config)
73                     .add("parentReferences", parentReferences);
74         }
75     }
76
77     private static final Logger LOG = LoggerFactory.getLogger(AbstractMountPointContextFactory.class);
78     private static final NodeIdentifier SCHEMA_MOUNTS = NodeIdentifier.create(
79         QName.create(SchemaMountConstants.RFC8528_MODULE, "schema-mounts").intern());
80     private static final NodeIdentifier MOUNT_POINT = NodeIdentifier.create(
81         QName.create(SchemaMountConstants.RFC8528_MODULE, "mount-point").intern());
82     private static final NodeIdentifier CONFIG = NodeIdentifier.create(
83         QName.create(SchemaMountConstants.RFC8528_MODULE, "config").intern());
84     private static final NodeIdentifier MODULE = NodeIdentifier.create(
85         QName.create(SchemaMountConstants.RFC8528_MODULE, "module").intern());
86     private static final NodeIdentifier LABEL = NodeIdentifier.create(
87         QName.create(SchemaMountConstants.RFC8528_MODULE, "label").intern());
88     private static final NodeIdentifier SCHEMA_REF = NodeIdentifier.create(
89         QName.create(SchemaMountConstants.RFC8528_MODULE, "schema-ref").intern());
90     private static final NodeIdentifier INLINE = NodeIdentifier.create(
91         QName.create(SchemaMountConstants.RFC8528_MODULE, "inline").intern());
92     private static final NodeIdentifier SHARED_SCHEMA = NodeIdentifier.create(
93         QName.create(SchemaMountConstants.RFC8528_MODULE, "shared-schema").intern());
94     private static final NodeIdentifier PARENT_REFERENCE = NodeIdentifier.create(
95         QName.create(SchemaMountConstants.RFC8528_MODULE, "parent-reference").intern());
96
97     protected AbstractMountPointContextFactory(final MountPointIdentifier mountId) {
98         super(mountId);
99     }
100
101     @Override
102     protected final MountPointContext createMountPointContext(final SchemaContext schemaContext,
103             final ContainerNode mountData) {
104         checkArgument(SCHEMA_MOUNTS.equals(mountData.getIdentifier()), "Unexpected top-level container %s", mountData);
105
106         final Optional<DataContainerChild<?, ?>> optMountPoint = mountData.getChild(MOUNT_POINT);
107         if (optMountPoint.isEmpty()) {
108             LOG.debug("mount-point list not present in {}", mountData);
109             return new EmptyMountPointContext(schemaContext);
110         }
111
112         final DataContainerChild<?, ?> mountPoint = optMountPoint.get();
113         checkArgument(mountPoint instanceof MapNode, "mount-point list %s is not a MapNode", mountPoint);
114
115         return new ImmutableMountPointContext(schemaContext, ((MapNode) mountPoint).getValue().stream().map(entry -> {
116             final String moduleName = entry.getChild(MODULE).map(mod -> {
117                 checkArgument(mod instanceof LeafNode, "Unexpected module leaf %s", mod);
118                 final Object value = mod.getValue();
119                 checkArgument(value instanceof String, "Unexpected module leaf value %s", value);
120                 return (String) value;
121             }).orElseThrow(() -> new IllegalArgumentException("Mount module missing in " + entry));
122             final Iterator<Module> it = schemaContext.findModules(moduleName).iterator();
123             checkArgument(it.hasNext(), "Failed to find a module named %s", moduleName);
124             final QNameModule module = it.next().getQNameModule();
125
126             return new MountPointDefinition(
127                 MountPointIdentifier.of(QName.create(module, entry.getChild(LABEL).map(lbl -> {
128                     checkArgument(lbl instanceof LeafNode, "Unexpected label leaf %s", lbl);
129                     final Object value = lbl.getValue();
130                     checkArgument(value instanceof String, "Unexpected label leaf value %s", value);
131                     return (String) value;
132                 }).orElseThrow(() -> new IllegalArgumentException("Mount module missing in " + entry)))),
133                 entry.getChild(CONFIG).map(cfg -> {
134                     checkArgument(cfg instanceof LeafNode, "Unexpected config leaf %s", cfg);
135                     final Object value = cfg.getValue();
136                     checkArgument(value instanceof Boolean, "Unexpected config leaf value %s", cfg);
137                     return (Boolean) value;
138                 }).orElse(Boolean.TRUE),
139                 getSchema(entry.getChild(SCHEMA_REF)
140                     .orElseThrow(() -> new IllegalArgumentException("Missing schema-ref choice in " + entry))));
141         }).collect(Collectors.toList()), this::createContextFactory);
142     }
143
144     private static ImmutableSet<String> getSchema(final DataContainerChild<?, ?> child) {
145         checkArgument(child instanceof ChoiceNode, "Unexpected schema-ref choice %s", child);
146         final ChoiceNode schemaRef = (ChoiceNode) child;
147
148         return schemaRef.getChild(SHARED_SCHEMA).map(sharedSchema -> {
149             checkArgument(sharedSchema instanceof ContainerNode, "Unexpected shared-schema container %s", sharedSchema);
150             return ((ContainerNode) sharedSchema).getChild(PARENT_REFERENCE).map(parentRef -> {
151                 // FIXME: decode
152                 return ImmutableSet.<String>of();
153             }).orElseGet(ImmutableSet::of);
154         }).orElseGet(() -> {
155             checkArgument(schemaRef.getChild(INLINE).isPresent(), "Unhandled schema-ref type in %s", schemaRef);
156             return ImmutableSet.of();
157         });
158     }
159
160     /**
161      * Create a fresh {@link MountPointContextFactory} for a nested {@link MountPointDefinition}.
162      *
163      * @param mountPoint Mount point definition
164      * @return A new factory, dealing with mount points nested within the mount point.
165      */
166     protected abstract MountPointContextFactory createContextFactory(MountPointDefinition mountPoint);
167 }