1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.openrdf.query.algebra.evaluation;
15
16 import java.util.AbstractSet;
17 import java.util.Collection;
18 import java.util.Iterator;
19 import java.util.NoSuchElementException;
20 import java.util.Set;
21
22 import com.google.common.base.Objects;
23
24 import org.openrdf.model.Value;
25 import org.openrdf.query.Binding;
26 import org.openrdf.query.BindingSet;
27 import org.openrdf.query.impl.BindingImpl;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31
32
33
34
35
36
37
38
39 public final class QueryBindingSet implements BindingSet {
40
41 private static final long serialVersionUID = 1L;
42
43 private static final int DEFAULT_CAPACITY = 8;
44
45 private static final Object DELETED = new Object();
46
47 private static final Logger LOGGER = LoggerFactory.getLogger("eu.fbk.rdfpro");
48
49 private Object[] data;
50
51 private int size;
52
53 private int slots;
54
55 public QueryBindingSet() {
56 this(DEFAULT_CAPACITY);
57 }
58
59 public QueryBindingSet(final int capacity) {
60 int power = DEFAULT_CAPACITY;
61 while (power < capacity) {
62 power *= 2;
63 }
64 this.data = new Object[power * 2];
65 this.size = 0;
66 this.slots = 0;
67 }
68
69 public QueryBindingSet(final BindingSet bindings) {
70 this(bindings.size());
71 addAll(bindings);
72 }
73
74 public void addAll(final BindingSet bindings) {
75 if (bindings instanceof QueryBindingSet) {
76 final QueryBindingSet other = (QueryBindingSet) bindings;
77 for (int i = 0; i < other.data.length; i += 2) {
78 final Object key = other.data[i];
79 if (key instanceof String) {
80 addBinding((String) key, (Value) other.data[i + 1]);
81 }
82 }
83 } else {
84 for (final Binding binding : bindings) {
85 this.addBinding(binding.getName(), binding.getValue());
86 }
87 }
88 }
89
90 public void addBinding(final Binding binding) {
91 addBinding(binding.getName(), binding.getValue());
92 }
93
94 public void addBinding(final String name, final Value value) {
95 setBinding(name, value);
96 }
97
98 public void setBinding(final Binding binding) {
99 setBinding(binding.getName(), binding.getValue());
100 }
101
102 public void setBinding(final String name, final Value value) {
103 for (int i = getSlot(name);; i = nextSlot(i)) {
104 final Object key = this.data[i];
105 if (!(key instanceof String)) {
106 this.data[i] = name;
107 this.data[i + 1] = value;
108 ++this.size;
109 if (key == null) {
110 ++this.slots;
111 if (this.slots * 4 > this.data.length) {
112 rehashSlots();
113 }
114 }
115 return;
116 } else if (key instanceof String && key.hashCode() == name.hashCode()
117 && key.equals(name)) {
118 this.data[i + 1] = value;
119 return;
120 }
121 }
122 }
123
124 public void removeBinding(final String name) {
125 for (int i = getSlot(name);; i = nextSlot(i)) {
126 final Object key = this.data[i];
127 if (key == null) {
128 return;
129 } else if (key instanceof String && key.hashCode() == name.hashCode()
130 && key.equals(name)) {
131 this.data[i] = DELETED;
132 this.data[i + 1] = null;
133 return;
134 }
135 }
136 }
137
138 public void removeAll(final Collection<String> names) {
139 if (names instanceof Set<?>) {
140 for (int i = 0; i < this.data.length; i += 2) {
141 final Object key = this.data[i];
142 if (key instanceof String && names.contains(key)) {
143 this.data[i] = DELETED;
144 this.data[i + 1] = null;
145 }
146 }
147 } else {
148 for (final String name : names) {
149 removeBinding(name);
150 }
151 }
152 }
153
154 public void retainAll(final Collection<String> names) {
155 for (int i = 0; i < this.data.length; i += 2) {
156 final Object key = this.data[i];
157 if (key instanceof String && !names.contains(key)) {
158 this.data[i] = DELETED;
159 this.data[i + 1] = null;
160 }
161 }
162 }
163
164 @Override
165 public Set<String> getBindingNames() {
166 return new AbstractSet<String>() {
167
168 @Override
169 public boolean contains(final Object object) {
170 return object instanceof String
171 && QueryBindingSet.this.hasBinding((String) object);
172 }
173
174 @Override
175 public int size() {
176 return QueryBindingSet.this.size();
177 }
178
179 @Override
180 public Iterator<String> iterator() {
181 return new NameIterator(QueryBindingSet.this.data);
182 }
183
184 };
185 }
186
187 @Override
188 public Value getValue(final String name) {
189 for (int i = getSlot(name);; i = nextSlot(i)) {
190 final Object key = this.data[i];
191 if (key == null) {
192 return null;
193 } else if (key instanceof String && key.hashCode() == name.hashCode()
194 && key.equals(name)) {
195 return (Value) this.data[i + 1];
196 }
197 }
198 }
199
200 @Override
201 public Binding getBinding(final String name) {
202 final Value value = getValue(name);
203 return value == null ? null : new BindingImpl(name, value);
204 }
205
206 @Override
207 public boolean hasBinding(final String name) {
208 for (int i = getSlot(name);; i = nextSlot(i)) {
209 final Object key = this.data[i];
210 if (key == null) {
211 return false;
212 } else if (key instanceof String && key.hashCode() == name.hashCode()
213 && key.equals(name)) {
214 return true;
215 }
216 }
217 }
218
219 @Override
220 public Iterator<Binding> iterator() {
221 return new BindingIterator(this.data);
222 }
223
224 @Override
225 public int size() {
226 return this.size;
227 }
228
229 @Override
230 public boolean equals(final Object object) {
231
232 if (object == this) {
233 return true;
234
235 } else if (object instanceof QueryBindingSet) {
236 final QueryBindingSet other = (QueryBindingSet) object;
237 if (other.data.length == this.data.length) {
238 for (int i = 0; i < this.data.length; i += 2) {
239 final Object thisKey = this.data[i] instanceof String ? this.data[i] : null;
240 final Object otherKey = other.data[i] instanceof String ? other.data[i] : null;
241 if (!Objects.equal(thisKey, otherKey)
242 || !Objects.equal(this.data[i + 1], other.data[i + 1])) {
243 return false;
244 }
245 }
246 return true;
247 }
248 if (size() != other.size()) {
249 return false;
250 }
251 for (int i = 0; i < other.data.length; i += 2) {
252 if (other.data[i] instanceof String) {
253 final String name = (String) other.data[i];
254 final Value otherValue = (Value) other.data[i + 1];
255 final Value thisValue = getValue(name);
256 if (!otherValue.equals(thisValue)) {
257 return false;
258 }
259 }
260 }
261 return true;
262
263 } else if (object instanceof BindingSet) {
264 final BindingSet other = (BindingSet) object;
265 if (size() != other.size()) {
266 return false;
267 }
268 for (final Binding binding : other) {
269 final Value thisValue = getValue(binding.getName());
270 if (!binding.getValue().equals(thisValue)) {
271 return false;
272 }
273 }
274 return true;
275 }
276
277 return false;
278 }
279
280 @Override
281 public int hashCode() {
282 int hashCode = 0;
283 for (int i = 0; i < this.data.length; i += 2) {
284 final Object key = this.data[i];
285 if (key instanceof String) {
286 hashCode = hashCode ^ key.hashCode() ^ this.data[i + 1].hashCode();
287 }
288 }
289 return hashCode;
290 }
291
292 @Override
293 public String toString() {
294 final StringBuilder builder = new StringBuilder(8 * this.data.length);
295 builder.append('[');
296 String separator = "";
297 for (int i = 0; i < this.data.length; i += 2) {
298 final Object key = this.data[i];
299 if (key instanceof String) {
300 builder.append(separator);
301 builder.append(key);
302 builder.append('=');
303 builder.append(this.data[i + 1]);
304 separator = ";";
305 }
306 }
307 builder.append(']');
308 return builder.toString();
309 }
310
311 private void rehashSlots() {
312 final Object[] oldData = this.data;
313 this.data = new Object[oldData.length * 2];
314 for (int j = 0; j < oldData.length; j += 2) {
315 if (oldData[j] instanceof String) {
316 final String name = (String) oldData[j];
317 final Value value = (Value) oldData[j + 1];
318 for (int i = getSlot(name);; i = nextSlot(i)) {
319 if (this.data[i] == null) {
320 this.data[i] = name;
321 this.data[i + 1] = value;
322 break;
323 }
324 }
325 }
326 }
327 this.slots = this.size;
328 }
329
330 private int getSlot(final String bindingName) {
331 return (bindingName.hashCode() & (this.data.length >>> 1) - 1) << 1;
332 }
333
334 private int nextSlot(final int slot) {
335 final int nextSlot = slot + 2;
336 return nextSlot < this.data.length ? nextSlot : 0;
337 }
338
339 static {
340 LOGGER.debug("Using patched QueryBindingSet class");
341 }
342
343 private static abstract class AbstractIterator<T> implements Iterator<T> {
344
345 private final Object[] data;
346
347 private int nextOffset;
348
349 private int removeOffset;
350
351 AbstractIterator(final Object[] data) {
352 this.data = data;
353 this.nextOffset = 0;
354 this.removeOffset = -1;
355 }
356
357 abstract T elementAt(Object[] data, int offset);
358
359 @Override
360 public boolean hasNext() {
361 while (this.nextOffset < this.data.length
362 && !(this.data[this.nextOffset] instanceof String)) {
363 this.nextOffset += 2;
364 }
365 return this.nextOffset < this.data.length;
366 }
367
368 @Override
369 public T next() {
370 if (!hasNext()) {
371 throw new NoSuchElementException();
372 }
373 this.removeOffset = this.nextOffset;
374 this.nextOffset += 2;
375 return elementAt(this.data, this.removeOffset);
376 }
377
378 @Override
379 public void remove() {
380 if (this.removeOffset < 0) {
381 throw new NoSuchElementException();
382 }
383 this.data[this.removeOffset] = DELETED;
384 this.data[this.removeOffset + 1] = null;
385 this.removeOffset = -1;
386 }
387
388 }
389
390 private static final class NameIterator extends AbstractIterator<String> {
391
392 NameIterator(final Object[] data) {
393 super(data);
394 }
395
396 @Override
397 String elementAt(final Object[] data, final int offset) {
398 return (String) data[offset];
399 }
400
401 }
402
403 private static final class BindingIterator extends AbstractIterator<Binding> {
404
405 BindingIterator(final Object[] data) {
406 super(data);
407 }
408
409 @Override
410 Binding elementAt(final Object[] data, final int offset) {
411 return new BindingImpl((String) data[offset], (Value) data[offset + 1]);
412 }
413
414 }
415
416 }