Rework NormalizedNodeStreamWriter
[yangtools.git] / yang / yang-data-api / src / main / java / org / opendaylight / yangtools / yang / data / api / StackedYangInstanceIdentifier.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;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.collect.Iterables;
15 import com.google.common.collect.Lists;
16 import java.io.IOException;
17 import java.io.ObjectInputStream;
18 import java.io.ObjectOutputStream;
19 import java.lang.reflect.Field;
20 import java.security.AccessController;
21 import java.security.PrivilegedAction;
22 import java.util.ArrayList;
23 import java.util.List;
24 import javax.annotation.Nonnull;
25
26 final class StackedYangInstanceIdentifier extends YangInstanceIdentifier implements Cloneable {
27     private static final long serialVersionUID = 1L;
28     private static final Field PARENT_FIELD;
29
30     static {
31         final Field f;
32         try {
33             f = StackedYangInstanceIdentifier.class.getDeclaredField("parent");
34         } catch (NoSuchFieldException | SecurityException e) {
35             throw new ExceptionInInitializerError(e);
36         }
37
38         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
39             f.setAccessible(true);
40             return null;
41         });
42
43         PARENT_FIELD = f;
44     }
45
46     private final YangInstanceIdentifier parent;
47     private final PathArgument pathArgument;
48
49     private transient volatile StackedPathArguments pathArguments;
50     private transient volatile StackedReversePathArguments reversePathArguments;
51
52     StackedYangInstanceIdentifier(final YangInstanceIdentifier parent, final PathArgument pathArgument,
53             final int hash) {
54         super(hash);
55         this.parent = requireNonNull(parent);
56         this.pathArgument = requireNonNull(pathArgument);
57     }
58
59     @Override
60     public StackedYangInstanceIdentifier clone() {
61         try {
62             return (StackedYangInstanceIdentifier) super.clone();
63         } catch (CloneNotSupportedException e) {
64             throw new IllegalStateException("clone() should be supported", e);
65         }
66     }
67
68     @Override
69     public YangInstanceIdentifier getParent() {
70         return parent;
71     }
72
73     @Nonnull
74     @Override
75     public YangInstanceIdentifier getAncestor(final int depth) {
76         checkArgument(depth >= 0, "Steps cannot be negative");
77
78         // Calculate how far up our FixedYangInstanceIdentifier ancestor is
79         int stackedDepth = 1;
80         YangInstanceIdentifier wlk = getParent();
81         while (wlk instanceof StackedYangInstanceIdentifier) {
82             wlk = wlk.getParent();
83             stackedDepth++;
84         }
85
86         // Guaranteed to come from FixedYangInstanceIdentifier
87         final int fixedDepth = wlk.getPathArguments().size();
88         if (fixedDepth >= depth) {
89             return wlk.getAncestor(depth);
90         }
91
92         // Calculate our depth and check argument
93         final int ourDepth = stackedDepth + fixedDepth;
94         checkArgument(depth <= ourDepth, "Depth %s exceeds maximum depth %s", depth, ourDepth);
95
96         // Requested depth is covered by the stack, traverse up for specified number of steps
97         final int toWalk = ourDepth - depth;
98         YangInstanceIdentifier result = this;
99         for (int i = 0; i < toWalk; ++i) {
100             result = result.getParent();
101         }
102
103         return result;
104     }
105
106     @Override
107     public boolean isEmpty() {
108         return false;
109     }
110
111     @Override
112     public List<PathArgument> getPathArguments() {
113         StackedPathArguments ret = tryPathArguments();
114         if (ret == null) {
115             final List<PathArgument> stack = new ArrayList<>();
116             YangInstanceIdentifier current = this;
117             do {
118                 verify(current instanceof StackedYangInstanceIdentifier);
119                 final StackedYangInstanceIdentifier stacked = (StackedYangInstanceIdentifier) current;
120                 stack.add(stacked.getLastPathArgument());
121                 current = stacked.getParent();
122             } while (current.tryPathArguments() == null);
123
124             ret = new StackedPathArguments(current, Lists.reverse(stack));
125             pathArguments = ret;
126         }
127
128         return ret;
129     }
130
131     @Override
132     public List<PathArgument> getReversePathArguments() {
133         StackedReversePathArguments ret = tryReversePathArguments();
134         if (ret == null) {
135             ret = new StackedReversePathArguments(this);
136             reversePathArguments = ret;
137         }
138         return ret;
139     }
140
141     @Override
142     public PathArgument getLastPathArgument() {
143         return pathArgument;
144     }
145
146     @Nonnull
147     @Override
148     StackedPathArguments tryPathArguments() {
149         return pathArguments;
150     }
151
152     @Nonnull
153     @Override
154     StackedReversePathArguments tryReversePathArguments() {
155         return reversePathArguments;
156     }
157
158     @Nonnull
159     @Override
160     YangInstanceIdentifier createRelativeIdentifier(final int skipFromRoot) {
161         // TODO: can we optimize this one?
162         return YangInstanceIdentifier.create(Iterables.skip(getPathArguments(), skipFromRoot));
163     }
164
165     @Override
166     boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
167         if (other instanceof StackedYangInstanceIdentifier) {
168             final StackedYangInstanceIdentifier stacked = (StackedYangInstanceIdentifier) other;
169             return pathArgument.equals(stacked.pathArgument) && parent.equals(stacked.parent);
170         }
171         return super.pathArgumentsEqual(other);
172     }
173
174     private void readObject(final ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
175         inputStream.defaultReadObject();
176
177         final FixedYangInstanceIdentifier p = (FixedYangInstanceIdentifier) inputStream.readObject();
178         try {
179             PARENT_FIELD.set(this, p);
180         } catch (IllegalArgumentException | IllegalAccessException e) {
181             throw new IOException("Failed to set parent", e);
182         }
183     }
184
185     private void writeObject(final ObjectOutputStream outputStream) throws IOException {
186         outputStream.defaultWriteObject();
187
188         final FixedYangInstanceIdentifier p;
189         if (parent instanceof FixedYangInstanceIdentifier) {
190             p = (FixedYangInstanceIdentifier) parent;
191         } else {
192             p = FixedYangInstanceIdentifier.create(parent.getPathArguments(), parent.hashCode());
193         }
194         outputStream.writeObject(p);
195     }
196
197     @Override
198     public YangInstanceIdentifier toOptimized() {
199         return FixedYangInstanceIdentifier.create(getPathArguments());
200     }
201 }