Coverage Report - com.allanbank.mongodb.bson.io.WriteVisitor
 
Classes in this File Line Coverage Branch Coverage Complexity
WriteVisitor
89%
106/118
100%
8/8
1.167
 
 1  
 /*
 2  
  * #%L
 3  
  * WriteVisitor.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.io.IOException;
 23  
 import java.io.OutputStream;
 24  
 import java.util.List;
 25  
 
 26  
 import com.allanbank.mongodb.bson.Document;
 27  
 import com.allanbank.mongodb.bson.Element;
 28  
 import com.allanbank.mongodb.bson.ElementType;
 29  
 import com.allanbank.mongodb.bson.element.ObjectId;
 30  
 import com.allanbank.mongodb.bson.element.SizeAwareVisitor;
 31  
 
 32  
 /**
 33  
  * A visitor to myOutput.write the BSON document to a {@link OutputStream}. The
 34  
  * BSON specification uses prefixed length integers in several locations. This
 35  
  * visitor uses a {@link StringEncoder} and the {@link Element#size()} to
 36  
  * compute the size item about to be written removing the requirements to buffer
 37  
  * the data being written.
 38  
  * 
 39  
  * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
 40  
  *         mutated in incompatible ways between any two releases of the driver.
 41  
  * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
 42  
  */
 43  
 /* package */class WriteVisitor implements SizeAwareVisitor {
 44  
 
 45  
     /** Stream to myOutput.write to. */
 46  
     protected final BsonOutputStream myOutput;
 47  
 
 48  
     /**
 49  
      * Creates a new {@link WriteVisitor}.
 50  
      * 
 51  
      * @param output
 52  
      *            The stream to myOutput.write to.
 53  
      */
 54  3328
     public WriteVisitor(final BsonOutputStream output) {
 55  3328
         myOutput = output;
 56  3328
     }
 57  
 
 58  
     /**
 59  
      * Creates a new {@link WriteVisitor}.
 60  
      * 
 61  
      * @param output
 62  
      *            The stream to myOutput.write to.
 63  
      */
 64  
     public WriteVisitor(final OutputStream output) {
 65  0
         this(new BsonOutputStream(output));
 66  0
     }
 67  
 
 68  
     /**
 69  
      * Returns the I/O exception encountered by the visitor.
 70  
      * 
 71  
      * @return The I/O exception encountered by the visitor.
 72  
      */
 73  
     public IOException getError() {
 74  0
         return myOutput.getError();
 75  
     }
 76  
 
 77  
     /**
 78  
      * Returns true if the visitor had an I/O error.
 79  
      * 
 80  
      * @return True if the visitor had an I/O error, false otherwise.
 81  
      */
 82  
     public boolean hasError() {
 83  4766
         return myOutput.hasError();
 84  
     }
 85  
 
 86  
     /**
 87  
      * Clears the internal buffer and prepares to myOutput.write another
 88  
      * document.
 89  
      */
 90  
     public void reset() {
 91  4766
         myOutput.reset();
 92  4766
     }
 93  
 
 94  
     /**
 95  
      * Determines the size of the document written in BSON format. The
 96  
      * {@link Document}'s size is cached for subsequent write operations.
 97  
      * 
 98  
      * @param doc
 99  
      *            The document to determine the size of.
 100  
      * @return The number of bytes require to Write the document.
 101  
      * @deprecated Replaced with {@link Document#size()}. This method will be
 102  
      *             removed after the 2.2.0 release.
 103  
      */
 104  
     @Deprecated
 105  
     public int sizeOf(final Document doc) {
 106  0
         return (int) doc.size();
 107  
     }
 108  
 
 109  
     /**
 110  
      * Computes the size of the encoded UTF8 String.
 111  
      * 
 112  
      * @param string
 113  
      *            The string to determine the length of.
 114  
      * @return The length of the string encoded as UTF8.
 115  
      * @see StringEncoder#utf8Size(String)
 116  
      */
 117  
     public int utf8Size(final String string) {
 118  5489
         return StringEncoder.utf8Size(string);
 119  
     }
 120  
 
 121  
     /**
 122  
      * {@inheritDoc}
 123  
      */
 124  
     @Override
 125  
     public void visit(final List<Element> elements) {
 126  
 
 127  4770
         int size = 4 + 1; // Length (int,4) and null.
 128  4770
         for (final Element element : elements) {
 129  4009
             size += element.size();
 130  4009
         }
 131  
 
 132  4770
         writeElements(elements, size);
 133  4770
     }
 134  
 
 135  
     /**
 136  
      * {@inheritDoc}
 137  
      */
 138  
     @Override
 139  
     public void visitArray(final String name, final List<Element> elements) {
 140  0
         myOutput.writeByte(ElementType.ARRAY.getToken());
 141  0
         myOutput.writeCString(name);
 142  0
         visit(elements);
 143  0
     }
 144  
 
 145  
     /**
 146  
      * {@inheritDoc}
 147  
      */
 148  
     @Override
 149  
     public void visitArray(final String name, final List<Element> elements,
 150  
             final long totalSize) {
 151  138
         myOutput.writeByte(ElementType.ARRAY.getToken());
 152  138
         myOutput.writeCString(name);
 153  138
         writeElements(elements,
 154  
                 ((int) totalSize) - (myOutput.sizeOfCString(name) + 1));
 155  138
     }
 156  
 
 157  
     /**
 158  
      * {@inheritDoc}
 159  
      */
 160  
     @Override
 161  
     public void visitBinary(final String name, final byte subType,
 162  
             final byte[] data) {
 163  13
         myOutput.writeByte(ElementType.BINARY.getToken());
 164  13
         myOutput.writeCString(name);
 165  13
         switch (subType) {
 166  
         case 2: {
 167  4
             myOutput.writeInt(data.length + 4);
 168  4
             myOutput.writeByte(subType);
 169  4
             myOutput.writeInt(data.length);
 170  4
             myOutput.writeBytes(data);
 171  4
             break;
 172  
 
 173  
         }
 174  
         case 0:
 175  
         default:
 176  9
             myOutput.writeInt(data.length);
 177  9
             myOutput.writeByte(subType);
 178  9
             myOutput.writeBytes(data);
 179  
             break;
 180  
         }
 181  13
     }
 182  
 
 183  
     /**
 184  
      * {@inheritDoc}
 185  
      */
 186  
     @Override
 187  
     public void visitBoolean(final String name, final boolean value) {
 188  
 
 189  156
         myOutput.writeByte(ElementType.BOOLEAN.getToken());
 190  156
         myOutput.writeCString(name);
 191  156
         myOutput.writeByte(value ? (byte) 0x01 : 0x00);
 192  156
     }
 193  
 
 194  
     /**
 195  
      * {@inheritDoc}
 196  
      */
 197  
     @SuppressWarnings("deprecation")
 198  
     @Override
 199  
     public void visitDBPointer(final String name, final String databaseName,
 200  
             final String collectionName, final ObjectId id) {
 201  4
         myOutput.writeByte(ElementType.DB_POINTER.getToken());
 202  4
         myOutput.writeCString(name);
 203  4
         myOutput.writeString(databaseName + "." + collectionName);
 204  
         // Just to be complicated the Object ID is big endian.
 205  4
         myOutput.writeInt(EndianUtils.swap(id.getTimestamp()));
 206  4
         myOutput.writeLong(EndianUtils.swap(id.getMachineId()));
 207  4
     }
 208  
 
 209  
     /**
 210  
      * {@inheritDoc}
 211  
      */
 212  
     @Override
 213  
     public void visitDocument(final String name, final List<Element> elements) {
 214  0
         myOutput.writeByte(ElementType.DOCUMENT.getToken());
 215  0
         myOutput.writeCString(name);
 216  0
         visit(elements);
 217  0
     }
 218  
 
 219  
     /**
 220  
      * {@inheritDoc}
 221  
      */
 222  
     @Override
 223  
     public void visitDocument(final String name, final List<Element> elements,
 224  
             final long totalSize) {
 225  143
         myOutput.writeByte(ElementType.DOCUMENT.getToken());
 226  143
         myOutput.writeCString(name);
 227  143
         writeElements(elements,
 228  
                 ((int) totalSize) - (myOutput.sizeOfCString(name) + 1));
 229  143
     }
 230  
 
 231  
     /**
 232  
      * {@inheritDoc}
 233  
      */
 234  
     @Override
 235  
     public void visitDouble(final String name, final double value) {
 236  5
         myOutput.writeByte(ElementType.DOUBLE.getToken());
 237  5
         myOutput.writeCString(name);
 238  5
         myOutput.writeLong(Double.doubleToLongBits(value));
 239  5
     }
 240  
 
 241  
     /**
 242  
      * {@inheritDoc}
 243  
      */
 244  
     @Override
 245  
     public void visitInteger(final String name, final int value) {
 246  3394
         myOutput.writeByte(ElementType.INTEGER.getToken());
 247  3394
         myOutput.writeCString(name);
 248  3394
         myOutput.writeInt(value);
 249  3394
     }
 250  
 
 251  
     /**
 252  
      * {@inheritDoc}
 253  
      */
 254  
     @Override
 255  
     public void visitJavaScript(final String name, final String code) {
 256  4
         myOutput.writeByte(ElementType.JAVA_SCRIPT.getToken());
 257  4
         myOutput.writeCString(name);
 258  4
         myOutput.writeString(code);
 259  4
     }
 260  
 
 261  
     /**
 262  
      * {@inheritDoc}
 263  
      */
 264  
     @Override
 265  
     public void visitJavaScript(final String name, final String code,
 266  
             final Document scope) {
 267  4
         myOutput.writeByte(ElementType.JAVA_SCRIPT_WITH_SCOPE.getToken());
 268  4
         myOutput.writeCString(name);
 269  
 
 270  4
         myOutput.writeInt(4 + StringEncoder.computeStringSize(code)
 271  
                 + (int) scope.size());
 272  4
         myOutput.writeString(code);
 273  
 
 274  4
         scope.accept(this);
 275  4
     }
 276  
 
 277  
     /**
 278  
      * {@inheritDoc}
 279  
      */
 280  
     @Override
 281  
     public void visitLong(final String name, final long value) {
 282  6
         myOutput.writeByte(ElementType.LONG.getToken());
 283  6
         myOutput.writeCString(name);
 284  6
         myOutput.writeLong(value);
 285  6
     }
 286  
 
 287  
     /**
 288  
      * {@inheritDoc}
 289  
      */
 290  
     @Override
 291  
     public void visitMaxKey(final String name) {
 292  4
         myOutput.writeByte(ElementType.MAX_KEY.getToken());
 293  4
         myOutput.writeCString(name);
 294  4
     }
 295  
 
 296  
     /**
 297  
      * {@inheritDoc}
 298  
      */
 299  
     @Override
 300  
     public void visitMinKey(final String name) {
 301  4
         myOutput.writeByte(ElementType.MIN_KEY.getToken());
 302  4
         myOutput.writeCString(name);
 303  4
     }
 304  
 
 305  
     /**
 306  
      * {@inheritDoc}
 307  
      */
 308  
     @Override
 309  
     public void visitMongoTimestamp(final String name, final long value) {
 310  4
         myOutput.writeByte(ElementType.MONGO_TIMESTAMP.getToken());
 311  4
         myOutput.writeCString(name);
 312  4
         myOutput.writeLong(value);
 313  4
     }
 314  
 
 315  
     /**
 316  
      * {@inheritDoc}
 317  
      */
 318  
     @Override
 319  
     public void visitNull(final String name) {
 320  4
         myOutput.writeByte(ElementType.NULL.getToken());
 321  4
         myOutput.writeCString(name);
 322  4
     }
 323  
 
 324  
     /**
 325  
      * {@inheritDoc}
 326  
      */
 327  
     @Override
 328  
     public void visitObjectId(final String name, final ObjectId id) {
 329  4
         myOutput.writeByte(ElementType.OBJECT_ID.getToken());
 330  4
         myOutput.writeCString(name);
 331  
         // Just to be complicated the Object ID is big endian.
 332  4
         myOutput.writeInt(EndianUtils.swap(id.getTimestamp()));
 333  4
         myOutput.writeLong(EndianUtils.swap(id.getMachineId()));
 334  4
     }
 335  
 
 336  
     /**
 337  
      * {@inheritDoc}
 338  
      */
 339  
     @Override
 340  
     public void visitRegularExpression(final String name, final String pattern,
 341  
             final String options) {
 342  10
         myOutput.writeByte(ElementType.REGEX.getToken());
 343  10
         myOutput.writeCString(name);
 344  10
         myOutput.writeCString(pattern);
 345  10
         myOutput.writeCString(options);
 346  10
     }
 347  
 
 348  
     /**
 349  
      * {@inheritDoc}
 350  
      */
 351  
     @Override
 352  
     public void visitString(final String name, final String value) {
 353  502
         myOutput.writeByte(ElementType.STRING.getToken());
 354  502
         myOutput.writeCString(name);
 355  502
         myOutput.writeString(value);
 356  502
     }
 357  
 
 358  
     /**
 359  
      * {@inheritDoc}
 360  
      */
 361  
     @Override
 362  
     public void visitSymbol(final String name, final String symbol) {
 363  4
         myOutput.writeByte(ElementType.SYMBOL.getToken());
 364  4
         myOutput.writeCString(name);
 365  4
         myOutput.writeString(symbol);
 366  4
     }
 367  
 
 368  
     /**
 369  
      * {@inheritDoc}
 370  
      */
 371  
     @Override
 372  
     public void visitTimestamp(final String name, final long timestamp) {
 373  8
         myOutput.writeByte(ElementType.UTC_TIMESTAMP.getToken());
 374  8
         myOutput.writeCString(name);
 375  8
         myOutput.writeLong(timestamp);
 376  8
     }
 377  
 
 378  
     /**
 379  
      * Writes a list of elements.
 380  
      * 
 381  
      * @param elements
 382  
      *            The sub elements of the document.
 383  
      * @param size
 384  
      *            The size of the elements.
 385  
      */
 386  
     protected void writeElements(final List<Element> elements, final int size) {
 387  5051
         myOutput.writeInt(size);
 388  5051
         for (final Element element : elements) {
 389  4411
             element.accept(this);
 390  4411
         }
 391  5051
         myOutput.writeByte((byte) 0);
 392  5051
     }
 393  
 
 394  
 }