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