/driver-core/src/main/com/mongodb/internal/client/model/AbstractConstructibleBson.java

http://github.com/mongodb/mongo-java-driver · Java · 132 lines · 79 code · 18 blank · 35 comment · 8 complexity · 7e0d70972b65f7c2c54ae7dee203c9dc MD5 · raw file

  1. /*
  2. * Copyright 2008-present MongoDB, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.mongodb.internal.client.model;
  17. import com.mongodb.annotations.Immutable;
  18. import org.bson.BsonDocument;
  19. import org.bson.Document;
  20. import org.bson.codecs.configuration.CodecRegistry;
  21. import org.bson.conversions.Bson;
  22. import java.util.Objects;
  23. import java.util.function.Consumer;
  24. /**
  25. * A {@link Bson} that allows constructing new instances via {@link #newAppended(String, Object)} instead of mutating {@code this}.
  26. * While instances are not {@link Immutable immutable},
  27. * {@link BsonDocument#isEmpty() empty} instances are treated specially and are immutable,
  28. * provided that the constructor arguments are not mutated.
  29. *
  30. * @param <S> A type introduced by the concrete class that extends this abstract class.
  31. */
  32. public abstract class AbstractConstructibleBson<S extends AbstractConstructibleBson<S>> implements Bson {
  33. private static final Document EMPTY_APPENDED = new Document();
  34. private final Bson base;
  35. private final Document appended;
  36. protected AbstractConstructibleBson(final Bson base) {
  37. this(base, EMPTY_APPENDED);
  38. }
  39. protected AbstractConstructibleBson(final Bson base, final Document appended) {
  40. this.base = base;
  41. this.appended = appended;
  42. }
  43. protected abstract S newSelf(Bson base, Document appended);
  44. @Override
  45. public final <TDocument> BsonDocument toBsonDocument(final Class<TDocument> documentClass, final CodecRegistry codecRegistry) {
  46. BsonDocument baseDoc = base.toBsonDocument(documentClass, codecRegistry);
  47. return baseDoc.isEmpty() && appended.isEmpty()
  48. // eliminate the possibility of exposing internal state when it is empty to enforce immutability of empty objects
  49. ? new BsonDocument()
  50. : appended.isEmpty() ? baseDoc : newMerged(baseDoc, appended.toBsonDocument(documentClass, codecRegistry));
  51. }
  52. /**
  53. * {@linkplain Document#append(String, Object) Appends} the specified mapping via {@link #newMutated(Consumer)}.
  54. *
  55. * @return A new instance.
  56. */
  57. protected final S newAppended(final String name, final Object value) {
  58. return newMutated(doc -> doc.append(name, value));
  59. }
  60. /**
  61. * Creates a {@link Document#Document(java.util.Map) shallow copy} of {@code this} and mutates it via the specified {@code mutator}.
  62. *
  63. * @return A new instance.
  64. */
  65. protected final S newMutated(final Consumer<Document> mutator) {
  66. Document newAppended = new Document(appended);
  67. mutator.accept(newAppended);
  68. return newSelf(base, newAppended);
  69. }
  70. public static AbstractConstructibleBson<?> of(final Bson doc) {
  71. return doc instanceof AbstractConstructibleBson
  72. // prevent double wrapping
  73. ? (AbstractConstructibleBson<?>) doc
  74. : new ConstructibleBson(doc);
  75. }
  76. @Override
  77. public final boolean equals(final Object o) {
  78. if (this == o) {
  79. return true;
  80. }
  81. if (o == null || getClass() != o.getClass()) {
  82. return false;
  83. }
  84. final AbstractConstructibleBson<?> that = (AbstractConstructibleBson<?>) o;
  85. return Objects.equals(base, that.base) && Objects.equals(appended, that.appended);
  86. }
  87. @Override
  88. public final int hashCode() {
  89. return Objects.hash(base, appended);
  90. }
  91. @Override
  92. public String toString() {
  93. return "{base=" + base
  94. + ", appended=" + appended
  95. + '}';
  96. }
  97. private static BsonDocument newMerged(final BsonDocument base, final BsonDocument appended) {
  98. final BsonDocument result = base.clone();
  99. result.putAll(appended);
  100. return result;
  101. }
  102. private static final class ConstructibleBson extends AbstractConstructibleBson<ConstructibleBson> {
  103. private ConstructibleBson(final Bson base) {
  104. super(base);
  105. }
  106. private ConstructibleBson(final Bson base, final Document appended) {
  107. super(base, appended);
  108. }
  109. @Override
  110. protected ConstructibleBson newSelf(final Bson base, final Document appended) {
  111. return new ConstructibleBson(base, appended);
  112. }
  113. }
  114. }