Fix license header violations in yang-data-impl
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / tree / MinMaxElementsValidation.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
9 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
10
11
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
15 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
16 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
17 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
18 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
19 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
22 import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 final class MinMaxElementsValidation extends SchemaAwareApplyOperation {
28
29     private static final Logger LOG = LoggerFactory.getLogger(MinMaxElementsValidation.class);
30     private final SchemaAwareApplyOperation delegate;
31     private final Integer minElements;
32     private final Integer maxElements;
33
34     private MinMaxElementsValidation(final SchemaAwareApplyOperation delegate, final Integer minElements,
35             final Integer maxElements) {
36         this.delegate = Preconditions.checkNotNull(delegate);
37         this.minElements = minElements;
38         this.maxElements = maxElements;
39     }
40
41     static SchemaAwareApplyOperation from(final SchemaAwareApplyOperation delegate, final DataSchemaNode schema) {
42         final ConstraintDefinition constraints = schema.getConstraints();
43         if (constraints == null || (constraints.getMinElements() == null && constraints.getMaxElements() == null)) {
44             return delegate;
45         }
46         return new MinMaxElementsValidation(delegate, constraints.getMinElements(), constraints.getMaxElements());
47
48     }
49
50     private static int findChildrenBefore(final Optional<TreeNode> current) {
51         if (current.isPresent()) {
52             return numOfChildrenFromValue(current.get().getData());
53         } else {
54             return 0;
55         }
56     }
57
58     private static int findChildrenAfter(final ModifiedNode modification) {
59         if (modification.getWrittenValue() != null) {
60             return numOfChildrenFromValue(modification.getWrittenValue());
61         } else {
62             return 0;
63         }
64     }
65
66     private void checkMinMaxElements(final YangInstanceIdentifier path, final NodeModification nodeMod,
67             final Optional<TreeNode> current) throws DataValidationFailedException {
68         if (!(nodeMod instanceof ModifiedNode)) {
69             LOG.debug("Could not validate {}, does not implement expected class {}", nodeMod, ModifiedNode.class);
70             return;
71         }
72         final ModifiedNode modification = (ModifiedNode) nodeMod;
73
74         final int childrenBefore = findChildrenBefore(current);
75
76         final int childrenAfter = findChildrenAfter(modification);
77
78         final int childrenTotal = childrenBefore + childrenAfter + numOfChildrenFromChildMods(modification, current);
79         if (minElements != null && minElements > childrenTotal) {
80             throw new DataValidationFailedException(path, String.format(
81                     "%s does not have enough elements (%s), needs at least %s", modification.getIdentifier(),
82                     childrenTotal, minElements));
83         }
84         if (maxElements != null && maxElements < childrenTotal) {
85             throw new DataValidationFailedException(path, String.format(
86                     "%s has too many elements (%s), can have at most %s", modification.getIdentifier(), childrenTotal,
87                     maxElements));
88         }
89     }
90
91     private static int numOfChildrenFromValue(final NormalizedNode<?, ?> value) {
92         if (value instanceof NormalizedNodeContainer) {
93             return ((NormalizedNodeContainer<?, ?, ?>) value).getValue().size();
94         } else if (value instanceof UnkeyedListNode) {
95             return ((UnkeyedListNode) value).getSize();
96         }
97
98         throw new IllegalArgumentException(String.format(
99                 "Unexpected type '%s', expected types are NormalizedNodeContainer and UnkeyedListNode",
100                 value.getClass()));
101     }
102
103     private static int numOfChildrenFromChildMods(final ModifiedNode modification, final Optional<TreeNode> current) {
104         int result = 0;
105         for (final ModifiedNode modChild : modification.getChildren()) {
106             switch (modChild.getOperation()) {
107                 case WRITE:
108                     result++;
109                     break;
110                 case MERGE:
111                     if (!current.isPresent()) {
112                         result++;
113                     }
114                     break;
115                 case DELETE:
116                     result--;
117                     break;
118                 case NONE:
119                 case TOUCH:
120                     // NOOP
121                     break;
122                 default:
123                     throw new IllegalArgumentException("Unsupported operation type: " + modChild.getOperation());
124             }
125         }
126         return result;
127     }
128
129     @Override
130     protected void checkTouchApplicable(final YangInstanceIdentifier path, final NodeModification modification,
131             final Optional<TreeNode> current) throws DataValidationFailedException {
132         delegate.checkTouchApplicable(path, modification, current);
133         checkMinMaxElements(path, modification, current);
134     }
135
136     @Override
137     protected void checkMergeApplicable(final YangInstanceIdentifier path, final NodeModification modification,
138             final Optional<TreeNode> current) throws DataValidationFailedException {
139         delegate.checkMergeApplicable(path, modification, current);
140         checkMinMaxElements(path, modification, current);
141     }
142
143     @Override
144     protected void checkWriteApplicable(final YangInstanceIdentifier path, final NodeModification modification,
145             final Optional<TreeNode> current) throws DataValidationFailedException {
146         delegate.checkWriteApplicable(path, modification, current);
147         checkMinMaxElements(path, modification, current);
148     }
149
150
151     @Override
152     public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
153         return delegate.getChild(child);
154     }
155
156     @Override
157     protected void verifyStructure(final NormalizedNode<?, ?> modification, final boolean verifyChildren) {
158         delegate.verifyStructure(modification, verifyChildren);
159     }
160
161     @Override
162     protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
163         return delegate.applyMerge(modification, currentMeta, version);
164     }
165
166     @Override
167     protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
168         return delegate.applyTouch(modification, currentMeta, version);
169     }
170
171     @Override
172     protected TreeNode applyWrite(final ModifiedNode modification, final Optional<TreeNode> currentMeta,
173             final Version version) {
174         return delegate.applyWrite(modification, currentMeta, version);
175     }
176
177     @Override
178     protected ChildTrackingPolicy getChildPolicy() {
179         return delegate.getChildPolicy();
180     }
181 }