1 package eu.fbk.knowledgestore.data;
2
3 import java.io.Serializable;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.Set;
7
8 import javax.annotation.Nullable;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.collect.ImmutableSet;
13 import com.google.common.collect.Iterables;
14 import com.google.common.collect.Lists;
15 import com.google.common.collect.Ordering;
16 import com.google.common.collect.Sets;
17
18 import org.openrdf.model.URI;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 public abstract class Criteria implements Serializable {
94
95 private static final long serialVersionUID = 1L;
96
97 private final Set<URI> properties;
98
99 private Criteria(final URI... properties) {
100 this.properties = ImmutableSet.copyOf(properties);
101 }
102
103 private static Criteria create(final String name, final URI... properties) {
104
105 if (Overwrite.class.getSimpleName().equalsIgnoreCase(name)) {
106 return overwrite(properties);
107 } else if (Update.class.getSimpleName().equalsIgnoreCase(name)) {
108 return update(properties);
109 } else if (Init.class.getSimpleName().equalsIgnoreCase(name)) {
110 return init(properties);
111 } else if (Min.class.getSimpleName().equalsIgnoreCase(name)) {
112 return min(properties);
113 } else if (Max.class.getSimpleName().equalsIgnoreCase(name)) {
114 return max(properties);
115 } else if (Union.class.getSimpleName().equalsIgnoreCase(name)) {
116 return union(properties);
117 } else {
118 throw new IllegalArgumentException("Unknown criteria name: " + name);
119 }
120 }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 public static Criteria parse(final String string,
137 @Nullable final Map<String, String> namespaces) throws ParseException {
138
139 Preconditions.checkNotNull(string);
140
141 final List<Criteria> criteria = Lists.newArrayList();
142 final List<URI> uris = Lists.newArrayList();
143 String name = null;
144
145 try {
146 for (final String token : string.split("[\\s\\,]+")) {
147 if ("*".equals(token)) {
148 criteria.add(create(name, uris.toArray(new URI[uris.size()])));
149 name = null;
150 uris.clear();
151
152 } else if (token.startsWith("<") && token.endsWith(">")
153 || token.indexOf(':') >= 0) {
154 uris.add((URI) Data.parseValue(token, namespaces));
155
156 } else if (name != null || !uris.isEmpty()) {
157 criteria.add(create(name, uris.toArray(new URI[uris.size()])));
158 name = token;
159 uris.clear();
160
161 } else {
162 name = token;
163 }
164 }
165
166 if (!uris.isEmpty()) {
167 criteria.add(create(name, uris.toArray(new URI[uris.size()])));
168 }
169
170 return criteria.size() == 1 ? criteria.get(0) : compose(criteria
171 .toArray(new Criteria[criteria.size()]));
172
173 } catch (final Exception ex) {
174 throw new ParseException(string, "Invalid criteria string - " + ex.getMessage(), ex);
175 }
176 }
177
178
179
180
181
182
183
184
185
186
187
188 public static Criteria overwrite(final URI... properties) {
189 return new Overwrite(properties);
190 }
191
192
193
194
195
196
197
198
199
200
201
202 public static Criteria update(final URI... properties) {
203 return new Update(properties);
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217 public static Criteria init(final URI... properties) {
218 return new Init(properties);
219 }
220
221
222
223
224
225
226
227
228
229
230
231
232 public static Criteria union(final URI... properties) {
233 return new Union(properties);
234 }
235
236
237
238
239
240
241
242
243
244
245
246 public static Criteria min(final URI... properties) {
247 return new Min(properties);
248 }
249
250
251
252
253
254
255
256
257
258
259
260 public static Criteria max(final URI... properties) {
261 return new Max(properties);
262 }
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277 public static Criteria compose(final Criteria... criteria) {
278 Preconditions.checkArgument(criteria.length > 0, "At least a criteria must be supplied");
279 if (criteria.length == 1) {
280 return criteria[0];
281 } else {
282 return new Compose(criteria);
283 }
284 }
285
286
287
288
289
290
291 public final Set<URI> getProperties() {
292 return this.properties;
293 }
294
295
296
297
298
299
300
301
302 public final boolean appliesTo(final URI property) {
303
304 if (this.properties.isEmpty() || this.properties.contains(property)) {
305 return true;
306 }
307 Preconditions.checkNotNull(property);
308 return false;
309 }
310
311
312
313
314
315
316 public final boolean appliesToAll() {
317 return this.properties.isEmpty();
318 }
319
320
321
322
323
324
325
326
327
328
329
330
331 public final void merge(final Record oldRecord, final Record newRecord) {
332
333 Preconditions.checkNotNull(oldRecord);
334
335 for (final URI property : newRecord.getProperties()) {
336 if (appliesTo(property)) {
337 oldRecord.set(property,
338 merge(property, oldRecord.get(property), newRecord.get(property)));
339 }
340 }
341 }
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357 @SuppressWarnings("unchecked")
358 public final List<Object> merge(final URI property, final List<? extends Object> oldValues,
359 final List<? extends Object> newValues) {
360
361 Preconditions.checkNotNull(oldValues);
362 Preconditions.checkNotNull(newValues);
363
364 return doMerge(property, (List<Object>) oldValues, (List<Object>) newValues);
365 }
366
367 List<Object> doMerge(final URI property, final List<Object> oldValues,
368 final List<Object> newValues) {
369 return appliesTo(property) ? doMerge(oldValues, newValues) : oldValues;
370 }
371
372 List<Object> doMerge(final List<Object> oldValues, final List<Object> newValues) {
373 return oldValues;
374 }
375
376
377
378
379
380
381
382
383
384
385 public final List<Criteria> decompose() {
386 return doDecompose();
387 }
388
389 List<Criteria> doDecompose() {
390 return ImmutableList.of(this);
391 }
392
393
394
395
396
397 @Override
398 public final boolean equals(final Object object) {
399 if (object == this) {
400 return true;
401 }
402 if (object == null || object.getClass() != this.getClass()) {
403 return false;
404 }
405 final Criteria other = (Criteria) object;
406 return this.properties.equals(other.getProperties());
407 }
408
409
410
411
412
413 @Override
414 public final int hashCode() {
415 return this.properties.hashCode();
416 }
417
418
419
420
421
422
423
424
425
426 public final String toString(@Nullable final Map<String, String> namespaces) {
427 final StringBuilder builder = new StringBuilder();
428 doToString(builder, namespaces);
429 return builder.toString();
430 }
431
432
433
434
435
436 @Override
437 public final String toString() {
438 return toString(null);
439 }
440
441 void doToString(final StringBuilder builder, @Nullable final Map<String, String> namespaces) {
442 builder.append(getClass().getSimpleName().toLowerCase()).append(" ");
443 if (this.properties.isEmpty()) {
444 builder.append("*");
445 } else {
446 String separator = "";
447 for (final URI property : this.properties) {
448 builder.append(separator).append(Data.toString(property, namespaces));
449 separator = ", ";
450 }
451 }
452 }
453
454 private static final class Overwrite extends Criteria {
455
456 private static final long serialVersionUID = 1L;
457
458 Overwrite(final URI... properties) {
459 super(properties);
460 }
461
462 @Override
463 List<Object> doMerge(final List<Object> oldValues, final List<Object> newValues) {
464 return newValues;
465 }
466
467 }
468
469 private static final class Update extends Criteria {
470
471 private static final long serialVersionUID = 1L;
472
473 Update(final URI... properties) {
474 super(properties);
475 }
476
477 @Override
478 List<Object> doMerge(final List<Object> oldValues, final List<Object> newValues) {
479 return newValues.isEmpty() ? oldValues : newValues;
480 }
481
482 }
483
484 private static final class Init extends Criteria {
485
486 private static final long serialVersionUID = 1L;
487
488 Init(final URI... properties) {
489 super(properties);
490 }
491
492 @Override
493 List<Object> doMerge(final List<Object> oldValues, final List<Object> newValues) {
494 return oldValues.isEmpty() ? newValues : oldValues;
495 }
496
497 }
498
499 private static final class Union extends Criteria {
500
501 private static final long serialVersionUID = 1L;
502
503 Union(final URI... properties) {
504 super(properties);
505 }
506
507 @Override
508 List<Object> doMerge(final List<Object> oldValues, final List<Object> newValues) {
509 if (oldValues.isEmpty()) {
510 return newValues;
511 } else if (newValues.isEmpty()) {
512 return oldValues;
513 } else {
514 final Set<Object> set = Sets.newLinkedHashSet();
515 set.addAll(oldValues);
516 set.addAll(newValues);
517 return ImmutableList.copyOf(set);
518 }
519 }
520
521 }
522
523 private static final class Min extends Criteria {
524
525 private static final long serialVersionUID = 1L;
526
527 Min(final URI... properties) {
528 super(properties);
529 }
530
531 @Override
532 List<Object> doMerge(final List<Object> oldValues, final List<Object> newValues) {
533 if (oldValues.isEmpty()) {
534 return newValues.size() <= 1 ? newValues : ImmutableList
535 .of(((Ordering<Object>) Data.getTotalComparator()).min(newValues));
536 } else if (newValues.isEmpty()) {
537 return oldValues.size() <= 1 ? oldValues : ImmutableList
538 .of(((Ordering<Object>) Data.getTotalComparator()).min(oldValues));
539 } else {
540 return ImmutableList.of(((Ordering<Object>) Data.getTotalComparator())
541 .min(Iterables.concat(oldValues, newValues)));
542 }
543 }
544
545 }
546
547 private static final class Max extends Criteria {
548
549 private static final long serialVersionUID = 1L;
550
551 Max(final URI... properties) {
552 super(properties);
553 }
554
555 @Override
556 List<Object> doMerge(final List<Object> oldValues, final List<Object> newValues) {
557 if (oldValues.isEmpty()) {
558 return newValues.size() <= 1 ? newValues : ImmutableList
559 .of(((Ordering<Object>) Data.getTotalComparator()).max(newValues));
560 } else if (newValues.isEmpty()) {
561 return oldValues.size() <= 1 ? oldValues : ImmutableList
562 .of(((Ordering<Object>) Data.getTotalComparator()).max(oldValues));
563 } else {
564 return ImmutableList.of(((Ordering<Object>) Data.getTotalComparator())
565 .max(Iterables.concat(oldValues, newValues)));
566 }
567 }
568
569 }
570
571 private static final class Compose extends Criteria {
572
573 private static final long serialVersionUID = 1L;
574
575 private final Criteria[] specificCriteria;
576
577 private final Criteria defaultCriteria;
578
579 Compose(final Criteria... criteria) {
580 super(extractProperties(criteria));
581 Criteria candidateDefaultCriteria = null;
582 final ImmutableList.Builder<Criteria> builder = ImmutableList.builder();
583 for (final Criteria c : criteria) {
584 for (final Criteria d : c.decompose()) {
585 if (!d.appliesToAll()) {
586 builder.add(d);
587 } else if (candidateDefaultCriteria == null) {
588 candidateDefaultCriteria = d;
589 }
590 }
591 }
592 this.specificCriteria = Iterables.toArray(builder.build(), Criteria.class);
593 this.defaultCriteria = candidateDefaultCriteria;
594 }
595
596 @Override
597 List<Object> doMerge(final URI property, final List<Object> oldValues,
598 final List<Object> newValues) {
599 for (final Criteria c : this.specificCriteria) {
600 if (c.appliesTo(property)) {
601 return c.doMerge(oldValues, newValues);
602 }
603 }
604 if (this.defaultCriteria != null) {
605 return this.defaultCriteria.doMerge(oldValues, newValues);
606 }
607 return oldValues;
608 }
609
610 @Override
611 List<Criteria> doDecompose() {
612 final ImmutableList.Builder<Criteria> builder = ImmutableList.builder();
613 builder.add(this.specificCriteria);
614 builder.add(this.defaultCriteria);
615 return builder.build();
616 }
617
618 @Override
619 void doToString(final StringBuilder builder,
620 @Nullable final Map<String, String> namespaces) {
621 String separator = "";
622 for (final Criteria c : this.specificCriteria) {
623 builder.append(separator);
624 c.doToString(builder, namespaces);
625 separator = ", ";
626 }
627 if (this.defaultCriteria != null) {
628 builder.append(separator);
629 this.defaultCriteria.doToString(builder, namespaces);
630 }
631 }
632
633 private static URI[] extractProperties(final Criteria... criteria) {
634 final List<URI> properties = Lists.newArrayList();
635 for (final Criteria c : criteria) {
636 properties.addAll(c.properties);
637 }
638 return properties.toArray(new URI[properties.size()]);
639 }
640
641 }
642
643 }
644
645
646
647
648
649
650