1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.allanbank.mongodb.bson.element;
21
22 import static com.allanbank.mongodb.util.Assertions.assertNotNull;
23
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.regex.Pattern;
33 import java.util.regex.PatternSyntaxException;
34
35 import com.allanbank.mongodb.bson.Document;
36 import com.allanbank.mongodb.bson.DocumentReference;
37 import com.allanbank.mongodb.bson.Element;
38 import com.allanbank.mongodb.bson.ElementType;
39 import com.allanbank.mongodb.bson.Visitor;
40 import com.allanbank.mongodb.bson.builder.BuilderFactory;
41 import com.allanbank.mongodb.bson.impl.EmptyDocument;
42 import com.allanbank.mongodb.bson.impl.RootDocument;
43 import com.allanbank.mongodb.bson.io.StringEncoder;
44 import com.allanbank.mongodb.util.PatternUtils;
45
46
47
48
49
50
51
52
53
54
55 public class DocumentElement extends AbstractElement implements Document {
56
57
58 public static final List<Element> EMPTY_ELEMENTS = Collections.emptyList();
59
60
61 public static final ElementType TYPE = ElementType.DOCUMENT;
62
63
64 private static final long serialVersionUID = -564259598403040796L;
65
66
67
68
69
70
71
72
73
74
75
76 private static long computeSize(final String name,
77 final Collection<Element> entries) {
78 long result = 7;
79
80 result += StringEncoder.utf8Size(name);
81 if ((entries != null) && !entries.isEmpty()) {
82 for (final Element element : entries) {
83 result += element.size();
84 }
85 }
86
87 return result;
88 }
89
90
91
92
93
94
95
96
97
98
99
100 private static long computeSize(final String name, final long documentSize) {
101 long result = 2;
102 result += StringEncoder.utf8Size(name);
103 result += documentSize;
104
105 return result;
106 }
107
108
109
110
111
112 private Map<String, Element> myElementMap;
113
114
115 private final List<Element> myElements;
116
117
118
119
120
121
122
123
124
125
126
127 public DocumentElement(final String name, final Collection<Element> elements) {
128
129 this(name, (elements != null) ? new ArrayList<Element>(elements)
130 : EMPTY_ELEMENTS, true);
131 }
132
133
134
135
136
137
138
139
140
141
142
143 public DocumentElement(final String name, final Document value) {
144 this(name, (value == null) ? EMPTY_ELEMENTS : value.getElements(),
145 true, computeSize(name, (value == null) ? EmptyDocument.SIZE
146 : value.size()));
147
148 assertNotNull(value, "Document element's sub-document cannot be null.");
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162 public DocumentElement(final String name, final DocumentElement value) {
163 this(name, (value != null) ? Collections.singletonList((Element) value)
164 : EMPTY_ELEMENTS, true);
165
166 assertNotNull(value, "Document element's sub-document cannot be null.");
167 }
168
169
170
171
172
173
174
175
176
177
178
179 public DocumentElement(final String name, final Element... elements) {
180 this(name, Arrays.asList(elements));
181 }
182
183
184
185
186
187
188
189
190
191
192
193 public DocumentElement(final String name, final List<Element> elements) {
194 this(name, elements, false);
195 }
196
197
198
199
200
201
202
203
204
205
206
207
208 public DocumentElement(final String name, final List<Element> elements,
209 final boolean takeOwnership) {
210 this(name, elements, takeOwnership, computeSize(name, elements));
211 }
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229 public DocumentElement(final String name, final List<Element> elements,
230 final boolean takeOwnership, final long size) {
231
232 super(name, size);
233
234 if ((elements != null) && !elements.isEmpty()) {
235 if (takeOwnership) {
236 myElements = Collections.unmodifiableList(elements);
237 }
238 else {
239 myElements = Collections
240 .unmodifiableList(new ArrayList<Element>(elements));
241 }
242 }
243 else {
244 myElements = EMPTY_ELEMENTS;
245 }
246 }
247
248
249
250
251
252
253 @Override
254 public void accept(final Visitor visitor) {
255 if (visitor instanceof SizeAwareVisitor) {
256 ((SizeAwareVisitor) visitor).visitDocument(getName(),
257 getElements(), size());
258 }
259 else {
260 visitor.visitDocument(getName(), getElements());
261 }
262 }
263
264
265
266
267
268
269
270 @Override
271 public Document asDocument() {
272 return this;
273 }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297 public DocumentReference asDocumentReference() {
298 final int elementCount = myElements.size();
299 if (elementCount == 2) {
300 final Element element1 = myElements.get(0);
301 final Element element2 = myElements.get(1);
302
303 final String element1Name = element1.getName();
304 final ElementType element1Type = element1.getType();
305 final String element2Name = element2.getName();
306
307 if (DocumentReference.COLLECTION_FIELD_NAME.equals(element1Name)
308 && DocumentReference.ID_FIELD_NAME.equals(element2Name)) {
309 if (element1Type == ElementType.STRING) {
310 return new DocumentReference(
311 ((StringElement) element1).getValue(), element2);
312 }
313 else if (element1Type == ElementType.SYMBOL) {
314 return new DocumentReference(
315 ((SymbolElement) element1).getSymbol(), element2);
316 }
317 }
318 }
319 else if (myElements.size() == 3) {
320 final Element element1 = myElements.get(0);
321 final Element element2 = myElements.get(1);
322 final Element element3 = myElements.get(2);
323
324 final String element1Name = element1.getName();
325 final ElementType element1Type = element1.getType();
326 final String element2Name = element2.getName();
327 final String element3Name = element3.getName();
328 final ElementType element3Type = element3.getType();
329
330 if (DocumentReference.COLLECTION_FIELD_NAME.equals(element1Name)
331 && DocumentReference.ID_FIELD_NAME.equals(element2Name)
332 && DocumentReference.DATABASE_FIELD_NAME
333 .equals(element3Name)) {
334 if (element1Type == ElementType.STRING) {
335 if (element3Type == ElementType.STRING) {
336 return new DocumentReference(
337 ((StringElement) element3).getValue(),
338 ((StringElement) element1).getValue(), element2);
339 }
340 else if (element3Type == ElementType.SYMBOL) {
341 return new DocumentReference(
342 ((SymbolElement) element3).getSymbol(),
343 ((StringElement) element1).getValue(), element2);
344 }
345 }
346 else if (element1Type == ElementType.SYMBOL) {
347 if (element3Type == ElementType.STRING) {
348 return new DocumentReference(
349 ((StringElement) element3).getValue(),
350 ((SymbolElement) element1).getSymbol(),
351 element2);
352 }
353 else if (element3Type == ElementType.SYMBOL) {
354 return new DocumentReference(
355 ((SymbolElement) element3).getSymbol(),
356 ((SymbolElement) element1).getSymbol(),
357 element2);
358 }
359 }
360 }
361 }
362 return null;
363 }
364
365
366
367
368
369
370
371
372 @Override
373 public int compareTo(final Element otherElement) {
374 int result = super.compareTo(otherElement);
375
376 if (result == 0) {
377 final DocumentElement other = (DocumentElement) otherElement;
378 final int length = Math.min(myElements.size(),
379 other.myElements.size());
380 for (int i = 0; i < length; ++i) {
381 result = myElements.get(i).compareTo(other.myElements.get(i));
382 if (result != 0) {
383 return result;
384 }
385 }
386
387 result = myElements.size() - other.myElements.size();
388 }
389
390 return result;
391 }
392
393
394
395
396
397
398
399
400
401
402 @Override
403 public boolean contains(final String name) {
404 return getElementMap().containsKey(name);
405 }
406
407
408
409
410
411
412
413
414
415
416 @Override
417 public boolean equals(final Object object) {
418 boolean result = false;
419 if (this == object) {
420 result = true;
421 }
422 else if ((object != null) && (getClass() == object.getClass())) {
423 final DocumentElement other = (DocumentElement) object;
424
425 result = super.equals(object)
426 && myElements.equals(other.myElements);
427 }
428 return result;
429 }
430
431
432
433
434
435
436
437
438
439
440 @Override
441 public <E extends Element> List<E> find(final Class<E> clazz,
442 final String... nameRegexs) {
443 List<E> elements = Collections.emptyList();
444
445 if (0 < nameRegexs.length) {
446 final String nameRegex = nameRegexs[0];
447 final String[] subNameRegexs = Arrays.copyOfRange(nameRegexs, 1,
448 nameRegexs.length);
449
450 elements = new ArrayList<E>();
451 try {
452 final Pattern pattern = PatternUtils.toPattern(nameRegex);
453 for (final Element element : myElements) {
454 if (pattern.matcher(element.getName()).matches()) {
455 elements.addAll(element.find(clazz, subNameRegexs));
456 }
457 }
458 }
459 catch (final PatternSyntaxException pse) {
460
461 for (final Element element : myElements) {
462 if (nameRegex.equals(element.getName())) {
463 elements.addAll(element.find(clazz, subNameRegexs));
464 }
465 }
466 }
467 }
468 else {
469
470 if (clazz.isAssignableFrom(this.getClass())) {
471 elements = Collections.singletonList(clazz.cast(this));
472 }
473 }
474 return elements;
475 }
476
477
478
479
480
481
482
483
484
485
486 @Override
487 public <E extends Element> E findFirst(final Class<E> clazz,
488 final String... nameRegexs) {
489 E element = null;
490 if (0 < nameRegexs.length) {
491 final String nameRegex = nameRegexs[0];
492 final String[] subNameRegexs = Arrays.copyOfRange(nameRegexs, 1,
493 nameRegexs.length);
494
495 try {
496 final Pattern pattern = PatternUtils.toPattern(nameRegex);
497 final Iterator<Element> iter = myElements.iterator();
498 while (iter.hasNext() && (element == null)) {
499 final Element docElement = iter.next();
500 if (pattern.matcher(docElement.getName()).matches()) {
501 element = docElement.findFirst(clazz, subNameRegexs);
502 }
503 }
504 }
505 catch (final PatternSyntaxException pse) {
506
507 final Iterator<Element> iter = myElements.iterator();
508 while (iter.hasNext() && (element == null)) {
509 final Element docElement = iter.next();
510 if (nameRegex.equals(docElement.getName())) {
511 element = docElement.findFirst(clazz, subNameRegexs);
512 }
513 }
514 }
515 }
516 else {
517
518 if (clazz.isAssignableFrom(this.getClass())) {
519 element = clazz.cast(this);
520 }
521 }
522 return element;
523 }
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539 @Override
540 public <E extends Element> E get(final Class<E> clazz, final String name) {
541 final Element element = get(name);
542 if ((element != null) && clazz.isAssignableFrom(element.getClass())) {
543 return clazz.cast(element);
544 }
545 return null;
546 }
547
548
549
550
551
552
553
554
555
556
557
558 @Override
559 public Element get(final String name) {
560 return getElementMap().get(name);
561 }
562
563
564
565
566
567
568 public Document getDocument() {
569 return new RootDocument(myElements);
570 }
571
572
573
574
575
576
577 @Override
578 public List<Element> getElements() {
579 return myElements;
580 }
581
582
583
584
585 @Override
586 public ElementType getType() {
587 return TYPE;
588 }
589
590
591
592
593
594
595
596 @Override
597 public Document getValueAsObject() {
598 return BuilderFactory.start(this).build();
599 }
600
601
602
603
604
605
606 @Override
607 public int hashCode() {
608 int result = 1;
609 result = (31 * result) + super.hashCode();
610 result = (31 * result) + myElements.hashCode();
611 return result;
612 }
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636 public boolean isDocumentReference() {
637 final int elementCount = myElements.size();
638 if (elementCount == 2) {
639 final Element element1 = myElements.get(0);
640 final Element element2 = myElements.get(1);
641
642 final String element1Name = element1.getName();
643 final ElementType element1Type = element1.getType();
644 final String element2Name = element2.getName();
645
646 return DocumentReference.COLLECTION_FIELD_NAME.equals(element1Name)
647 && ((element1Type == ElementType.STRING) || (element1Type == ElementType.SYMBOL))
648 && DocumentReference.ID_FIELD_NAME.equals(element2Name);
649 }
650 else if (myElements.size() == 3) {
651 final Element element1 = myElements.get(0);
652 final Element element2 = myElements.get(1);
653 final Element element3 = myElements.get(2);
654
655 final String element1Name = element1.getName();
656 final ElementType element1Type = element1.getType();
657 final String element2Name = element2.getName();
658 final String element3Name = element3.getName();
659 final ElementType element3Type = element3.getType();
660
661 return DocumentReference.COLLECTION_FIELD_NAME.equals(element1Name)
662 && ((element1Type == ElementType.STRING) || (element1Type == ElementType.SYMBOL))
663 && DocumentReference.ID_FIELD_NAME.equals(element2Name)
664 && DocumentReference.DATABASE_FIELD_NAME
665 .equals(element3Name)
666 && ((element3Type == ElementType.STRING) || (element3Type == ElementType.SYMBOL));
667
668 }
669 return false;
670 }
671
672
673
674
675
676
677 @Override
678 public Iterator<Element> iterator() {
679 return getElements().iterator();
680 }
681
682
683
684
685
686
687
688 @Override
689 public DocumentElement withName(final String name) {
690 if (getName().equals(name)) {
691 return this;
692 }
693 return new DocumentElement(name, myElements);
694 }
695
696
697
698
699
700
701
702 private Map<String, Element> getElementMap() {
703 if (myElementMap == null) {
704 final List<Element> elements = myElements;
705 final Map<String, Element> mapping = new HashMap<String, Element>(
706 elements.size() + elements.size());
707
708 for (final Element element : elements) {
709 mapping.put(element.getName(), element);
710 }
711
712
713 myElementMap = mapping;
714 }
715
716 return myElementMap;
717 }
718 }