Refactor ModificationApplyOperation
[yangtools.git] / data / yang-data-tree-ri / src / main / java / org / opendaylight / yangtools / yang / data / tree / impl / AbstractValidation.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.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import java.util.Optional;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
17 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
18 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
19 import org.opendaylight.yangtools.yang.data.tree.impl.node.TreeNode;
20 import org.opendaylight.yangtools.yang.data.tree.impl.node.Version;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * A forwarding {@link ModificationApplyOperation}. Useful for strategies which do not deal with data layout, but rather
26  * perform additional validation.
27  */
28 abstract sealed class AbstractValidation extends ModificationApplyOperation
29         permits MinMaxElementsValidation, UniqueValidation {
30     private static final Logger LOG = LoggerFactory.getLogger(AbstractValidation.class);
31
32     private final @NonNull ModificationApplyOperation delegate;
33
34     AbstractValidation(final ModificationApplyOperation delegate) {
35         this.delegate = requireNonNull(delegate);
36     }
37
38     @Override
39     public final ModificationApplyOperation childByArg(final PathArgument arg) {
40         return delegate.childByArg(arg);
41     }
42
43     @Override
44     final ChildTrackingPolicy getChildPolicy() {
45         return delegate.getChildPolicy();
46     }
47
48     @Override
49     final void mergeIntoModifiedNode(final ModifiedNode node, final NormalizedNode value, final Version version) {
50         delegate.mergeIntoModifiedNode(node, value, version);
51     }
52
53     @Override
54     final void quickVerifyStructure(final NormalizedNode modification) {
55         delegate.quickVerifyStructure(modification);
56     }
57
58     @Override
59     final void recursivelyVerifyStructure(final NormalizedNode value) {
60         delegate.recursivelyVerifyStructure(value);
61     }
62
63     @Override
64     final Optional<? extends TreeNode> apply(final ModifiedNode modification, final TreeNode currentMeta,
65             final Version version) {
66         var validated = modification.validatedNode(this, currentMeta);
67         if (validated != null) {
68             return validated.toOptional();
69         }
70
71         // This might also mean the delegate is maintaining validation
72         if (delegate instanceof AbstractValidation) {
73             validated = modification.validatedNode(delegate, currentMeta);
74             if (validated != null) {
75                 return validated.toOptional();
76             }
77         }
78
79         // Deal with the result moving on us
80         final var ret = delegate.apply(modification, currentMeta, version);
81         ret.ifPresent(meta -> enforceOnData(meta.getData()));
82         return ret;
83     }
84
85     @Override
86     final void checkApplicable(final ModificationPath path, final NodeModification modification,
87             final TreeNode currentMeta, final Version version) throws DataValidationFailedException {
88         delegate.checkApplicable(path, modification, currentMeta, version);
89         if (!(modification instanceof ModifiedNode modified)) {
90             // FIXME: 7.0.0: turn this into a verify?
91             LOG.debug("Could not validate {}, does not implement expected class {}", modification, ModifiedNode.class);
92             return;
93         }
94
95         if (delegate instanceof AbstractValidation) {
96             checkApplicable(path, verifyNotNull(modified.validatedNode(delegate, currentMeta)).toOptional());
97             return;
98         }
99
100         // We need to actually perform the operation to deal with merge in a sane manner. We know the modification
101         // is immutable, so the result of validation will probably not change. Note we should not be checking number
102         final var applied = delegate.apply(modified, currentMeta, version);
103         checkApplicable(path, applied);
104
105         // Everything passed. We now have a snapshot of the result node, it would be too bad if we just threw it out.
106         // We know what the result of an apply operation is going to be *if* the following are kept unchanged:
107         // - the 'current' node
108         // - the effective model context (therefore, the fact this object is associated with the modification)
109         //
110         // So let's stash the result. We will pick it up during apply operation.
111         modified.setValidatedNode(this, currentMeta, applied);
112     }
113
114     private void checkApplicable(final ModificationPath path, final Optional<? extends TreeNode> applied)
115             throws DataValidationFailedException {
116         if (applied.isPresent()) {
117             // We only enforce min/max on present data and rely on MandatoryLeafEnforcer to take care of the empty case
118             enforceOnData(path, applied.orElseThrow().getData());
119         }
120     }
121
122     @Override
123     void fullVerifyStructure(final NormalizedNode modification) {
124         delegate.fullVerifyStructure(modification);
125         enforceOnData(modification);
126     }
127
128     final @NonNull ModificationApplyOperation delegate() {
129         return delegate;
130     }
131
132     abstract void enforceOnData(ModificationPath path, NormalizedNode value) throws DataValidationFailedException;
133
134     abstract void enforceOnData(@NonNull NormalizedNode data);
135
136     @Override
137     ToStringHelper addToStringAttributes(final ToStringHelper helper) {
138         return helper.add("delegate", delegate);
139     }
140 }