8e271e69cb4bd833732bcc91bdd04fbb04aa34dd
[yangtools.git] / yang / yang-data-api / src / main / java / org / opendaylight / yangtools / yang / data / api / schema / tree / DataTreeCandidateNodes.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 package org.opendaylight.yangtools.yang.data.api.schema.tree;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import java.util.Collection;
13 import java.util.Iterator;
14 import javax.annotation.Nonnull;
15 import javax.annotation.Nullable;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
18 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
19
20 @Beta
21 public final class DataTreeCandidateNodes {
22     private DataTreeCandidateNodes() {
23         throw new UnsupportedOperationException();
24     }
25
26     /**
27      * Return an empty {@link DataTreeCandidateNode} identified by specified {@link PathArgument}.
28      * @param identifier Node identifier
29      * @return An empty DataTreeCandidateNode
30      */
31     public static DataTreeCandidateNode empty(final PathArgument identifier) {
32         return new EmptyDataTreeCandidateNode(identifier);
33     }
34
35     public static DataTreeCandidateNode fromNormalizedNode(final NormalizedNode<?, ?> node) {
36         return new NormalizedNodeDataTreeCandidateNode(node);
37     }
38
39     /**
40      * Applies the {@code node} to the {@code cursor}, note that if the top node of (@code node} is RootNode
41      * you need to use {@link #applyRootedNodeToCursor(DataTreeModificationCursor, YangInstanceIdentifier,
42      * DataTreeCandidateNode) applyRootedNodeToCursor} method that works with rooted node candidates.
43      *
44      * @param cursor cursor from the modification we want to apply the {@code node} to
45      * @param node candidate tree to apply
46      */
47     public static void applyToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) {
48         switch (node.getModificationType()) {
49             case DELETE:
50                 cursor.delete(node.getIdentifier());
51                 break;
52             case SUBTREE_MODIFIED:
53                 cursor.enter(node.getIdentifier());
54                 AbstractNodeIterator iterator = new ExitingNodeIterator(null, node.getChildNodes().iterator());
55                 do {
56                     iterator = iterator.next(cursor);
57                 } while (iterator != null);
58                 break;
59             case UNMODIFIED:
60                 // No-op
61                 break;
62             case WRITE:
63                 cursor.write(node.getIdentifier(), node.getDataAfter().get());
64                 break;
65             default:
66                 throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
67         }
68     }
69
70     /**
71      * Applies the {@code node} that is rooted(doesn't have an identifier) in tree A to tree B's {@code cursor}
72      * at location specified by {@code rootPath}.
73      *
74      * @param cursor cursor from the modification we want to apply the {@code node} to
75      * @param rootPath path in the {@code cursor}'s tree we want to apply to candidate to
76      * @param node candidate tree to apply
77      */
78     public static void applyRootedNodeToCursor(final DataTreeModificationCursor cursor,
79             final YangInstanceIdentifier rootPath, final DataTreeCandidateNode node) {
80         switch (node.getModificationType()) {
81             case DELETE:
82                 cursor.delete(rootPath.getLastPathArgument());
83                 break;
84             case SUBTREE_MODIFIED:
85                 cursor.enter(rootPath.getLastPathArgument());
86                 AbstractNodeIterator iterator = new ExitingNodeIterator(null, node.getChildNodes().iterator());
87                 do {
88                     iterator = iterator.next(cursor);
89                 } while (iterator != null);
90                 break;
91             case UNMODIFIED:
92                 // No-op
93                 break;
94             case WRITE:
95                 cursor.write(rootPath.getLastPathArgument(), node.getDataAfter().get());
96                 break;
97             default:
98                 throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
99         }
100     }
101
102     public static void applyRootToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) {
103         switch (node.getModificationType()) {
104             case DELETE:
105                 throw new IllegalArgumentException("Can not delete root.");
106             case WRITE:
107             case SUBTREE_MODIFIED:
108                 AbstractNodeIterator iterator = new RootNonExitingIterator(node.getChildNodes().iterator());
109                 do {
110                     iterator = iterator.next(cursor);
111                 } while (iterator != null);
112                 break;
113             case UNMODIFIED:
114                 // No-op
115                 break;
116             default:
117                 throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
118         }
119     }
120
121     private abstract static class AbstractNodeIterator {
122         private final Iterator<DataTreeCandidateNode> iterator;
123
124         AbstractNodeIterator(final Iterator<DataTreeCandidateNode> iterator) {
125             this.iterator = Preconditions.checkNotNull(iterator);
126         }
127
128         AbstractNodeIterator next(final DataTreeModificationCursor cursor) {
129             while (iterator.hasNext()) {
130                 final DataTreeCandidateNode node = iterator.next();
131                 switch (node.getModificationType()) {
132                     case DELETE:
133                         cursor.delete(node.getIdentifier());
134                         break;
135                     case APPEARED:
136                     case DISAPPEARED:
137                     case SUBTREE_MODIFIED:
138                         final Collection<DataTreeCandidateNode> children = node.getChildNodes();
139                         if (!children.isEmpty()) {
140                             cursor.enter(node.getIdentifier());
141                             return new ExitingNodeIterator(this, children.iterator());
142                         }
143                         break;
144                     case UNMODIFIED:
145                         // No-op
146                         break;
147                     case WRITE:
148                         cursor.write(node.getIdentifier(), node.getDataAfter().get());
149                         break;
150                     default:
151                         throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
152                 }
153             }
154             exitNode(cursor);
155             return getParent();
156         }
157
158         protected abstract @Nullable AbstractNodeIterator getParent();
159
160         protected abstract void exitNode(DataTreeModificationCursor cursor);
161     }
162
163     private static final class RootNonExitingIterator extends AbstractNodeIterator {
164
165         protected RootNonExitingIterator(@Nonnull final Iterator<DataTreeCandidateNode> iterator) {
166             super(iterator);
167         }
168
169         @Override
170         protected void exitNode(final DataTreeModificationCursor cursor) {
171             // Intentional noop.
172         }
173
174         @Override
175         protected AbstractNodeIterator getParent() {
176             return null;
177         }
178     }
179
180     private static final class ExitingNodeIterator extends AbstractNodeIterator {
181
182         private final AbstractNodeIterator parent;
183
184         ExitingNodeIterator(@Nullable final AbstractNodeIterator parent,
185                 @Nonnull final Iterator<DataTreeCandidateNode> iterator) {
186             super(iterator);
187             this.parent = parent;
188         }
189
190         @Override
191         protected AbstractNodeIterator getParent() {
192             return parent;
193         }
194
195         @Override
196         protected void exitNode(final DataTreeModificationCursor cursor) {
197             cursor.exit();
198         }
199     }
200 }