1 /*
2 * #%L
3 * AbstractBuilder.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.builder.impl;
21
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.util.ArrayList;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Set;
28
29 import com.allanbank.mongodb.bson.Element;
30 import com.allanbank.mongodb.bson.ElementType;
31 import com.allanbank.mongodb.bson.Visitor;
32 import com.allanbank.mongodb.bson.builder.ArrayBuilder;
33 import com.allanbank.mongodb.bson.builder.Builder;
34 import com.allanbank.mongodb.bson.builder.DocumentBuilder;
35 import com.allanbank.mongodb.bson.element.AbstractElement;
36
37 /**
38 * Base class with common functionality for the all builders. A builder is
39 * responsible for constructing a single level of the BSON document.
40 *
41 * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
42 * mutated in incompatible ways between any two releases of the driver.
43 * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
44 */
45 public abstract class AbstractBuilder implements Builder {
46
47 /** If true then assertions have been enabled for the class. */
48 protected static final boolean ASSERTIONS_ENABLED;
49
50 /** The class used for intermediate sub-builders in the elements list. */
51 protected static final Class<BuilderElement> BUILDER_ELEMENT_CLASS;
52
53 static {
54 BUILDER_ELEMENT_CLASS = BuilderElement.class;
55 ASSERTIONS_ENABLED = AbstractBuilder.class.desiredAssertionStatus();
56 }
57
58 /** The list of elements in the builder. */
59 protected final List<Element> myElements;
60
61 /** The size of the document added. */
62 protected long mySize;
63
64 /** The outer scope to this builder. */
65 private final AbstractBuilder myOuterBuilder;
66
67 /**
68 * Creates a new builder.
69 *
70 * @param outerBuilder
71 * The outer scoped builder.
72 */
73 public AbstractBuilder(final AbstractBuilder outerBuilder) {
74 super();
75 myOuterBuilder = outerBuilder;
76 myElements = new ArrayList<Element>(32);
77 mySize = 0;
78 }
79
80 /**
81 * {@inheritDoc}
82 */
83 @Override
84 public Builder pop() {
85 return myOuterBuilder;
86 }
87
88 /**
89 * {@inheritDoc}
90 */
91 @Override
92 public Builder reset() {
93 myElements.clear();
94 return this;
95 }
96
97 /**
98 * Constructs the final form of the element being constructed.
99 *
100 * @param name
101 * The name of the element.
102 * @return The Element constructed by the builder.
103 */
104 protected abstract Element build(String name);
105
106 /**
107 * Pushes a context for constructing a sub-document.
108 *
109 * @param name
110 * The name of the sub-document.
111 * @return A {@link DocumentBuilder} for constructing the sub-document.
112 */
113
114 protected DocumentBuilder doPush(final String name) {
115 final DocumentBuilderImpl pushed = new DocumentBuilderImpl(this);
116 myElements.add(new BuilderElement(name, pushed));
117 return pushed;
118 }
119
120 /**
121 * Pushes a context for constructing a sub-array.
122 *
123 * @param name
124 * The name of the sub-array.
125 * @return A {@link ArrayBuilder} for constructing the sub-array.
126 */
127 protected ArrayBuilder doPushArray(final String name) {
128 final ArrayBuilderImpl pushed = new ArrayBuilderImpl(this);
129 myElements.add(new BuilderElement(name, pushed));
130 return pushed;
131 }
132
133 /**
134 * Renders the final form of the sub elements in the builder replacing all
135 * {@link BuilderElement}s with the final element form.
136 *
137 * @return The final sub element list.
138 */
139 protected List<Element> subElements() {
140 final List<Element> elements = new ArrayList<Element>(myElements.size());
141
142 Set<String> names = null;
143 for (Element element : myElements) {
144 if (element.getClass() == BUILDER_ELEMENT_CLASS) {
145 element = ((BuilderElement) element).build();
146 }
147
148 if (ASSERTIONS_ENABLED) {
149 if (names == null) {
150 names = new HashSet<String>(myElements.size() << 1);
151 }
152 final String name = element.getName();
153 if (!names.add(name)) {
154 assert false : name + " is not unique in " + myElements;
155 }
156 }
157
158 elements.add(element);
159 }
160
161 return elements;
162 }
163
164 /**
165 * A temporary Element to stand in for a element being constructed with a
166 * builder.
167 * <p>
168 * <b>Note:</b> This class if final to allow the class comparison in
169 * {@link AbstractBuilder}.subElements() method.
170 * </p>
171 */
172 public static final class BuilderElement extends AbstractElement {
173
174 /** Serialization version for the class. */
175 private static final long serialVersionUID = 4421203621373216989L;
176
177 /** The encapsulated builder. */
178 private transient AbstractBuilder myBuilder;
179
180 /**
181 * Creates a new {@link BuilderElement}.
182 *
183 * @param name
184 * The name for the element to build.
185 * @param builder
186 * The Builder doing the building.
187 */
188 public BuilderElement(final String name, final AbstractBuilder builder) {
189 super(name, 0);
190 myBuilder = builder;
191 }
192
193 /**
194 * {@inheritDoc}
195 */
196 @Override
197 public void accept(final Visitor visitor) {
198 build().accept(visitor);
199 }
200
201 /**
202 * Constructs the final form of the element being constructed by the
203 * encapsulated builder.
204 *
205 * @return The Element constructed by the encapsulated builder.
206 */
207 public Element build() {
208 return myBuilder.build(getName());
209 }
210
211 /**
212 * {@inheritDoc}
213 */
214 @Override
215 public ElementType getType() {
216 return null;
217 }
218
219 /**
220 * {@inheritDoc}
221 * <p>
222 * Overridden to return null as this class should not be seen outside of
223 * the builders.
224 * </p>
225 */
226 @Override
227 public Object getValueAsObject() {
228 return null;
229 }
230
231 /**
232 * {@inheritDoc}
233 * <p>
234 * Returns a new {@link BuilderElement}.
235 * </p>
236 */
237 @Override
238 public BuilderElement withName(final String name) {
239 return new BuilderElement(name, myBuilder);
240 }
241
242 /**
243 * Sets the transient state of this non-Element.
244 *
245 * @param in
246 * The input stream.
247 * @throws ClassNotFoundException
248 * On a failure loading a class in this classed reachable
249 * tree.
250 * @throws IOException
251 * On a failure reading from the stream.
252 */
253 private void readObject(final ObjectInputStream in)
254 throws ClassNotFoundException, IOException {
255 in.defaultReadObject();
256 myBuilder = null;
257 }
258 }
259 }