Commit | Line | Data |
---|---|---|
3dde9149 MK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2016 Ericsson | |
3 | * | |
4 | * All rights reserved. This program and the accompanying materials | |
5 | * are made available under the terms of the Eclipse Public License v1.0 | |
6 | * which accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | *******************************************************************************/ | |
9 | ||
664a3a81 | 10 | package org.eclipse.tracecompass.internal.segmentstore.core.arraylist; |
3dde9149 MK |
11 | |
12 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; | |
13 | ||
14 | import java.util.ArrayList; | |
15 | import java.util.Collection; | |
16 | import java.util.Collections; | |
17 | import java.util.Comparator; | |
18 | import java.util.Iterator; | |
19 | import java.util.List; | |
20 | import java.util.concurrent.locks.ReadWriteLock; | |
21 | import java.util.concurrent.locks.ReentrantReadWriteLock; | |
22 | import java.util.stream.Collectors; | |
23 | ||
24 | import org.eclipse.jdt.annotation.NonNull; | |
25 | import org.eclipse.jdt.annotation.Nullable; | |
2047fe04 | 26 | import org.eclipse.tracecompass.segmentstore.core.BasicSegment; |
3dde9149 MK |
27 | import org.eclipse.tracecompass.segmentstore.core.ISegment; |
28 | import org.eclipse.tracecompass.segmentstore.core.ISegmentStore; | |
664a3a81 | 29 | import org.eclipse.tracecompass.segmentstore.core.SegmentComparators; |
3dde9149 MK |
30 | |
31 | import com.google.common.collect.ImmutableList; | |
664a3a81 | 32 | import com.google.common.collect.Ordering; |
3dde9149 MK |
33 | |
34 | /** | |
35 | * Implementation of an {@link ISegmentStore} using one in-memory | |
36 | * {@link ArrayList}. This relatively simple implementation holds everything in | |
37 | * memory, and as such cannot contain too much data. | |
38 | * | |
39 | * The ArrayListStore itself is {@link Iterable}, and its iteration order will | |
40 | * be by ascending order of start times. For segments with identical start | |
41 | * times, the secondary comparator will be the end time. If even those are | |
42 | * equal, it will defer to the segments' natural ordering ( | |
43 | * {@link ISegment#compareTo}). | |
44 | * | |
45 | * The store's tree maps will not accept duplicate key-value pairs, which means | |
46 | * that if you want several segments with the same start and end times, make | |
47 | * sure their compareTo() differentiates them. | |
48 | * | |
49 | * Removal operations are not supported. | |
50 | * | |
51 | * @param <E> | |
52 | * The type of segment held in this store | |
53 | * | |
54 | * @author Matthew Khouzam | |
55 | */ | |
56 | public class ArrayListStore<@NonNull E extends ISegment> implements ISegmentStore<E> { | |
57 | ||
664a3a81 LPD |
58 | private final Comparator<E> COMPARATOR = Ordering.from(SegmentComparators.INTERVAL_START_COMPARATOR) |
59 | .compound(SegmentComparators.INTERVAL_END_COMPARATOR); | |
3dde9149 MK |
60 | |
61 | private final ReadWriteLock fLock = new ReentrantReadWriteLock(false); | |
62 | ||
aa9082f9 | 63 | private final List<E> fStore; |
3dde9149 MK |
64 | |
65 | private @Nullable transient Iterable<E> fLastSnapshot = null; | |
66 | ||
67 | /** | |
68 | * Constructor | |
69 | */ | |
70 | public ArrayListStore() { | |
aa9082f9 | 71 | fStore = new ArrayList<>(); |
3dde9149 MK |
72 | } |
73 | ||
aa9082f9 MK |
74 | /** |
75 | * Constructor | |
76 | * | |
77 | * @param array | |
78 | * an array of elements to wrap in the segment store | |
79 | * | |
80 | */ | |
81 | public ArrayListStore(Object[] array) { | |
82 | fStore = new ArrayList<>(); | |
83 | for (int i = 0; i < array.length; i++) { | |
84 | if (array[i] instanceof ISegment) { | |
85 | fStore.add((E) array[i]); | |
86 | } | |
87 | } | |
88 | fStore.sort(COMPARATOR); | |
89 | } | |
3dde9149 MK |
90 | // ------------------------------------------------------------------------ |
91 | // Methods from Collection | |
92 | // ------------------------------------------------------------------------ | |
93 | ||
94 | @Override | |
95 | public Iterator<E> iterator() { | |
96 | fLock.readLock().lock(); | |
97 | try { | |
98 | Iterable<E> lastSnapshot = fLastSnapshot; | |
99 | if (lastSnapshot == null) { | |
100 | lastSnapshot = ImmutableList.copyOf(fStore); | |
101 | fLastSnapshot = lastSnapshot; | |
102 | } | |
103 | return checkNotNull(lastSnapshot.iterator()); | |
104 | } finally { | |
105 | fLock.readLock().unlock(); | |
106 | } | |
107 | } | |
108 | ||
109 | @Override | |
110 | public boolean add(@Nullable E val) { | |
111 | if (val == null) { | |
112 | throw new IllegalArgumentException("Cannot add null value"); //$NON-NLS-1$ | |
113 | } | |
114 | ||
115 | fLock.writeLock().lock(); | |
116 | try { | |
2047fe04 LPD |
117 | int insertPoint = Collections.binarySearch(fStore, val); |
118 | insertPoint = insertPoint >= 0 ? insertPoint : -insertPoint - 1; | |
119 | fStore.add(insertPoint, val); | |
581e0f63 | 120 | fLastSnapshot = null; |
3dde9149 MK |
121 | return true; |
122 | } finally { | |
123 | fLock.writeLock().unlock(); | |
124 | } | |
125 | } | |
126 | ||
127 | @Override | |
128 | public int size() { | |
524fb56d MK |
129 | fLock.readLock().lock(); |
130 | try { | |
131 | return fStore.size(); | |
132 | } finally { | |
133 | fLock.readLock().unlock(); | |
134 | } | |
3dde9149 MK |
135 | } |
136 | ||
137 | @Override | |
138 | public boolean isEmpty() { | |
f601ac35 MK |
139 | fLock.readLock().lock(); |
140 | try { | |
141 | return fStore.isEmpty(); | |
142 | } finally { | |
143 | fLock.readLock().unlock(); | |
144 | } | |
3dde9149 MK |
145 | } |
146 | ||
147 | @Override | |
148 | public boolean contains(@Nullable Object o) { | |
149 | fLock.readLock().lock(); | |
150 | try { | |
151 | return fStore.contains(o); | |
152 | } finally { | |
153 | fLock.readLock().unlock(); | |
154 | } | |
155 | } | |
156 | ||
157 | @Override | |
158 | public boolean containsAll(@Nullable Collection<?> c) { | |
159 | fLock.readLock().lock(); | |
160 | try { | |
161 | return fStore.containsAll(c); | |
162 | } finally { | |
163 | fLock.readLock().unlock(); | |
164 | } | |
165 | } | |
166 | ||
167 | @Override | |
168 | public Object[] toArray() { | |
169 | fLock.readLock().lock(); | |
170 | try { | |
171 | return fStore.toArray(); | |
172 | } finally { | |
173 | fLock.readLock().unlock(); | |
174 | } | |
175 | } | |
176 | ||
177 | @Override | |
178 | public <T> T[] toArray(T[] a) { | |
179 | fLock.readLock().lock(); | |
180 | try { | |
181 | return fStore.toArray(a); | |
182 | } finally { | |
183 | fLock.readLock().unlock(); | |
184 | } | |
185 | } | |
186 | ||
187 | @Override | |
188 | public boolean remove(@Nullable Object o) { | |
189 | throw new UnsupportedOperationException(); | |
190 | } | |
191 | ||
192 | @Override | |
193 | public boolean addAll(@Nullable Collection<? extends E> c) { | |
194 | if (c == null) { | |
195 | throw new IllegalArgumentException(); | |
196 | } | |
197 | ||
198 | fLock.writeLock().lock(); | |
199 | try { | |
200 | boolean changed = false; | |
201 | for (E elem : c) { | |
202 | if (this.add(elem)) { | |
203 | changed = true; | |
204 | } | |
205 | } | |
206 | return changed; | |
207 | } finally { | |
208 | fLock.writeLock().unlock(); | |
209 | } | |
210 | } | |
211 | ||
212 | @Override | |
213 | public boolean removeAll(@Nullable Collection<?> c) { | |
214 | throw new UnsupportedOperationException(); | |
215 | } | |
216 | ||
217 | @Override | |
218 | public boolean retainAll(@Nullable Collection<?> c) { | |
219 | throw new UnsupportedOperationException(); | |
220 | } | |
221 | ||
222 | @Override | |
223 | public void clear() { | |
224 | fLock.writeLock().lock(); | |
225 | try { | |
226 | fStore.clear(); | |
227 | } finally { | |
228 | fLock.writeLock().unlock(); | |
229 | } | |
230 | } | |
231 | ||
232 | // ------------------------------------------------------------------------ | |
233 | // Methods added by ISegmentStore | |
234 | // ------------------------------------------------------------------------ | |
235 | ||
236 | @Override | |
237 | public Iterable<E> getIntersectingElements(long position) { | |
238 | /* | |
239 | * The intervals intersecting 't' are those whose 1) start time is | |
240 | * *lower* than 't' AND 2) end time is *higher* than 't'. | |
241 | */ | |
242 | fLock.readLock().lock(); | |
243 | try { | |
2047fe04 LPD |
244 | /* |
245 | * as fStore is sorted by start then end times, restrict sub array | |
246 | * to elements whose start times <= t as stream.filter won't do it. | |
247 | */ | |
248 | int index = Collections.binarySearch(fStore, new BasicSegment(position, Long.MAX_VALUE)); | |
249 | index = (index >= 0) ? index : -index - 1; | |
250 | return fStore.subList(0, index).stream().filter(element -> position >= element.getStart() && position <= element.getEnd()).collect(Collectors.toList()); | |
3dde9149 MK |
251 | } finally { |
252 | fLock.readLock().unlock(); | |
253 | } | |
254 | } | |
255 | ||
256 | @Override | |
257 | public Iterable<E> getIntersectingElements(long start, long end) { | |
258 | fLock.readLock().lock(); | |
259 | try { | |
2047fe04 LPD |
260 | int index = Collections.binarySearch(fStore, new BasicSegment(end, Long.MAX_VALUE)); |
261 | index = (index >= 0) ? index : -index - 1; | |
262 | return fStore.subList(0, index).stream().filter(element -> !(start > element.getEnd() || end < element.getStart())).collect(Collectors.toList()); | |
3dde9149 MK |
263 | } finally { |
264 | fLock.readLock().unlock(); | |
265 | } | |
266 | } | |
267 | ||
268 | @Override | |
269 | public void dispose() { | |
270 | fLock.writeLock().lock(); | |
271 | try { | |
272 | fStore.clear(); | |
273 | } finally { | |
274 | fLock.writeLock().unlock(); | |
275 | } | |
276 | } | |
277 | } |