Clean up TerminalDataTreeCandidateNode
[yangtools.git] / data / yang-data-tree-spi / src / main / java / org / opendaylight / yangtools / yang / data / tree / spi / TerminalDataTreeCandidateNode.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.spi;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.Optional;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
19 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
20 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
21 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
22
23 // Non-final for mocking
24 class TerminalDataTreeCandidateNode implements DataTreeCandidateNode {
25     private ModificationType modificationType;
26     private final PathArgument identifier;
27     private final NormalizedNode before;
28     private NormalizedNode after;
29     private final HashMap<PathArgument, TerminalDataTreeCandidateNode> childNodes = new HashMap<>();
30     private TerminalDataTreeCandidateNode parentNode;
31
32     TerminalDataTreeCandidateNode(final PathArgument identifier, final NormalizedNode data,
33                                   final TerminalDataTreeCandidateNode parentNode) {
34         this(identifier, data);
35         this.parentNode = requireNonNull(parentNode);
36     }
37
38     TerminalDataTreeCandidateNode(final PathArgument identifier, final NormalizedNode data) {
39         this(identifier, ModificationType.UNMODIFIED, data, data);
40     }
41
42     TerminalDataTreeCandidateNode(final PathArgument identifier, final ModificationType modificationType,
43                                   final NormalizedNode before, final NormalizedNode after) {
44         this.modificationType = modificationType;
45         this.identifier = identifier;
46         this.before = before;
47         this.after = after;
48     }
49
50     @Override
51     public PathArgument getIdentifier() {
52         return identifier;
53     }
54
55     @Override
56     public Collection<DataTreeCandidateNode> getChildNodes() {
57         return Collections.unmodifiableCollection(childNodes.values());
58     }
59
60     @Override
61     public Optional<DataTreeCandidateNode> getModifiedChild(final PathArgument childIdentifier) {
62         return Optional.ofNullable(childNodes.get(identifier));
63     }
64
65     @Override
66     public ModificationType getModificationType() {
67         return modificationType;
68     }
69
70     @Override
71     public Optional<NormalizedNode> getDataAfter() {
72         return Optional.ofNullable(after);
73     }
74
75     @NonNull Optional<NormalizedNode> getDataAfter(final PathArgument id) {
76         return getNode(id).flatMap(TerminalDataTreeCandidateNode::getDataAfter);
77     }
78
79     @Override
80     public Optional<NormalizedNode> getDataBefore() {
81         return Optional.ofNullable(before);
82     }
83
84     @NonNull Optional<NormalizedNode> getDataBefore(final PathArgument id) {
85         return getNode(id).flatMap(TerminalDataTreeCandidateNode::getDataBefore);
86     }
87
88     void setAfter(final NormalizedNode after) {
89         this.after = after;
90     }
91
92     void addChildNode(final TerminalDataTreeCandidateNode node) {
93         childNodes.put(node.getIdentifier(), node);
94     }
95
96     void setModification(final PathArgument id, final ModificationType modification) {
97         getNode(id)
98             .orElseThrow(() -> new IllegalArgumentException("No node with " + id + " id was found"))
99             .setModification(modification);
100     }
101
102     private void setModification(final ModificationType modification) {
103         modificationType = modification;
104     }
105
106     ModificationType getModification(final PathArgument id) {
107         return getNode(id).map(TerminalDataTreeCandidateNode::getModificationType).orElse(ModificationType.UNMODIFIED);
108     }
109
110     void deleteNode(final PathArgument id) {
111         if (id != null) {
112             getNode(id).orElseThrow(() -> new IllegalArgumentException("No node with " + id + " id was found"))
113                 .parentNode.deleteChild(id);
114         } else {
115             modificationType = ModificationType.UNMODIFIED;
116         }
117     }
118
119     private void deleteChild(final PathArgument id) {
120         childNodes.remove(id);
121     }
122
123     @NonNull Optional<TerminalDataTreeCandidateNode> getNode(final PathArgument id) {
124         if (id == null) {
125             return Optional.of(this);
126         }
127         if (childNodes.isEmpty()) {
128             return Optional.empty();
129         }
130         if (childNodes.containsKey(id)) {
131             return Optional.ofNullable(childNodes.get(id));
132         }
133         return findNode(id);
134     }
135
136     void setData(final PathArgument id, final NormalizedNode node) {
137         getNode(id).orElseThrow().setAfter(node);
138     }
139
140     private @NonNull Optional<TerminalDataTreeCandidateNode> findNode(final PathArgument id) {
141         Collection<HashMap<PathArgument, TerminalDataTreeCandidateNode>> nodes = new HashSet<>();
142         childNodes.forEach((childIdentifier, childNode) -> {
143             nodes.add(childNode.childNodes);
144         });
145         return findNode(nodes, id);
146     }
147
148     private @NonNull Optional<TerminalDataTreeCandidateNode> findNode(
149             final Collection<HashMap<PathArgument, TerminalDataTreeCandidateNode>> nodes, final PathArgument id) {
150         if (nodes.isEmpty()) {
151             return Optional.empty();
152         }
153         Collection<HashMap<PathArgument, TerminalDataTreeCandidateNode>> nextNodes = new HashSet<>();
154         for (HashMap<PathArgument, TerminalDataTreeCandidateNode> map : nodes) {
155             if (map.containsKey(id)) {
156                 return Optional.ofNullable(map.get(id));
157             }
158             map.forEach((childIdentifier, childNode) -> {
159                 nextNodes.add(childNode.childNodes);
160             });
161         }
162         return findNode(nextNodes, id);
163     }
164 }