Make StatementMap extend AbstractCollection
[yangtools.git] / yang / 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         int capacity() {
70             return 0;
71         }
72
73         @Override
74         public void forEach(final Consumer<? super AbstractResumedStatement<?, ?, ?>> action) {
75             // No-op
76         }
77
78         @Override
79         public Iterator<AbstractResumedStatement<?, ?, ?>> iterator() {
80             return EMPTY_ITERATOR;
81         }
82     }
83
84     private static final class Regular extends StatementMap {
85         private AbstractResumedStatement<?, ?, ?>[] elements;
86         private int size;
87
88         Regular(final int expectedLimit) {
89             elements = new AbstractResumedStatement<?, ?, ?>[expectedLimit];
90         }
91
92         Regular(final int index, final AbstractResumedStatement<?, ?, ?> object) {
93             this(index + 1, index, object);
94         }
95
96         Regular(final AbstractResumedStatement<?, ?, ?> object0, final int index,
97                 final AbstractResumedStatement<?, ?, ?> object) {
98             this(index + 1, 0, object0);
99             elements[index] = requireNonNull(object);
100             size = 2;
101         }
102
103         Regular(final int expectedLimit, final int index, final AbstractResumedStatement<?, ?, ?> object) {
104             this(expectedLimit);
105             elements[index] = requireNonNull(object);
106             size = 1;
107         }
108
109         @Override
110         AbstractResumedStatement<?, ?, ?> get(final int index) {
111             return index >= elements.length ? null : elements[index];
112         }
113
114         @Override
115         StatementMap put(final int index, final AbstractResumedStatement<?, ?, ?> obj) {
116             if (index < elements.length) {
117                 checkArgument(elements[index] == null);
118             } else {
119                 // FIXME: detect linear growth
120                 elements = Arrays.copyOf(elements, index + 1);
121             }
122
123             elements[index] = requireNonNull(obj);
124             size++;
125             return this;
126         }
127
128         @Override
129         public int size() {
130             return size;
131         }
132
133         @Override
134         StatementMap ensureCapacity(final int expectedLimit) {
135             if (elements.length < expectedLimit) {
136                 elements = Arrays.copyOf(elements, expectedLimit);
137             }
138             return this;
139         }
140
141         @Override
142         int capacity() {
143             return elements.length;
144         }
145
146         @Override
147         public Iterator<AbstractResumedStatement<?, ?, ?>> iterator() {
148             return new AbstractIterator<>() {
149                 private int nextOffset = 0;
150
151                 @Override
152                 protected AbstractResumedStatement<?, ?, ?> computeNext() {
153                     while (nextOffset < elements.length) {
154                         final AbstractResumedStatement<?, ?, ?> ret = elements[nextOffset++];
155                         if (ret != null) {
156                             return ret;
157                         }
158                     }
159
160                     return endOfData();
161                 }
162             };
163         }
164     }
165
166     private static final class Singleton extends StatementMap {
167         private final AbstractResumedStatement<?, ?, ?> object;
168
169         Singleton(final AbstractResumedStatement<?, ?, ?> object) {
170             this.object = requireNonNull(object);
171         }
172
173         @Override
174         AbstractResumedStatement<?, ?, ?> get(final int index) {
175             return index == 0 ? object : null;
176         }
177
178         @Override
179         StatementMap put(final int index, final AbstractResumedStatement<?, ?, ?> obj) {
180             checkArgument(index != 0);
181             return new Regular(this.object, index, obj);
182         }
183
184         @Override
185         public int size() {
186             return 1;
187         }
188
189         @Override
190         StatementMap ensureCapacity(final int expectedLimit) {
191             return expectedLimit < 2 ? this : new Regular(expectedLimit, 0, object);
192         }
193
194         @Override
195         int capacity() {
196             return 1;
197         }
198
199         @Override
200         public Iterator<AbstractResumedStatement<?, ?, ?>> iterator() {
201             return Iterators.singletonIterator(object);
202         }
203     }
204
205     private static final StatementMap EMPTY = new Empty();
206
207     static StatementMap empty() {
208         return EMPTY;
209     }
210
211     /**
212      * Return the statement context at specified index.
213      *
214      * @param index Element index, must be non-negative
215      * @return Requested element or null if there is no element at that index
216      */
217     abstract @Nullable AbstractResumedStatement<?, ?, ?> get(int index);
218
219     /**
220      * Add a statement at specified index.
221      *
222      * @param index Element index, must be non-negative
223      * @param obj Object to store
224      * @return New statement map
225      * @throws IllegalArgumentException if the index is already occupied
226      */
227     abstract @NonNull StatementMap put(int index, @NonNull AbstractResumedStatement<?, ?, ?> obj);
228
229     abstract @NonNull StatementMap ensureCapacity(int expectedLimit);
230
231     abstract int capacity();
232 }