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