Clean up more Sonar warnings
[yangtools.git] / data / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / DataSchemaContextTree.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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 java.util.Objects.requireNonNull;
11
12 import com.google.common.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import java.util.Optional;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.yangtools.concepts.CheckedValue;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
21 import org.opendaylight.yangtools.yang.data.util.DataSchemaContext.Composite;
22 import org.opendaylight.yangtools.yang.data.util.impl.context.ContainerContext;
23 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
24 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
25
26 /**
27  * Semantic tree binding a {@link EffectiveModelContext} to a {@link NormalizedNode} tree. Since the layout of the
28  * schema and data has differences, the mapping is not trivial -- which is where this class comes in.
29  */
30 public final class DataSchemaContextTree {
31     public record NodeAndStack(@NonNull DataSchemaContext node, @NonNull SchemaInferenceStack stack) {
32         public NodeAndStack(final @NonNull DataSchemaContext node, final @NonNull SchemaInferenceStack stack) {
33             this.node = requireNonNull(node);
34             this.stack = requireNonNull(stack);
35         }
36     }
37
38     private static final LoadingCache<EffectiveModelContext, @NonNull DataSchemaContextTree> TREES =
39         CacheBuilder.newBuilder().weakKeys().weakValues().build(new CacheLoader<>() {
40             @Override
41             public DataSchemaContextTree load(final EffectiveModelContext key) {
42                 return new DataSchemaContextTree(key);
43             }
44         });
45
46     private final @NonNull EffectiveModelContext modelContext;
47     private final @NonNull ContainerContext root;
48
49     private DataSchemaContextTree(final EffectiveModelContext modelContext) {
50         this.modelContext = requireNonNull(modelContext);
51         root = new ContainerContext(modelContext);
52     }
53
54     public static @NonNull DataSchemaContextTree from(final @NonNull EffectiveModelContext ctx) {
55         return TREES.getUnchecked(ctx);
56     }
57
58     /**
59      * Return the {@link EffectiveModelContext} used to derive this tree.
60      *
61      * @return the {@link EffectiveModelContext} used to derive this tree
62      */
63     public @NonNull EffectiveModelContext modelContext() {
64         return modelContext;
65     }
66
67     /**
68      * Find a child node as identified by an absolute {@link YangInstanceIdentifier}.
69      *
70      * @param path Path towards the child node
71      * @return Child node if present, or {@code null} when corresponding child is not found.
72      * @throws NullPointerException if {@code path} is {@code null}
73      */
74     public @Nullable DataSchemaContext childByPath(final @NonNull YangInstanceIdentifier path) {
75         return root.childByPath(path);
76     }
77
78     /**
79      * Find a child node as identified by an absolute {@link YangInstanceIdentifier}.
80      *
81      * @param path Path towards the child node
82      * @return Child node if present, or empty when corresponding child is not found.
83      * @throws NullPointerException if {@code path} is {@code null}
84      */
85     public @NonNull Optional<@NonNull DataSchemaContext> findChild(final @NonNull YangInstanceIdentifier path) {
86         // Optional.ofNullable() inline due to annotations
87         final var child = root.childByPath(path);
88         return child == null ? Optional.empty() : Optional.of(child);
89     }
90
91     /**
92      * Find a child node as identified by an absolute {@link YangInstanceIdentifier} and return it along with a suitably
93      * initialized {@link SchemaInferenceStack}.
94      *
95      * @param path Path towards the child node
96      * @return A {@link NodeAndStack}, or empty when corresponding child is not found.
97      * @throws NullPointerException if {@code path} is null
98      */
99     public @NonNull CheckedValue<@NonNull NodeAndStack, @NonNull IllegalArgumentException> enterPath(
100             final YangInstanceIdentifier path) {
101         final var stack = SchemaInferenceStack.of((EffectiveModelContext) root.dataSchemaNode());
102         DataSchemaContext node = root;
103         for (var arg : path.getPathArguments()) {
104             final var child = node instanceof Composite composite ? composite.enterChild(stack, arg) : null;
105             if (child == null) {
106                 return CheckedValue.ofException(new IllegalArgumentException("Failed to find " + arg + " in " + node));
107             }
108             node = child;
109         }
110
111         return CheckedValue.ofValue(new NodeAndStack(node, stack));
112     }
113
114     public DataSchemaContext.@NonNull Composite getRoot() {
115         return root;
116     }
117 }