Clean up more Sonar warnings
[yangtools.git] / data / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / 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.yang.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.collect.ImmutableSet;
15 import java.util.stream.Collectors;
16 import org.eclipse.jdt.annotation.NonNullByDefault;
17 import org.opendaylight.yangtools.concepts.Immutable;
18 import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants;
19 import org.opendaylight.yangtools.yang.common.MountPointLabel;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.common.QNameModule;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
26 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
29 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContextFactory;
30 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * Abstract base class for MountPointContextFactory implementations, which can process RFC8525 mount point definitions.
36  */
37 @Beta
38 @NonNullByDefault
39 public abstract class AbstractMountPointContextFactory extends AbstractDynamicMountPointContextFactory {
40     /**
41      * Definition of a MountPoint, as known to RFC8528.
42      */
43     protected record MountPointDefinition(
44             MountPointLabel label,
45             boolean config,
46             // FIXME: make this return a set of XPath expressions
47             ImmutableSet<String> parentReferences) implements Immutable {
48         protected MountPointDefinition {
49             requireNonNull(parentReferences);
50             requireNonNull(label);
51         }
52     }
53
54     private static final Logger LOG = LoggerFactory.getLogger(AbstractMountPointContextFactory.class);
55     private static final NodeIdentifier SCHEMA_MOUNTS = NodeIdentifier.create(
56         QName.create(SchemaMountConstants.RFC8528_MODULE, "schema-mounts").intern());
57     private static final NodeIdentifier MOUNT_POINT = NodeIdentifier.create(
58         QName.create(SchemaMountConstants.RFC8528_MODULE, "mount-point").intern());
59     private static final NodeIdentifier CONFIG = NodeIdentifier.create(
60         QName.create(SchemaMountConstants.RFC8528_MODULE, "config").intern());
61     private static final NodeIdentifier MODULE = NodeIdentifier.create(
62         QName.create(SchemaMountConstants.RFC8528_MODULE, "module").intern());
63     private static final NodeIdentifier LABEL = NodeIdentifier.create(
64         QName.create(SchemaMountConstants.RFC8528_MODULE, "label").intern());
65     private static final NodeIdentifier SCHEMA_REF = NodeIdentifier.create(
66         QName.create(SchemaMountConstants.RFC8528_MODULE, "schema-ref").intern());
67     private static final NodeIdentifier INLINE = NodeIdentifier.create(
68         QName.create(SchemaMountConstants.RFC8528_MODULE, "inline").intern());
69     private static final NodeIdentifier SHARED_SCHEMA = NodeIdentifier.create(
70         QName.create(SchemaMountConstants.RFC8528_MODULE, "shared-schema").intern());
71     private static final NodeIdentifier PARENT_REFERENCE = NodeIdentifier.create(
72         QName.create(SchemaMountConstants.RFC8528_MODULE, "parent-reference").intern());
73
74     protected AbstractMountPointContextFactory(final MountPointLabel label) {
75         super(label);
76     }
77
78     @Override
79     protected final MountPointContext createMountPointContext(final EffectiveModelContext schemaContext,
80             final ContainerNode mountData) {
81         checkArgument(SCHEMA_MOUNTS.equals(mountData.name()), "Unexpected top-level container %s", mountData);
82
83         final var mountPoint = mountData.childByArg(MOUNT_POINT);
84         if (mountPoint == null) {
85             LOG.debug("mount-point list not present in {}", mountData);
86             return MountPointContext.of(schemaContext);
87         }
88         if (!(mountPoint instanceof MapNode mapMountPoint)) {
89             throw new IllegalArgumentException("mount-point list " + mountPoint + " is not a MapNode");
90         }
91
92         return new ImmutableMountPointContext(schemaContext, mapMountPoint.body().stream().map(entry -> {
93             final String moduleName = entry.findChildByArg(MODULE).map(mod -> {
94                 checkArgument(mod instanceof LeafNode, "Unexpected module leaf %s", mod);
95                 final Object value = mod.body();
96                 checkArgument(value instanceof String, "Unexpected module leaf value %s", value);
97                 return (String) value;
98             }).orElseThrow(() -> new IllegalArgumentException("Mount module missing in " + entry));
99             final var it = schemaContext.findModuleStatements(moduleName).iterator();
100             checkArgument(it.hasNext(), "Failed to find a module named %s", moduleName);
101             final QNameModule module = it.next().localQNameModule();
102
103             return new MountPointDefinition(
104                 new MountPointLabel(QName.create(module, entry.findChildByArg(LABEL).map(lbl -> {
105                     checkArgument(lbl instanceof LeafNode, "Unexpected label leaf %s", lbl);
106                     final Object value = lbl.body();
107                     checkArgument(value instanceof String, "Unexpected label leaf value %s", value);
108                     return (String) value;
109                 }).orElseThrow(() -> new IllegalArgumentException("Mount module missing in " + entry)))).intern(),
110                 entry.findChildByArg(CONFIG).map(cfg -> {
111                     checkArgument(cfg instanceof LeafNode, "Unexpected config leaf %s", cfg);
112                     final Object value = cfg.body();
113                     checkArgument(value instanceof Boolean, "Unexpected config leaf value %s", cfg);
114                     return (Boolean) value;
115                 }).orElse(Boolean.TRUE),
116                 getSchema(entry.findChildByArg(SCHEMA_REF)
117                     .orElseThrow(() -> new IllegalArgumentException("Missing schema-ref choice in " + entry))));
118         }).collect(Collectors.toList()), this::createContextFactory);
119     }
120
121     private static ImmutableSet<String> getSchema(final DataContainerChild child) {
122         checkArgument(child instanceof ChoiceNode, "Unexpected schema-ref choice %s", child);
123         final ChoiceNode schemaRef = (ChoiceNode) child;
124
125         return schemaRef.findChildByArg(SHARED_SCHEMA).map(sharedSchema -> {
126             checkArgument(sharedSchema instanceof ContainerNode, "Unexpected shared-schema container %s", sharedSchema);
127             return ((ContainerNode) sharedSchema).findChildByArg(PARENT_REFERENCE)
128                 // FIXME: 7.0.0: parse XPaths. Do we have enough context for that?
129                 .map(parentRef -> ImmutableSet.<String>of())
130                 .orElseGet(ImmutableSet::of);
131         }).orElseGet(() -> {
132             checkArgument(schemaRef.findChildByArg(INLINE).isPresent(), "Unhandled schema-ref type in %s", schemaRef);
133             return ImmutableSet.of();
134         });
135     }
136
137     /**
138      * Create a fresh {@link MountPointContextFactory} for a nested {@link MountPointDefinition}.
139      *
140      * @param mountPoint Mount point definition
141      * @return A new factory, dealing with mount points nested within the mount point.
142      */
143     protected abstract MountPointContextFactory createContextFactory(MountPointDefinition mountPoint);
144 }