Bump odlparent to 10.0.0
[yangtools.git] / data / yang-data-tree-ri / src / main / java / org / opendaylight / yangtools / yang / data / tree / impl / UniqueValidator.java
1 /*
2  * Copyright (c) 2020 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.tree.impl;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static com.google.common.base.Verify.verify;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.base.MoreObjects;
15 import com.google.common.collect.Collections2;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.collect.ImmutableSet;
18 import com.google.common.collect.Maps;
19 import java.util.Collections;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.yangtools.concepts.Immutable;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
29 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
31 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Descendant;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * A validator for a single {@code unique} constraint. This class is further specialized for single- and
37  * multiple-constraint implementations.
38  *
39  * <p>
40  * The basic idea is that for each list entry there is a corresponding value vector of one or more values, each
41  * corresponding to one component of the {@code unique} constraint.
42  */
43 abstract class UniqueValidator<T> implements Immutable {
44     private static final class One extends UniqueValidator<Object> {
45         One(final List<NodeIdentifier> path) {
46             super(encodePath(path));
47         }
48
49         @Override
50         Object extractValues(final Map<List<NodeIdentifier>, Object> valueCache, final DataContainerNode data) {
51             return extractValue(valueCache, data, decodePath(descendants));
52         }
53
54         @Override
55         Map<Descendant, @Nullable Object> indexValues(final Object values) {
56             return Collections.singletonMap(decodeDescendant(descendants), values);
57         }
58     }
59
60     private static final class Many extends UniqueValidator<Set<Object>> {
61         Many(final List<List<NodeIdentifier>> descendantPaths) {
62             super(descendantPaths.stream().map(UniqueValidator::encodePath).collect(ImmutableSet.toImmutableSet()));
63         }
64
65         @Override
66         UniqueValues extractValues(final Map<List<NodeIdentifier>, Object> valueCache, final DataContainerNode data) {
67             return descendants.stream()
68                 .map(obj -> extractValue(valueCache, data, decodePath(obj)))
69                 .collect(UniqueValues.COLLECTOR);
70         }
71
72         @Override
73         Map<Descendant, @Nullable Object> indexValues(final Object values) {
74             final Map<Descendant, @Nullable Object> index = Maps.newHashMapWithExpectedSize(descendants.size());
75             final Iterator<?> it = ((UniqueValues) values).iterator();
76             for (Object obj : descendants) {
77                 verify(index.put(decodeDescendant(obj), it.next()) == null);
78             }
79             return index;
80         }
81     }
82
83     private static final Logger LOG = LoggerFactory.getLogger(UniqueValidator.class);
84
85     final @NonNull T descendants;
86
87     UniqueValidator(final T descendants) {
88         this.descendants = requireNonNull(descendants);
89     }
90
91     static UniqueValidator<?> of(final List<List<NodeIdentifier>> descendants) {
92         return descendants.size() == 1 ? new One(descendants.get(0)) : new Many(descendants);
93     }
94
95     /**
96      * Extract a value vector from a particular child.
97      *
98      * @param valueCache Cache of descendants already looked up
99      * @param data Root data node
100      * @return Value vector
101      */
102     abstract @Nullable Object extractValues(Map<List<NodeIdentifier>, Object> valueCache, DataContainerNode data);
103
104     /**
105      * Index a value vector by associating each value with its corresponding {@link Descendant}.
106      *
107      * @param values Value vector
108      * @return Map of Descandant/value relations
109      */
110     abstract Map<Descendant, @Nullable Object> indexValues(Object values);
111
112     @Override
113     public final String toString() {
114         return MoreObjects.toStringHelper(this).add("paths", descendants).toString();
115     }
116
117     /**
118      * Encode a path for storage. Single-element paths are squashed to their only element. The inverse operation is
119      * {@link #decodePath(Object)}.
120      *
121      * @param path Path to encode
122      * @return Encoded path.
123      */
124     private static Object encodePath(final List<NodeIdentifier> path) {
125         return path.size() == 1 ? path.get(0) : ImmutableList.copyOf(path);
126     }
127
128     /**
129      * Decode a path from storage. This is the inverse operation to {@link #encodePath(List)}.
130      *
131      * @param obj Encoded path
132      * @return Decoded path
133      */
134     private static @NonNull ImmutableList<NodeIdentifier> decodePath(final Object obj) {
135         return obj instanceof NodeIdentifier ? ImmutableList.of((NodeIdentifier) obj)
136             : (ImmutableList<NodeIdentifier>) obj;
137     }
138
139     private static @NonNull Descendant decodeDescendant(final Object obj) {
140         return Descendant.of(Collections2.transform(decodePath(obj), NodeIdentifier::getNodeType));
141     }
142
143     /**
144      * Extract the value for a single descendant.
145      *
146      * @param valueCache Cache of descendants already looked up
147      * @param data Root data node
148      * @param path Descendant path
149      * @return Value for the descendant
150      */
151     private static @Nullable Object extractValue(final Map<List<NodeIdentifier>, Object> valueCache,
152             final DataContainerNode data, final List<NodeIdentifier> path) {
153         return valueCache.computeIfAbsent(path, key -> extractValue(data, key));
154     }
155
156     /**
157      * Extract the value for a single descendant.
158      *
159      * @param data Root data node
160      * @param path Descendant path
161      * @return Value for the descendant
162      */
163     private static @Nullable Object extractValue(final DataContainerNode data, final List<NodeIdentifier> path) {
164         DataContainerNode current = data;
165         final Iterator<NodeIdentifier> it = path.iterator();
166         while (true) {
167             final NodeIdentifier step = it.next();
168             final DataContainerChild next = current.childByArg(step);
169             if (next == null) {
170                 return null;
171             }
172
173             if (!it.hasNext()) {
174                 checkState(next instanceof LeafNode, "Unexpected node %s at %s", next, path);
175                 final Object value = next.body();
176                 LOG.trace("Resolved {} to value {}", path, value);
177                 return value;
178             }
179
180             checkState(next instanceof DataContainerNode, "Unexpected node %s in %s", next, path);
181             current = (DataContainerNode) next;
182         }
183     }
184 }