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