Coverage Report - com.allanbank.mongodb.bson.element.DocumentElement
 
Classes in this File Line Coverage Branch Coverage Complexity
DocumentElement
100%
179/179
97%
129/132
3.714
 
 1  
 /*
 2  
  * #%L
 3  
  * DocumentElement.java - mongodb-async-driver - Allanbank Consulting, Inc.
 4  
  * %%
 5  
  * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
 6  
  * %%
 7  
  * Licensed under the Apache License, Version 2.0 (the "License");
 8  
  * you may not use this file except in compliance with the License.
 9  
  * You may obtain a copy of the License at
 10  
  * 
 11  
  *      http://www.apache.org/licenses/LICENSE-2.0
 12  
  * 
 13  
  * Unless required by applicable law or agreed to in writing, software
 14  
  * distributed under the License is distributed on an "AS IS" BASIS,
 15  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  
  * See the License for the specific language governing permissions and
 17  
  * limitations under the License.
 18  
  * #L%
 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  
  * Wraps a single BSON document that may contain nested documents.
 48  
  * 
 49  
  * @api.yes This class is part of the driver's API. Public and protected members
 50  
  *          will be deprecated for at least 1 non-bugfix release (version
 51  
  *          numbers are <major>.<minor>.<bugfix>) before being
 52  
  *          removed or modified.
 53  
  * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
 54  
  */
 55  900923
 public class DocumentElement extends AbstractElement implements Document {
 56  
 
 57  
     /** The empty list of elements. */
 58  1
     public static final List<Element> EMPTY_ELEMENTS = Collections.emptyList();
 59  
 
 60  
     /** The BSON type for a document. */
 61  1
     public static final ElementType TYPE = ElementType.DOCUMENT;
 62  
 
 63  
     /** Serialization version for the class. */
 64  
     private static final long serialVersionUID = -564259598403040796L;
 65  
 
 66  
     /**
 67  
      * Computes and returns the number of bytes that are used to encode the
 68  
      * element.
 69  
      * 
 70  
      * @param name
 71  
      *            The name for the BSON array.
 72  
      * @param entries
 73  
      *            The entries in the array.
 74  
      * @return The size of the element when encoded in bytes.
 75  
      */
 76  
     private static long computeSize(final String name,
 77  
             final Collection<Element> entries) {
 78  701565
         long result = 7; // type (1) + name null byte (1) + int length (4) +
 79  
         // element null byte (1).
 80  701565
         result += StringEncoder.utf8Size(name);
 81  701565
         if ((entries != null) && !entries.isEmpty()) {
 82  701314
             for (final Element element : entries) {
 83  1602237
                 result += element.size();
 84  1602237
             }
 85  
         }
 86  
 
 87  701565
         return result;
 88  
     }
 89  
 
 90  
     /**
 91  
      * Computes and returns the number of bytes that are used to encode the
 92  
      * element.
 93  
      * 
 94  
      * @param name
 95  
      *            The name for the BSON array.
 96  
      * @param documentSize
 97  
      *            The size of the document used to construct the element.
 98  
      * @return The size of the element when encoded in bytes.
 99  
      */
 100  
     private static long computeSize(final String name, final long documentSize) {
 101  1317349
         long result = 2; // type (1) + name null byte (1)
 102  1317349
         result += StringEncoder.utf8Size(name);
 103  1317349
         result += documentSize;
 104  
 
 105  1317349
         return result;
 106  
     }
 107  
 
 108  
     /**
 109  
      * Constructed when a user tries to access the elements of the document by
 110  
      * name.
 111  
      */
 112  
     private Map<String, Element> myElementMap;
 113  
 
 114  
     /** The elements of the document. */
 115  
     private final List<Element> myElements;
 116  
 
 117  
     /**
 118  
      * Constructs a new {@link DocumentElement}.
 119  
      * 
 120  
      * @param name
 121  
      *            The name for the BSON document.
 122  
      * @param elements
 123  
      *            The sub-elements for the document.
 124  
      * @throws IllegalArgumentException
 125  
      *             If the {@code name} is <code>null</code>.
 126  
      */
 127  
     public DocumentElement(final String name, final Collection<Element> elements) {
 128  
 
 129  172
         this(name, (elements != null) ? new ArrayList<Element>(elements)
 130  
                 : EMPTY_ELEMENTS, true);
 131  172
     }
 132  
 
 133  
     /**
 134  
      * Constructs a new {@link DocumentElement}.
 135  
      * 
 136  
      * @param name
 137  
      *            The name for the BSON document.
 138  
      * @param value
 139  
      *            The document to copy elements from.
 140  
      * @throws IllegalArgumentException
 141  
      *             If the {@code name} or {@code value} is <code>null</code>.
 142  
      */
 143  
     public DocumentElement(final String name, final Document value) {
 144  1317349
         this(name, (value == null) ? EMPTY_ELEMENTS : value.getElements(),
 145  
                 true, computeSize(name, (value == null) ? EmptyDocument.SIZE
 146  
                         : value.size()));
 147  
 
 148  1317348
         assertNotNull(value, "Document element's sub-document cannot be null.");
 149  1317348
     }
 150  
 
 151  
     /**
 152  
      * Constructs a new {@link DocumentElement} with a single sub-document
 153  
      * element.
 154  
      * 
 155  
      * @param name
 156  
      *            The name for the BSON document.
 157  
      * @param value
 158  
      *            The document to copy elements from.
 159  
      * @throws IllegalArgumentException
 160  
      *             If the {@code name} or {@code value} is <code>null</code>.
 161  
      */
 162  
     public DocumentElement(final String name, final DocumentElement value) {
 163  7
         this(name, (value != null) ? Collections.singletonList((Element) value)
 164  
                 : EMPTY_ELEMENTS, true);
 165  
 
 166  7
         assertNotNull(value, "Document element's sub-document cannot be null.");
 167  7
     }
 168  
 
 169  
     /**
 170  
      * Constructs a new {@link DocumentElement}.
 171  
      * 
 172  
      * @param name
 173  
      *            The name for the BSON document.
 174  
      * @param elements
 175  
      *            The sub-elements for the document.
 176  
      * @throws IllegalArgumentException
 177  
      *             If the {@code name} is <code>null</code>.
 178  
      */
 179  
     public DocumentElement(final String name, final Element... elements) {
 180  287
         this(name, Arrays.asList(elements));
 181  287
     }
 182  
 
 183  
     /**
 184  
      * Constructs a new {@link DocumentElement}.
 185  
      * 
 186  
      * @param name
 187  
      *            The name for the BSON document.
 188  
      * @param elements
 189  
      *            The sub-elements for the document.
 190  
      * @throws IllegalArgumentException
 191  
      *             If the {@code name} is <code>null</code>.
 192  
      */
 193  
     public DocumentElement(final String name, final List<Element> elements) {
 194  301
         this(name, elements, false);
 195  301
     }
 196  
 
 197  
     /**
 198  
      * Constructs a new {@link DocumentElement}.
 199  
      * 
 200  
      * @param name
 201  
      *            The name for the BSON document.
 202  
      * @param elements
 203  
      *            The sub-elements for the document.
 204  
      * @param takeOwnership
 205  
      *            If true this element takes ownership of the list to avoid a
 206  
      *            copy of the list.
 207  
      */
 208  
     public DocumentElement(final String name, final List<Element> elements,
 209  
             final boolean takeOwnership) {
 210  701565
         this(name, elements, takeOwnership, computeSize(name, elements));
 211  701565
     }
 212  
 
 213  
     /**
 214  
      * Constructs a new {@link DocumentElement}.
 215  
      * 
 216  
      * @param name
 217  
      *            The name for the BSON document.
 218  
      * @param elements
 219  
      *            The sub-elements for the document.
 220  
      * @param takeOwnership
 221  
      *            If true this element takes ownership of the list to avoid a
 222  
      *            copy of the list.
 223  
      * @param size
 224  
      *            The size of the element when encoded in bytes. If not known
 225  
      *            then use the
 226  
      *            {@link DocumentElement#DocumentElement(String, List, boolean)}
 227  
      *            constructor instead.
 228  
      */
 229  
     public DocumentElement(final String name, final List<Element> elements,
 230  
             final boolean takeOwnership, final long size) {
 231  
 
 232  2051042
         super(name, size);
 233  
 
 234  2051041
         if ((elements != null) && !elements.isEmpty()) {
 235  2002505
             if (takeOwnership) {
 236  2002227
                 myElements = Collections.unmodifiableList(elements);
 237  
             }
 238  
             else {
 239  278
                 myElements = Collections
 240  
                         .unmodifiableList(new ArrayList<Element>(elements));
 241  
             }
 242  
         }
 243  
         else {
 244  48536
             myElements = EMPTY_ELEMENTS;
 245  
         }
 246  2051041
     }
 247  
 
 248  
     /**
 249  
      * Accepts the visitor and calls the {@link Visitor#visitDocument} method.
 250  
      * 
 251  
      * @see Element#accept(Visitor)
 252  
      */
 253  
     @Override
 254  
     public void accept(final Visitor visitor) {
 255  36783
         if (visitor instanceof SizeAwareVisitor) {
 256  144
             ((SizeAwareVisitor) visitor).visitDocument(getName(),
 257  
                     getElements(), size());
 258  
         }
 259  
         else {
 260  36639
             visitor.visitDocument(getName(), getElements());
 261  
         }
 262  36783
     }
 263  
 
 264  
     /**
 265  
      * {@inheritDoc}
 266  
      * <p>
 267  
      * Returns this element.
 268  
      * </p>
 269  
      */
 270  
     @Override
 271  
     public Document asDocument() {
 272  81
         return this;
 273  
     }
 274  
 
 275  
     /**
 276  
      * Returns this sub-document as a {@link DocumentReference} if it conforms
 277  
      * to the MongoDB DBRef convention. Returns <code>null</code> otherwise.
 278  
      * <p>
 279  
      * A DocumentReference contains (order matters):
 280  
      * <ol>
 281  
      * <li>The name of the collection where the referenced document resides:
 282  
      * {@code $ref}.</li>
 283  
      * <li>The value of the _id field in the referenced document: {@code $id}.</li>
 284  
      * <li>The name of the database where the referenced document resides:
 285  
      * {@code $db} (Optional).</li>
 286  
      * </ol>
 287  
      * 
 288  
      * @return This sub-document as a {@link DocumentReference} if it conforms
 289  
      *         to the MongoDB DBRef convention. Returns <code>null</code>
 290  
      *         otherwise.
 291  
      * 
 292  
      * @see #isDocumentReference()
 293  
      * @see <a
 294  
      *      href="http://docs.mongodb.org/manual/applications/database-references/#dbref">MongoDB
 295  
      *      DBRef Information</a>
 296  
      */
 297  
     public DocumentReference asDocumentReference() {
 298  70
         final int elementCount = myElements.size();
 299  70
         if (elementCount == 2) {
 300  15
             final Element element1 = myElements.get(0);
 301  15
             final Element element2 = myElements.get(1);
 302  
 
 303  15
             final String element1Name = element1.getName();
 304  15
             final ElementType element1Type = element1.getType();
 305  15
             final String element2Name = element2.getName();
 306  
 
 307  15
             if (DocumentReference.COLLECTION_FIELD_NAME.equals(element1Name)
 308  
                     && DocumentReference.ID_FIELD_NAME.equals(element2Name)) {
 309  9
                 if (element1Type == ElementType.STRING) {
 310  3
                     return new DocumentReference(
 311  
                             ((StringElement) element1).getValue(), element2);
 312  
                 }
 313  6
                 else if (element1Type == ElementType.SYMBOL) {
 314  3
                     return new DocumentReference(
 315  
                             ((SymbolElement) element1).getSymbol(), element2);
 316  
                 }
 317  
             }
 318  9
         }
 319  55
         else if (myElements.size() == 3) {
 320  54
             final Element element1 = myElements.get(0);
 321  54
             final Element element2 = myElements.get(1);
 322  54
             final Element element3 = myElements.get(2);
 323  
 
 324  54
             final String element1Name = element1.getName();
 325  54
             final ElementType element1Type = element1.getType();
 326  54
             final String element2Name = element2.getName();
 327  54
             final String element3Name = element3.getName();
 328  54
             final ElementType element3Type = element3.getType();
 329  
 
 330  54
             if (DocumentReference.COLLECTION_FIELD_NAME.equals(element1Name)
 331  
                     && DocumentReference.ID_FIELD_NAME.equals(element2Name)
 332  
                     && DocumentReference.DATABASE_FIELD_NAME
 333  
                             .equals(element3Name)) {
 334  27
                 if (element1Type == ElementType.STRING) {
 335  9
                     if (element3Type == ElementType.STRING) {
 336  3
                         return new DocumentReference(
 337  
                                 ((StringElement) element3).getValue(),
 338  
                                 ((StringElement) element1).getValue(), element2);
 339  
                     }
 340  6
                     else if (element3Type == ElementType.SYMBOL) {
 341  3
                         return new DocumentReference(
 342  
                                 ((SymbolElement) element3).getSymbol(),
 343  
                                 ((StringElement) element1).getValue(), element2);
 344  
                     }
 345  
                 }
 346  18
                 else if (element1Type == ElementType.SYMBOL) {
 347  9
                     if (element3Type == ElementType.STRING) {
 348  3
                         return new DocumentReference(
 349  
                                 ((StringElement) element3).getValue(),
 350  
                                 ((SymbolElement) element1).getSymbol(),
 351  
                                 element2);
 352  
                     }
 353  6
                     else if (element3Type == ElementType.SYMBOL) {
 354  3
                         return new DocumentReference(
 355  
                                 ((SymbolElement) element3).getSymbol(),
 356  
                                 ((SymbolElement) element1).getSymbol(),
 357  
                                 element2);
 358  
                     }
 359  
                 }
 360  
             }
 361  
         }
 362  52
         return null;
 363  
     }
 364  
 
 365  
     /**
 366  
      * {@inheritDoc}
 367  
      * <p>
 368  
      * Overridden to compare the elements of the document if the base class
 369  
      * comparison is equals.
 370  
      * </p>
 371  
      */
 372  
     @Override
 373  
     public int compareTo(final Element otherElement) {
 374  8
         int result = super.compareTo(otherElement);
 375  
 
 376  8
         if (result == 0) {
 377  5
             final DocumentElement other = (DocumentElement) otherElement;
 378  5
             final int length = Math.min(myElements.size(),
 379  
                     other.myElements.size());
 380  8
             for (int i = 0; i < length; ++i) {
 381  5
                 result = myElements.get(i).compareTo(other.myElements.get(i));
 382  5
                 if (result != 0) {
 383  2
                     return result;
 384  
                 }
 385  
             }
 386  
 
 387  3
             result = myElements.size() - other.myElements.size();
 388  
         }
 389  
 
 390  6
         return result;
 391  
     }
 392  
 
 393  
     /**
 394  
      * Returns true if the document contains an element with the specified name.
 395  
      * 
 396  
      * @param name
 397  
      *            The name of the element to locate.
 398  
      * @return True if the document contains an element with the given name,
 399  
      *         false otherwise.
 400  
      * @see Document#contains(String)
 401  
      */
 402  
     @Override
 403  
     public boolean contains(final String name) {
 404  2
         return getElementMap().containsKey(name);
 405  
     }
 406  
 
 407  
     /**
 408  
      * Determines if the passed object is of this same type as this object and
 409  
      * if so that its fields are equal.
 410  
      * 
 411  
      * @param object
 412  
      *            The object to compare to.
 413  
      * 
 414  
      * @see java.lang.Object#equals(java.lang.Object)
 415  
      */
 416  
     @Override
 417  
     public boolean equals(final Object object) {
 418  35252
         boolean result = false;
 419  35252
         if (this == object) {
 420  8
             result = true;
 421  
         }
 422  35244
         else if ((object != null) && (getClass() == object.getClass())) {
 423  35217
             final DocumentElement other = (DocumentElement) object;
 424  
 
 425  35217
             result = super.equals(object)
 426  
                     && myElements.equals(other.myElements);
 427  
         }
 428  35252
         return result;
 429  
     }
 430  
 
 431  
     /**
 432  
      * {@inheritDoc}
 433  
      * <p>
 434  
      * Searches this sub-elements for matching elements on the path and are of
 435  
      * the right type.
 436  
      * </p>
 437  
      * 
 438  
      * @see Element#find
 439  
      */
 440  
     @Override
 441  
     public <E extends Element> List<E> find(final Class<E> clazz,
 442  
             final String... nameRegexs) {
 443  76
         List<E> elements = Collections.emptyList();
 444  
 
 445  76
         if (0 < nameRegexs.length) {
 446  41
             final String nameRegex = nameRegexs[0];
 447  41
             final String[] subNameRegexs = Arrays.copyOfRange(nameRegexs, 1,
 448  
                     nameRegexs.length);
 449  
 
 450  41
             elements = new ArrayList<E>();
 451  
             try {
 452  41
                 final Pattern pattern = PatternUtils.toPattern(nameRegex);
 453  39
                 for (final Element element : myElements) {
 454  45
                     if (pattern.matcher(element.getName()).matches()) {
 455  38
                         elements.addAll(element.find(clazz, subNameRegexs));
 456  
                     }
 457  45
                 }
 458  
             }
 459  2
             catch (final PatternSyntaxException pse) {
 460  
                 // Assume a non-pattern?
 461  2
                 for (final Element element : myElements) {
 462  2
                     if (nameRegex.equals(element.getName())) {
 463  1
                         elements.addAll(element.find(clazz, subNameRegexs));
 464  
                     }
 465  2
                 }
 466  39
             }
 467  41
         }
 468  
         else {
 469  
             // End of the path -- are we the right type/element?
 470  35
             if (clazz.isAssignableFrom(this.getClass())) {
 471  34
                 elements = Collections.singletonList(clazz.cast(this));
 472  
             }
 473  
         }
 474  76
         return elements;
 475  
     }
 476  
 
 477  
     /**
 478  
      * {@inheritDoc}
 479  
      * <p>
 480  
      * Searches this sub-elements for matching elements on the path and are of
 481  
      * the right type.
 482  
      * </p>
 483  
      * 
 484  
      * @see Element#findFirst
 485  
      */
 486  
     @Override
 487  
     public <E extends Element> E findFirst(final Class<E> clazz,
 488  
             final String... nameRegexs) {
 489  49
         E element = null;
 490  49
         if (0 < nameRegexs.length) {
 491  11
             final String nameRegex = nameRegexs[0];
 492  11
             final String[] subNameRegexs = Arrays.copyOfRange(nameRegexs, 1,
 493  
                     nameRegexs.length);
 494  
 
 495  
             try {
 496  11
                 final Pattern pattern = PatternUtils.toPattern(nameRegex);
 497  7
                 final Iterator<Element> iter = myElements.iterator();
 498  16
                 while (iter.hasNext() && (element == null)) {
 499  9
                     final Element docElement = iter.next();
 500  9
                     if (pattern.matcher(docElement.getName()).matches()) {
 501  6
                         element = docElement.findFirst(clazz, subNameRegexs);
 502  
                     }
 503  9
                 }
 504  
             }
 505  4
             catch (final PatternSyntaxException pse) {
 506  
                 // Assume a non-pattern?
 507  4
                 final Iterator<Element> iter = myElements.iterator();
 508  8
                 while (iter.hasNext() && (element == null)) {
 509  4
                     final Element docElement = iter.next();
 510  4
                     if (nameRegex.equals(docElement.getName())) {
 511  3
                         element = docElement.findFirst(clazz, subNameRegexs);
 512  
                     }
 513  4
                 }
 514  7
             }
 515  11
         }
 516  
         else {
 517  
             // End of the path -- are we the right type/element?
 518  38
             if (clazz.isAssignableFrom(this.getClass())) {
 519  37
                 element = clazz.cast(this);
 520  
             }
 521  
         }
 522  49
         return element;
 523  
     }
 524  
 
 525  
     /**
 526  
      * Returns the element with the specified name and type or null if no
 527  
      * element with that name and type exists.
 528  
      * 
 529  
      * @param <E>
 530  
      *            The type of element to get.
 531  
      * @param clazz
 532  
      *            The class of element to get.
 533  
      * @param name
 534  
      *            The name of the element to locate.
 535  
      * @return The sub-element in the document with the given name or null if
 536  
      *         element exists with the given name.
 537  
      * @see Document#get(Class, String)
 538  
      */
 539  
     @Override
 540  
     public <E extends Element> E get(final Class<E> clazz, final String name) {
 541  36
         final Element element = get(name);
 542  36
         if ((element != null) && clazz.isAssignableFrom(element.getClass())) {
 543  34
             return clazz.cast(element);
 544  
         }
 545  2
         return null;
 546  
     }
 547  
 
 548  
     /**
 549  
      * Returns the element with the specified name or null if no element with
 550  
      * that name exists.
 551  
      * 
 552  
      * @param name
 553  
      *            The name of the element to locate.
 554  
      * @return The sub-element in the document with the given name or null if
 555  
      *         element exists with the given name.
 556  
      * @see Document#get(String)
 557  
      */
 558  
     @Override
 559  
     public Element get(final String name) {
 560  45
         return getElementMap().get(name);
 561  
     }
 562  
 
 563  
     /**
 564  
      * Returns the element's document.
 565  
      * 
 566  
      * @return The document contained within the element.
 567  
      */
 568  
     public Document getDocument() {
 569  20
         return new RootDocument(myElements);
 570  
     }
 571  
 
 572  
     /**
 573  
      * Returns the elements in the document.
 574  
      * 
 575  
      * @return The elements in the document.
 576  
      */
 577  
     @Override
 578  
     public List<Element> getElements() {
 579  36886
         return myElements;
 580  
     }
 581  
 
 582  
     /**
 583  
      * {@inheritDoc}
 584  
      */
 585  
     @Override
 586  
     public ElementType getType() {
 587  26
         return TYPE;
 588  
     }
 589  
 
 590  
     /**
 591  
      * {@inheritDoc}
 592  
      * <p>
 593  
      * Returns a stand-alone {@link Document}.
 594  
      * </p>
 595  
      */
 596  
     @Override
 597  
     public Document getValueAsObject() {
 598  1
         return BuilderFactory.start(this).build();
 599  
     }
 600  
 
 601  
     /**
 602  
      * Computes a reasonable hash code.
 603  
      * 
 604  
      * @return The hash code value.
 605  
      */
 606  
     @Override
 607  
     public int hashCode() {
 608  4842
         int result = 1;
 609  4842
         result = (31 * result) + super.hashCode();
 610  4842
         result = (31 * result) + myElements.hashCode();
 611  4842
         return result;
 612  
     }
 613  
 
 614  
     /**
 615  
      * Returns true if this sub-document conforms to the MongoDB DBRef
 616  
      * convention, false otherwise.
 617  
      * <p>
 618  
      * A DocumentReference contains (order matters):
 619  
      * <ol>
 620  
      * <li>The name (string or symbol) of the collection where the referenced
 621  
      * document resides: {@code $ref}.</li>
 622  
      * <li>The value of the _id field in the referenced document: {@code $id}.</li>
 623  
      * <li>The name (string or symbol) of the database where the referenced
 624  
      * document resides: {@code $db} (Optional).</li>
 625  
      * </ol>
 626  
      * 
 627  
      * @return True if this sub-document conforms to the MongoDB DBRef
 628  
      *         convention, false otherwise.
 629  
      * 
 630  
      * @see #asDocumentReference()
 631  
      * @see DocumentReference
 632  
      * @see <a
 633  
      *      href="http://docs.mongodb.org/manual/applications/database-references/#dbref">MongoDB
 634  
      *      DBRef Information</a>
 635  
      */
 636  
     public boolean isDocumentReference() {
 637  70
         final int elementCount = myElements.size();
 638  70
         if (elementCount == 2) {
 639  15
             final Element element1 = myElements.get(0);
 640  15
             final Element element2 = myElements.get(1);
 641  
 
 642  15
             final String element1Name = element1.getName();
 643  15
             final ElementType element1Type = element1.getType();
 644  15
             final String element2Name = element2.getName();
 645  
 
 646  15
             return DocumentReference.COLLECTION_FIELD_NAME.equals(element1Name)
 647  
                     && ((element1Type == ElementType.STRING) || (element1Type == ElementType.SYMBOL))
 648  
                     && DocumentReference.ID_FIELD_NAME.equals(element2Name);
 649  
         }
 650  55
         else if (myElements.size() == 3) {
 651  54
             final Element element1 = myElements.get(0);
 652  54
             final Element element2 = myElements.get(1);
 653  54
             final Element element3 = myElements.get(2);
 654  
 
 655  54
             final String element1Name = element1.getName();
 656  54
             final ElementType element1Type = element1.getType();
 657  54
             final String element2Name = element2.getName();
 658  54
             final String element3Name = element3.getName();
 659  54
             final ElementType element3Type = element3.getType();
 660  
 
 661  54
             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  1
         return false;
 670  
     }
 671  
 
 672  
     /**
 673  
      * Returns an iterator over the documents elements.
 674  
      * 
 675  
      * @see Iterable#iterator()
 676  
      */
 677  
     @Override
 678  
     public Iterator<Element> iterator() {
 679  26
         return getElements().iterator();
 680  
     }
 681  
 
 682  
     /**
 683  
      * {@inheritDoc}
 684  
      * <p>
 685  
      * Returns a new {@link DocumentElement}.
 686  
      * </p>
 687  
      */
 688  
     @Override
 689  
     public DocumentElement withName(final String name) {
 690  900928
         if (getName().equals(name)) {
 691  900922
             return this;
 692  
         }
 693  6
         return new DocumentElement(name, myElements);
 694  
     }
 695  
 
 696  
     /**
 697  
      * Returns a map from the element names to the elements in the document.
 698  
      * Used for faster by-name access.
 699  
      * 
 700  
      * @return The element name to element mapping.
 701  
      */
 702  
     private Map<String, Element> getElementMap() {
 703  47
         if (myElementMap == null) {
 704  23
             final List<Element> elements = myElements;
 705  23
             final Map<String, Element> mapping = new HashMap<String, Element>(
 706  
                     elements.size() + elements.size());
 707  
 
 708  23
             for (final Element element : elements) {
 709  51
                 mapping.put(element.getName(), element);
 710  51
             }
 711  
 
 712  
             // Swap the finished map into position.
 713  23
             myElementMap = mapping;
 714  
         }
 715  
 
 716  47
         return myElementMap;
 717  
     }
 718  
 }