View Javadoc
1   /*
2    * #%L
3    * SizeOfVisitor.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.io;
21  
22  import java.nio.charset.Charset;
23  import java.util.List;
24  
25  import com.allanbank.mongodb.bson.Document;
26  import com.allanbank.mongodb.bson.Element;
27  import com.allanbank.mongodb.bson.ElementType;
28  import com.allanbank.mongodb.bson.Visitor;
29  import com.allanbank.mongodb.bson.element.BinaryElement;
30  import com.allanbank.mongodb.bson.element.ObjectId;
31  
32  /**
33   * A visitor to determine the size of the documents it visits.
34   * 
35   * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
36   *         mutated in incompatible ways between any two releases of the driver.
37   * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
38   */
39  public class SizeOfVisitor implements Visitor {
40      /** UTF-8 Character set for encoding strings. */
41      public final static Charset UTF8 = StringDecoder.UTF8;
42  
43      /** The computed size. */
44      private int mySize;
45  
46      /** The encoder for strings. */
47      private final StringEncoder myStringEncoder;
48  
49      /**
50       * Creates a new SizeOfVisitor.
51       */
52      public SizeOfVisitor() {
53          this(null);
54      }
55  
56      /**
57       * Creates a new SizeOfVisitor.
58       * 
59       * @param encoder
60       *            The encoder for strings.
61       */
62      public SizeOfVisitor(final StringEncoder encoder) {
63          super();
64          mySize = 0;
65          myStringEncoder = encoder;
66      }
67  
68      /**
69       * Returns the visitor's output buffer.
70       * 
71       * @param string
72       *            The 'C' string to determine the size of.
73       * @return The visitor's output buffer.
74       */
75      public int computeCStringSize(final String string) {
76          return utf8Size(string) + 1;
77      }
78  
79      /**
80       * Returns the visitor's output buffer.
81       * 
82       * @param string
83       *            The 'UTF8' string to determine the size of.
84       * @return The visitor's output buffer.
85       */
86      public int computeStringSize(final String string) {
87          return 4 + utf8Size(string) + 1;
88      }
89  
90      /**
91       * Return the current Size of the written document.
92       * 
93       * @return The current size of the encoded document.
94       */
95      public int getSize() {
96          return mySize;
97      }
98  
99      /**
100      * Resets the size to zero and clears the cached documents. Use
101      * {@link #rewind()} to just set the size to zero and not clear the cached
102      * documents.
103      */
104     public void reset() {
105         mySize = 0;
106     }
107 
108     /**
109      * Resets the size to zero but does not clear the cached documents. Use
110      * {@link #reset()} to set the size to zero and clear the cached documents.
111      */
112     public void rewind() {
113         mySize = 0;
114     }
115 
116     /**
117      * Computes the size of the encoded UTF8 String based on the table below.
118      * 
119      * <pre>
120      * #    Code Points      Bytes
121      * 1    U+0000..U+007F   1
122      * 
123      * 2    U+0080..U+07FF   2
124      * 
125      * 3    U+0800..U+0FFF   3
126      *      U+1000..U+FFFF
127      * 
128      * 4   U+10000..U+3FFFF  4
129      *     U+40000..U+FFFFF  4
130      *    U+100000..U10FFFF  4
131      * </pre>
132      * 
133      * @param string
134      *            The string to determine the length of.
135      * @return The length of the string encoded as UTF8.
136      */
137     public int utf8Size(final String string) {
138         if (myStringEncoder != null) {
139             return myStringEncoder.encodeSize(string);
140         }
141         return StringEncoder.utf8Size(string);
142     }
143 
144     /**
145      * {@inheritDoc}
146      */
147     @Override
148     public void visit(final List<Element> elements) {
149         mySize += 4;
150         for (final Element element : elements) {
151             // Optimization to avoid the array copy.
152             if (element.getType() == ElementType.BINARY) {
153                 final BinaryElement be = (BinaryElement) element;
154                 doVisitBinary(be.getName(), be.getSubType(), be.length());
155             }
156             else {
157                 element.accept(this);
158             }
159         }
160         mySize += 1;
161     }
162 
163     /**
164      * {@inheritDoc}
165      */
166     @Override
167     public void visitArray(final String name, final List<Element> elements) {
168 
169         mySize += 1;
170         mySize += computeCStringSize(name);
171         visit(elements);
172     }
173 
174     /**
175      * {@inheritDoc}
176      */
177     @Override
178     public void visitBinary(final String name, final byte subType,
179             final byte[] data) {
180 
181         final int dataLength = data.length;
182 
183         doVisitBinary(name, subType, dataLength);
184     }
185 
186     /**
187      * {@inheritDoc}
188      */
189     @Override
190     public void visitBoolean(final String name, final boolean value) {
191 
192         mySize += 1;
193         mySize += computeCStringSize(name);
194         mySize += 1;
195     }
196 
197     /**
198      * {@inheritDoc}
199      */
200     @Override
201     public void visitDBPointer(final String name, final String databaseName,
202             final String collectionName, final ObjectId id) {
203         mySize += 1;
204         mySize += computeCStringSize(name);
205         mySize += computeStringSize(databaseName + "." + collectionName);
206         mySize += (4 + 8);
207     }
208 
209     /**
210      * {@inheritDoc}
211      */
212     @Override
213     public void visitDocument(final String name, final List<Element> elements) {
214         mySize += 1;
215         mySize += computeCStringSize(name);
216         visit(elements);
217     }
218 
219     /**
220      * {@inheritDoc}
221      */
222     @Override
223     public void visitDouble(final String name, final double value) {
224         mySize += 1;
225         mySize += computeCStringSize(name);
226         mySize += 8;
227     }
228 
229     /**
230      * {@inheritDoc}
231      */
232     @Override
233     public void visitInteger(final String name, final int value) {
234         mySize += 1;
235         mySize += computeCStringSize(name);
236         mySize += 4;
237     }
238 
239     /**
240      * {@inheritDoc}
241      */
242     @Override
243     public void visitJavaScript(final String name, final String code) {
244         mySize += 1;
245         mySize += computeCStringSize(name);
246         mySize += computeStringSize(code);
247     }
248 
249     /**
250      * {@inheritDoc}
251      */
252     @Override
253     public void visitJavaScript(final String name, final String code,
254             final Document scope) {
255         mySize += 1;
256         mySize += computeCStringSize(name);
257 
258         mySize += 4;
259         mySize += computeStringSize(code);
260 
261         scope.accept(this);
262     }
263 
264     /**
265      * {@inheritDoc}
266      */
267     @Override
268     public void visitLong(final String name, final long value) {
269         mySize += 1;
270         mySize += computeCStringSize(name);
271         mySize += 8;
272     }
273 
274     /**
275      * {@inheritDoc}
276      */
277     @Override
278     public void visitMaxKey(final String name) {
279         mySize += 1;
280         mySize += computeCStringSize(name);
281     }
282 
283     /**
284      * {@inheritDoc}
285      */
286     @Override
287     public void visitMinKey(final String name) {
288         mySize += 1;
289         mySize += computeCStringSize(name);
290     }
291 
292     /**
293      * {@inheritDoc}
294      */
295     @Override
296     public void visitMongoTimestamp(final String name, final long value) {
297         mySize += 1;
298         mySize += computeCStringSize(name);
299         mySize += 8;
300     }
301 
302     /**
303      * {@inheritDoc}
304      */
305     @Override
306     public void visitNull(final String name) {
307         mySize += 1;
308         mySize += computeCStringSize(name);
309     }
310 
311     /**
312      * {@inheritDoc}
313      */
314     @Override
315     public void visitObjectId(final String name, final ObjectId id) {
316         mySize += 1;
317         mySize += computeCStringSize(name);
318         mySize += (4 + 8);
319     }
320 
321     /**
322      * {@inheritDoc}
323      */
324     @Override
325     public void visitRegularExpression(final String name, final String pattern,
326             final String options) {
327         mySize += 1;
328         mySize += computeCStringSize(name);
329         mySize += computeCStringSize(pattern);
330         mySize += computeCStringSize(options);
331     }
332 
333     /**
334      * {@inheritDoc}
335      */
336     @Override
337     public void visitString(final String name, final String value) {
338         mySize += 1;
339         mySize += computeCStringSize(name);
340         mySize += computeStringSize(value);
341     }
342 
343     /**
344      * {@inheritDoc}
345      */
346     @Override
347     public void visitSymbol(final String name, final String symbol) {
348         mySize += 1;
349         mySize += computeCStringSize(name);
350         mySize += computeStringSize(symbol);
351     }
352 
353     /**
354      * {@inheritDoc}
355      */
356     @Override
357     public void visitTimestamp(final String name, final long timestamp) {
358         mySize += 1;
359         mySize += computeCStringSize(name);
360         mySize += 8;
361     }
362 
363     /**
364      * Computes the size of the binary based on the name, type and length of the
365      * data.
366      * 
367      * @param name
368      *            The name of the element.
369      * @param subType
370      *            The sub-type of the binary element.
371      * @param dataLength
372      *            The length of data contained in the element.
373      */
374     private void doVisitBinary(final String name, final byte subType,
375             final int dataLength) {
376         mySize += 1;
377         mySize += computeCStringSize(name);
378 
379         switch (subType) {
380         case 2: {
381             mySize += (4 + 1 + 4 + dataLength);
382             break;
383 
384         }
385         case 0:
386         default:
387             mySize += (4 + 1 + dataLength);
388             break;
389         }
390     }
391 }