1 /* 2 * #%L 3 * ImmutableDocument.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.impl; 21 22 import java.io.IOException; 23 import java.io.ObjectInputStream; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 30 import com.allanbank.mongodb.bson.DocumentAssignable; 31 import com.allanbank.mongodb.bson.Element; 32 33 /** 34 * A root level document that is truly immutable. 35 * <p> 36 * Documents normally returned from the document builders have the ability to be 37 * modified by injecting an {@code _id} field into the document. This document 38 * class does not have that ability. 39 * </p> 40 * <p> 41 * Most users will not need to use this class except when creating static 42 * documents within classes. The intended usage is then to use the builder when 43 * constructing the immutable document: <blockquote> 44 * 45 * <pre> 46 * <code> 47 * public static final Document QUERY; 48 * 49 * ... 50 * static { 51 * DocumentBuilder builder = BuilderFactory.start(); 52 * 53 * builder.add(...); 54 * ... 55 * 56 * QUERY = new ImmutableDocument(builder); 57 * } 58 * </code> 59 * </pre> 60 * 61 * </blockquote> 62 * 63 * @api.yes This interface is part of the driver's API. Public and protected 64 * members will be deprecated for at least 1 non-bugfix release 65 * (version numbers are <major>.<minor>.<bugfix>) 66 * before being removed or modified. 67 * @copyright 2011-2014, Allanbank Consulting, Inc., All Rights Reserved 68 */ 69 public class ImmutableDocument extends AbstractDocument { 70 71 /** Serialization version for the class. */ 72 private static final long serialVersionUID = -2875918328146027037L; 73 74 /** 75 * Constructed when a user tries to access the elements of the document by 76 * name. 77 */ 78 private volatile Map<String, Element> myElementMap; 79 80 /** The elements of the document. */ 81 private final List<Element> myElements; 82 83 /** The size of the document when encoded as bytes. */ 84 private transient long mySize; 85 86 /** 87 * Constructs a new {@link ImmutableDocument}. 88 * 89 * @param document 90 * The elements for the BSON document. 91 */ 92 public ImmutableDocument(final DocumentAssignable document) { 93 94 final List<Element> elements = document.asDocument().getElements(); 95 96 myElements = Collections.unmodifiableList(new ArrayList<Element>( 97 elements)); 98 myElementMap = null; 99 mySize = computeSize(); 100 } 101 102 /** 103 * Constructs a new {@link ImmutableDocument}. 104 * 105 * @param document 106 * The elements for the BSON document. 107 * @param size 108 * The size of the document when encoded in bytes. If not known 109 * then use the 110 * {@link ImmutableDocument#ImmutableDocument(DocumentAssignable)} 111 * constructor instead. 112 */ 113 public ImmutableDocument(final DocumentAssignable document, final long size) { 114 115 final List<Element> elements = document.asDocument().getElements(); 116 117 myElements = Collections.unmodifiableList(new ArrayList<Element>( 118 elements)); 119 myElementMap = null; 120 mySize = size; 121 } 122 123 /** 124 * Returns the elements in the document. 125 * 126 * @return The elements in the document. 127 */ 128 @Override 129 public List<Element> getElements() { 130 return myElements; 131 } 132 133 /** 134 * Returns the size of the document when encoded as bytes. 135 * 136 * @return The size of the document when encoded as bytes. 137 */ 138 @Override 139 public long size() { 140 return mySize; 141 } 142 143 /** 144 * Returns a map from the element names to the elements in the document. 145 * Used for faster by-name access. 146 * 147 * @return The element name to element mapping. 148 */ 149 @Override 150 protected Map<String, Element> getElementMap() { 151 if (myElementMap == null) { 152 final Map<String, Element> mapping = new HashMap<String, Element>( 153 myElements.size() << 1); 154 155 for (final Element element : myElements) { 156 mapping.put(element.getName(), element); 157 } 158 159 // Swap the finished map into position. 160 myElementMap = mapping; 161 } 162 163 return myElementMap; 164 } 165 166 /** 167 * Computes and returns the length of the document in bytes. 168 * 169 * @return The length of the document in bytes. 170 */ 171 private long computeSize() { 172 long result = 5; // int length (4) + terminal null byte (1). 173 for (final Element element : myElements) { 174 result += element.size(); 175 } 176 177 return result; 178 } 179 180 /** 181 * Sets the transient state of this document. 182 * 183 * @param in 184 * The input stream. 185 * @throws ClassNotFoundException 186 * On a failure loading a class in this classed reachable tree. 187 * @throws IOException 188 * On a failure reading from the stream. 189 */ 190 private void readObject(final ObjectInputStream in) 191 throws ClassNotFoundException, IOException { 192 in.defaultReadObject(); 193 mySize = computeSize(); 194 } 195 }