2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.tree.impl;
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;
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;
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;
36 * A validator for a single {@code unique} constraint. This class is further specialized for single- and
37 * multiple-constraint implementations.
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.
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));
50 Object extractValues(final Map<List<NodeIdentifier>, Object> valueCache, final DataContainerNode data) {
51 return extractValue(valueCache, data, decodePath(descendants));
55 Map<Descendant, @Nullable Object> indexValues(final Object values) {
56 return Collections.singletonMap(decodeDescendant(descendants), values);
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()));
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);
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);
83 private static final Logger LOG = LoggerFactory.getLogger(UniqueValidator.class);
85 final @NonNull T descendants;
87 UniqueValidator(final T descendants) {
88 this.descendants = requireNonNull(descendants);
91 static UniqueValidator<?> of(final List<List<NodeIdentifier>> descendants) {
92 return descendants.size() == 1 ? new One(descendants.get(0)) : new Many(descendants);
96 * Extract a value vector from a particular child.
98 * @param valueCache Cache of descendants already looked up
99 * @param data Root data node
100 * @return Value vector
102 abstract @Nullable Object extractValues(Map<List<NodeIdentifier>, Object> valueCache, DataContainerNode data);
105 * Index a value vector by associating each value with its corresponding {@link Descendant}.
107 * @param values Value vector
108 * @return Map of Descandant/value relations
110 abstract Map<Descendant, @Nullable Object> indexValues(Object values);
113 public final String toString() {
114 return MoreObjects.toStringHelper(this).add("paths", descendants).toString();
118 * Encode a path for storage. Single-element paths are squashed to their only element. The inverse operation is
119 * {@link #decodePath(Object)}.
121 * @param path Path to encode
122 * @return Encoded path.
124 private static Object encodePath(final List<NodeIdentifier> path) {
125 return path.size() == 1 ? path.get(0) : ImmutableList.copyOf(path);
129 * Decode a path from storage. This is the inverse operation to {@link #encodePath(List)}.
131 * @param obj Encoded path
132 * @return Decoded path
134 private static @NonNull ImmutableList<NodeIdentifier> decodePath(final Object obj) {
135 return obj instanceof NodeIdentifier ? ImmutableList.of((NodeIdentifier) obj)
136 : (ImmutableList<NodeIdentifier>) obj;
139 private static @NonNull Descendant decodeDescendant(final Object obj) {
140 return Descendant.of(Collections2.transform(decodePath(obj), NodeIdentifier::getNodeType));
144 * Extract the value for a single descendant.
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
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));
157 * Extract the value for a single descendant.
159 * @param data Root data node
160 * @param path Descendant path
161 * @return Value for the descendant
163 private static @Nullable Object extractValue(final DataContainerNode data, final List<NodeIdentifier> path) {
164 DataContainerNode current = data;
165 final Iterator<NodeIdentifier> it = path.iterator();
167 final NodeIdentifier step = it.next();
168 final DataContainerChild next = current.childByArg(step);
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);
180 checkState(next instanceof DataContainerNode, "Unexpected node %s in %s", next, path);
181 current = (DataContainerNode) next;