Do not retain reference to StatementMap
[yangtools.git] / parser / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / StatementMap.java
1 /*
2  * Copyright (c) 2016 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.parser.stmt.reactor;
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.AbstractIterator;
15 import com.google.common.collect.Iterators;
16 import java.util.AbstractCollection;
17 import java.util.Arrays;
18 import java.util.Collection;
19 import java.util.Iterator;
20 import java.util.function.Consumer;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.Nullable;
23
24 /**
25  * Simple integer-to-StatementContextBase map optimized for size and restricted in scope of operations. It does not
26  * implement {@link java.util.Map} for simplicity's sake.
27  *
28  * <p>
29  * Unlike other collections, this view does not detect concurrent modification. Iteration is performed in order of
30  * increasing offset. In face of concurrent modification, number of elements returned through iteration may not match
31  * the size reported via {@link Collection#size()}.
32  *
33  * @author Robert Varga
34  */
35 abstract class StatementMap extends AbstractCollection<AbstractResumedStatement<?, ?, ?>> {
36     private static final class Empty extends StatementMap {
37         private static final Iterator<AbstractResumedStatement<?, ?, ?>> EMPTY_ITERATOR;
38
39         static {
40             // This may look weird, but we really want to return two Iterator implementations from StatementMap, so that
41             // users have to deal with bimorphic invocation. Note that we want to invoke hasNext() here, as we want to
42             // initialize state to AbstractIterator.endOfData().
43             final Iterator<AbstractResumedStatement<?, ?, ?>> it = new Regular(0).iterator();
44             verify(!it.hasNext());
45             EMPTY_ITERATOR = it;
46         }
47
48         @Override
49         AbstractResumedStatement<?, ?, ?> get(final int index) {
50             return null;
51         }
52
53         @Override
54         StatementMap put(final int index, final AbstractResumedStatement<?, ?, ?> obj) {
55             return index == 0 ? new Singleton(obj) : new Regular(index, obj);
56         }
57
58         @Override
59         public int size() {
60             return 0;
61         }
62
63         @Override
64         StatementMap ensureCapacity(final int expectedLimit) {
65             return expectedLimit < 2 ? this : new Regular(expectedLimit);
66         }
67
68         @Override
69         public void forEach(final Consumer<? super AbstractResumedStatement<?, ?, ?>> action) {
70             // No-op
71         }
72
73         @Override
74         public Iterator<AbstractResumedStatement<?, ?, ?>> iterator() {
75             return EMPTY_ITERATOR;
76         }
77     }
78
79     private static final class Regular extends StatementMap {
80         private AbstractResumedStatement<?, ?, ?>[] elements;
81         private int size;
82
83         Regular(final int expectedLimit) {
84             elements = new AbstractResumedStatement<?, ?, ?>[expectedLimit];
85         }
86
87         Regular(final int index, final AbstractResumedStatement<?, ?, ?> object) {
88             this(index + 1, index, object);
89         }
90
91         Regular(final AbstractResumedStatement<?, ?, ?> object0, final int index,
92                 final AbstractResumedStatement<?, ?, ?> object) {
93             this(index + 1, 0, object0);
94             elements[index] = requireNonNull(object);
95             size = 2;
96         }
97
98         Regular(final int expectedLimit, final int index, final AbstractResumedStatement<?, ?, ?> object) {
99             this(expectedLimit);
100             elements[index] = requireNonNull(object);
101             size = 1;
102         }
103
104         @Override
105         AbstractResumedStatement<?, ?, ?> get(final int index) {
106             return index >= elements.length ? null : elements[index];
107         }
108
109         @Override
110         StatementMap put(final int index, final AbstractResumedStatement<?, ?, ?> obj) {
111             if (index < elements.length) {
112                 checkArgument(elements[index] == null);
113             } else {
114                 // FIXME: detect linear growth
115                 elements = Arrays.copyOf(elements, index + 1);
116             }
117
118             elements[index] = requireNonNull(obj);
119             size++;
120             return this;
121         }
122
123         @Override
124         public int size() {
125             return size;
126         }
127
128         @Override
129         StatementMap ensureCapacity(final int expectedLimit) {
130             if (elements.length < expectedLimit) {
131                 elements = Arrays.copyOf(elements, expectedLimit);
132             }
133             return this;
134         }
135
136         @Override
137         public Iterator<AbstractResumedStatement<?, ?, ?>> iterator() {
138             return new Iter(this);
139         }
140
141         private static final class Iter extends AbstractIterator<AbstractResumedStatement<?, ?, ?>> {
142             private int nextOffset = 0;
143             private Regular map;
144
145             Iter(final Regular map) {
146                 this.map = requireNonNull(map);
147             }
148
149             @Override
150             protected AbstractResumedStatement<?, ?, ?> computeNext() {
151                 while (nextOffset < map.elements.length) {
152                     final AbstractResumedStatement<?, ?, ?> ret = map.elements[nextOffset++];
153                     if (ret != null) {
154                         return ret;
155                     }
156                 }
157
158                 map = null;
159                 return endOfData();
160             }
161         }
162     }
163
164     private static final class Singleton extends StatementMap {
165         private final AbstractResumedStatement<?, ?, ?> object;
166
167         Singleton(final AbstractResumedStatement<?, ?, ?> object) {
168             this.object = requireNonNull(object);
169         }
170
171         @Override
172         AbstractResumedStatement<?, ?, ?> get(final int index) {
173             return index == 0 ? object : null;
174         }
175
176         @Override
177         StatementMap put(final int index, final AbstractResumedStatement<?, ?, ?> obj) {
178             checkArgument(index != 0);
179             return new Regular(this.object, index, obj);
180         }
181
182         @Override
183         public int size() {
184             return 1;
185         }
186
187         @Override
188         StatementMap ensureCapacity(final int expectedLimit) {
189             return expectedLimit < 2 ? this : new Regular(expectedLimit, 0, object);
190         }
191
192         @Override
193         public Iterator<AbstractResumedStatement<?, ?, ?>> iterator() {
194             return Iterators.singletonIterator(object);
195         }
196     }
197
198     private static final StatementMap EMPTY = new Empty();
199
200     static StatementMap empty() {
201         return EMPTY;
202     }
203
204     /**
205      * Return the statement context at specified index.
206      *
207      * @param index Element index, must be non-negative
208      * @return Requested element or null if there is no element at that index
209      */
210     abstract @Nullable AbstractResumedStatement<?, ?, ?> get(int index);
211
212     /**
213      * Add a statement at specified index.
214      *
215      * @param index Element index, must be non-negative
216      * @param obj Object to store
217      * @return New statement map
218      * @throws IllegalArgumentException if the index is already occupied
219      */
220     abstract @NonNull StatementMap put(int index, @NonNull AbstractResumedStatement<?, ?, ?> obj);
221
222     abstract @NonNull StatementMap ensureCapacity(int expectedLimit);
223 }