1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.graph;
25
26 import java.io.Serializable;
27 import java.util.ArrayList;
28 import java.util.Comparator;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.NoSuchElementException;
33 import java.util.Set;
34 import java.util.UUID;
35 import net.jcip.annotations.Immutable;
36 import org.modeshape.common.text.TextEncoder;
37 import org.modeshape.common.util.CheckArg;
38 import org.modeshape.common.util.HashCode;
39 import org.modeshape.graph.property.Name;
40 import org.modeshape.graph.property.NamespaceRegistry;
41 import org.modeshape.graph.property.Path;
42 import org.modeshape.graph.property.Property;
43 import org.modeshape.graph.property.Path.Segment;
44
45
46
47
48
49 @Immutable
50 public abstract class Location implements Iterable<Property>, Comparable<Location>, Serializable {
51
52 private static final long serialVersionUID = 1L;
53
54 private static final Comparator<Location> COMPARATOR = new Comparator<Location>() {
55
56
57
58
59
60 public int compare( Location o1,
61 Location o2 ) {
62 if (o1 == o2) return 0;
63 if (o1 == null) return -1;
64 if (o2 == null) return 1;
65 return o1.compareTo(o2);
66 }
67 };
68
69
70
71
72
73
74
75 public static final Comparator<Location> comparator() {
76 return COMPARATOR;
77 }
78
79
80
81
82 protected static final Iterator<Property> NO_ID_PROPERTIES_ITERATOR = new Iterator<Property>() {
83 public boolean hasNext() {
84 return false;
85 }
86
87 public Property next() {
88 throw new NoSuchElementException();
89 }
90
91 public void remove() {
92 throw new UnsupportedOperationException();
93 }
94 };
95
96
97
98
99
100
101
102
103 public static Location create( Path path ) {
104 CheckArg.isNotNull(path, "path");
105 UUID id = identifierFor(path);
106 if (id != null) return new LocationWithUuid(id);
107 return new LocationWithPath(path);
108 }
109
110 protected static UUID identifierFor( Path identifierPath ) {
111 if (!identifierPath.isIdentifier()) return null;
112
113 Segment segment = identifierPath.getLastSegment();
114 assert segment.isIdentifier();
115 String id = segment.getName().getLocalName();
116 try {
117
118 return UUID.fromString(id);
119 } catch (IllegalArgumentException err) {
120 String pathStr = "[" + id + "]";
121 throw new IllegalArgumentException(GraphI18n.identifierPathContainedUnsupportedIdentifierFormat.text(pathStr));
122 }
123 }
124
125
126
127
128
129
130
131
132
133 public static Location create( UUID uuid ) {
134 CheckArg.isNotNull(uuid, "uuid");
135 return new LocationWithUuid(uuid);
136 }
137
138
139
140
141
142
143
144
145
146
147 public static Location create( Path path,
148 UUID uuid ) {
149 if (path == null) return create(uuid);
150 if (uuid == null) return create(path);
151 UUID id = identifierFor(path);
152 if (id != null) {
153 if (!id.equals(uuid)) {
154 String pathStr = "[" + id + "]";
155 throw new IllegalArgumentException(GraphI18n.identifierPathDoesNotMatchSuppliedUuid.text(pathStr, uuid));
156 }
157 return new LocationWithUuid(id);
158 }
159 return new LocationWithPathAndUuid(path, uuid);
160 }
161
162
163
164
165
166
167
168
169
170 public static Location create( Path path,
171 Property idProperty ) {
172 CheckArg.isNotNull(path, "path");
173 CheckArg.isNotNull(idProperty, "idProperty");
174 if (ModeShapeLexicon.UUID.equals(idProperty.getName()) && idProperty.isSingle()) {
175 Object uuid = idProperty.getFirstValue();
176 assert uuid instanceof UUID;
177 UUID id = identifierFor(path);
178 if (id != null) {
179 if (!id.equals(uuid)) {
180 String pathStr = "[" + id + "]";
181 throw new IllegalArgumentException(GraphI18n.identifierPathDoesNotMatchSuppliedUuid.text(pathStr, uuid));
182 }
183 return new LocationWithUuid(id);
184 }
185 return new LocationWithPathAndUuid(path, (UUID)uuid);
186 }
187 return new LocationWithPathAndProperty(path, idProperty);
188 }
189
190
191
192
193
194
195
196
197
198
199 public static Location create( Path path,
200 Property firstIdProperty,
201 Property... remainingIdProperties ) {
202 CheckArg.isNotNull(path, "path");
203 CheckArg.isNotNull(firstIdProperty, "firstIdProperty");
204 CheckArg.isNotNull(remainingIdProperties, "remainingIdProperties");
205 List<Property> idProperties = new ArrayList<Property>(1 + remainingIdProperties.length);
206 Set<Name> names = new HashSet<Name>();
207 names.add(firstIdProperty.getName());
208 idProperties.add(firstIdProperty);
209 for (Property property : remainingIdProperties) {
210 if (property == null) continue;
211 if (names.add(property.getName())) idProperties.add(property);
212 }
213 if (idProperties.isEmpty()) return new LocationWithPath(path);
214 if (idProperties.size() == 1) {
215 Property property = idProperties.get(0);
216 if (property.isSingle()
217 && (property.getName().equals(JcrLexicon.UUID) || property.getName().equals(ModeShapeLexicon.UUID))) {
218 Object value = property.getFirstValue();
219 if (value instanceof UUID) {
220 return new LocationWithPathAndUuid(path, (UUID)value);
221 }
222 }
223 return new LocationWithPathAndProperty(path, idProperties.get(0));
224 }
225 return new LocationWithPathAndProperties(path, idProperties);
226 }
227
228
229
230
231
232
233
234
235
236 public static Location create( Path path,
237 Iterable<Property> idProperties ) {
238 CheckArg.isNotNull(path, "path");
239 CheckArg.isNotNull(idProperties, "idProperties");
240 List<Property> idPropertiesList = new ArrayList<Property>();
241 Set<Name> names = new HashSet<Name>();
242 for (Property property : idProperties) {
243 if (property == null) continue;
244 if (names.add(property.getName())) idPropertiesList.add(property);
245 }
246 if (idPropertiesList.isEmpty()) return new LocationWithPath(path);
247 if (idPropertiesList.size() == 1) {
248 Property property = idPropertiesList.get(0);
249 if (property.isSingle()
250 && (property.getName().equals(JcrLexicon.UUID) || property.getName().equals(ModeShapeLexicon.UUID))) {
251 Object value = property.getFirstValue();
252 if (value instanceof UUID) {
253 return new LocationWithPathAndUuid(path, (UUID)value);
254 }
255 }
256 return new LocationWithPathAndProperty(path, idPropertiesList.get(0));
257 }
258 return new LocationWithPathAndProperties(path, idPropertiesList);
259 }
260
261
262
263
264
265
266
267
268 public static Location create( Property idProperty ) {
269 CheckArg.isNotNull(idProperty, "idProperty");
270 if (ModeShapeLexicon.UUID.equals(idProperty.getName()) && idProperty.isSingle()) {
271 Object uuid = idProperty.getFirstValue();
272 assert uuid instanceof UUID;
273 return new LocationWithUuid((UUID)uuid);
274 }
275 return new LocationWithProperty(idProperty);
276 }
277
278
279
280
281
282
283
284
285
286 public static Location create( Property firstIdProperty,
287 Property... remainingIdProperties ) {
288 CheckArg.isNotNull(firstIdProperty, "firstIdProperty");
289 CheckArg.isNotNull(remainingIdProperties, "remainingIdProperties");
290 if (remainingIdProperties.length == 0) return create(firstIdProperty);
291 List<Property> idProperties = new ArrayList<Property>(1 + remainingIdProperties.length);
292 Set<Name> names = new HashSet<Name>();
293 names.add(firstIdProperty.getName());
294 idProperties.add(firstIdProperty);
295 for (Property property : remainingIdProperties) {
296 if (names.add(property.getName())) idProperties.add(property);
297 }
298 return new LocationWithProperties(idProperties);
299 }
300
301
302
303
304
305
306
307
308 public static Location create( Iterable<Property> idProperties ) {
309 CheckArg.isNotNull(idProperties, "idProperties");
310 List<Property> idPropertiesList = new ArrayList<Property>();
311 Set<Name> names = new HashSet<Name>();
312 for (Property property : idProperties) {
313 if (names.add(property.getName())) idPropertiesList.add(property);
314 }
315 return create(idPropertiesList);
316 }
317
318
319
320
321
322
323
324
325
326 public static Location create( List<Property> idProperties ) {
327 CheckArg.isNotEmpty(idProperties, "idProperties");
328 return new LocationWithProperties(idProperties);
329 }
330
331
332
333
334
335
336 public abstract Path getPath();
337
338
339
340
341
342
343 public boolean hasPath() {
344 return getPath() != null;
345 }
346
347
348
349
350
351
352 public abstract List<Property> getIdProperties();
353
354
355
356
357
358
359 public boolean hasIdProperties() {
360 return getIdProperties() != null && getIdProperties().size() != 0;
361 }
362
363
364
365
366
367
368
369
370 public Property getIdProperty( Name name ) {
371 CheckArg.isNotNull(name, "name");
372 if (getIdProperties() != null) {
373 for (Property property : getIdProperties()) {
374 if (property.getName().equals(name)) return property;
375 }
376 }
377 return null;
378 }
379
380
381
382
383
384
385 public UUID getUuid() {
386 Property property = getIdProperty(ModeShapeLexicon.UUID);
387 if (property != null && !property.isEmpty()) {
388 Object value = property.getFirstValue();
389 if (value instanceof UUID) return (UUID)value;
390 }
391 return null;
392 }
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408 public boolean isSame( Location that ) {
409 if (that == null) return false;
410 if (this.hasPath()) {
411 if (!this.getPath().equals(that.getPath())) return false;
412 } else if (that.hasPath()) {
413
414 return false;
415 }
416 if (this.hasIdProperties()) {
417 if (that.hasIdProperties()) return this.getIdProperties().equals(that.getIdProperties());
418 return false;
419 }
420 return (!that.hasIdProperties());
421 }
422
423
424
425
426
427
428 public Iterator<Property> iterator() {
429 return getIdProperties() != null ? getIdProperties().iterator() : NO_ID_PROPERTIES_ITERATOR;
430 }
431
432
433
434
435
436
437 @Override
438 public int hashCode() {
439 return HashCode.compute(getPath(), getIdProperties());
440 }
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457 @Override
458 public boolean equals( Object obj ) {
459 return equals(obj, true);
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483 public boolean equals( Object obj,
484 boolean requireSameNameSiblingIndexes ) {
485 if (obj instanceof Location) {
486 Location that = (Location)obj;
487
488
489 if (requireSameNameSiblingIndexes) {
490 if (this.hasPath() && that.hasPath()) return (this.getPath().equals(that.getPath()));
491 } else {
492 Path thisPath = this.getPath();
493 Path thatPath = that.getPath();
494 if (thisPath.isRoot()) return thatPath.isRoot();
495 if (thatPath.isRoot()) return thisPath.isRoot();
496
497 if (!thisPath.hasSameAncestor(thatPath)) return false;
498
499 if (!thisPath.getLastSegment().getName().equals(thatPath.getLastSegment().getName())) return false;
500 }
501
502
503 if (this.hasIdProperties()) return (this.getIdProperties().equals(that.getIdProperties()));
504 }
505
506 return false;
507 }
508
509
510
511
512
513
514 public int compareTo( Location that ) {
515 if (this == that) return 0;
516 if (this.hasPath() && that.hasPath()) {
517 return this.getPath().compareTo(that.getPath());
518 }
519 UUID thisUuid = this.getUuid();
520 UUID thatUuid = that.getUuid();
521 if (thisUuid != null && thatUuid != null) {
522 return thisUuid.compareTo(thatUuid);
523 }
524 return this.hashCode() - that.hashCode();
525 }
526
527
528
529
530
531
532
533
534
535
536 public String getString() {
537 return getString(null, null, null);
538 }
539
540
541
542
543
544
545
546
547
548
549
550
551 public String getString( TextEncoder encoder ) {
552 return getString(null, encoder, null);
553 }
554
555
556
557
558
559
560
561
562
563
564
565
566
567 public String getString( NamespaceRegistry namespaceRegistry ) {
568 return getString(namespaceRegistry, null, null);
569 }
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584 public String getString( NamespaceRegistry namespaceRegistry,
585 TextEncoder encoder ) {
586 return getString(namespaceRegistry, encoder, null);
587 }
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604 public String getString( NamespaceRegistry namespaceRegistry,
605 TextEncoder encoder,
606 TextEncoder delimiterEncoder ) {
607 StringBuilder sb = new StringBuilder();
608 sb.append("{ ");
609 boolean hasPath = this.hasPath();
610 if (hasPath) {
611 sb.append(this.getPath().getString(namespaceRegistry, encoder, delimiterEncoder));
612 }
613 if (this.hasIdProperties()) {
614 if (hasPath) sb.append(" && ");
615 sb.append("[");
616 boolean first = true;
617 for (Property idProperty : this.getIdProperties()) {
618 if (first) first = false;
619 else sb.append(", ");
620 sb.append(idProperty.getString(namespaceRegistry, encoder, delimiterEncoder));
621 }
622 sb.append("]");
623 }
624 sb.append(" }");
625 return sb.toString();
626 }
627
628
629
630
631
632
633 @Override
634 public String toString() {
635 StringBuilder sb = new StringBuilder();
636 boolean hasPath = this.hasPath();
637 boolean hasProps = this.hasIdProperties();
638 if (hasPath) {
639 if (hasProps) {
640 sb.append("<");
641 }
642 sb.append(this.getPath());
643 }
644 if (hasProps) {
645 if (hasPath) sb.append(" && ");
646 sb.append("[");
647 boolean first = true;
648 for (Property idProperty : this.getIdProperties()) {
649 if (first) first = false;
650 else sb.append(", ");
651 sb.append(idProperty);
652 }
653 sb.append("]");
654 if (hasPath) {
655 sb.append(">");
656 }
657 }
658 return sb.toString();
659 }
660
661
662
663
664
665
666
667
668 public abstract Location with( Property newIdProperty );
669
670
671
672
673
674
675
676 public abstract Location with( Path newPath );
677
678
679
680
681
682
683
684
685 public abstract Location with( UUID uuid );
686
687 }