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