1 /* 2 * #%L 3 * BsonOutputStream.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.nio.charset.Charset; 25 26 import com.allanbank.mongodb.bson.Document; 27 28 /** 29 * A wrapper for an {@link OutputStream} to handle writing BSON primitives. 30 * 31 * @api.yes This class is part of the driver's API. Public and protected members 32 * will be deprecated for at least 1 non-bugfix release (version 33 * numbers are <major>.<minor>.<bugfix>) before being 34 * removed or modified. 35 * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved 36 */ 37 public class BsonOutputStream { 38 39 /** UTF-8 Character set for encoding strings. */ 40 public final static Charset UTF8 = StringDecoder.UTF8; 41 42 /** Any thrown exceptions. */ 43 protected IOException myError; 44 45 /** Output buffer for spooling the written document. */ 46 protected final OutputStream myOutput; 47 48 /** The encoder for strings. */ 49 protected final StringEncoder myStringEncoder; 50 51 /** The visitor for writing BSON documents. */ 52 protected final WriteVisitor myWriteVisitor; 53 54 /** 55 * Creates a new {@link BsonOutputStream}. 56 * 57 * @param output 58 * The underlying Stream to write to. 59 */ 60 public BsonOutputStream(final OutputStream output) { 61 this(output, new StringEncoderCache()); 62 } 63 64 /** 65 * Creates a new {@link BsonOutputStream}. 66 * 67 * @param output 68 * The underlying Stream to write to. 69 * @param cache 70 * The cache for encoding string. 71 */ 72 public BsonOutputStream(final OutputStream output, 73 final StringEncoderCache cache) { 74 myOutput = output; 75 myStringEncoder = new StringEncoder(cache); 76 myWriteVisitor = new WriteVisitor(this); 77 } 78 79 /** 80 * Returns the I/O exception encountered by the visitor. 81 * 82 * @return The I/O exception encountered by the visitor. 83 */ 84 public IOException getError() { 85 return myError; 86 } 87 88 /** 89 * Returns the maximum number of strings that may have their encoded form 90 * cached. 91 * 92 * @return The maximum number of strings that may have their encoded form 93 * cached. 94 * @deprecated The cache {@link StringEncoderCache} should be controlled 95 * directory. This method will be removed after the 2.1.0 96 * release. 97 */ 98 @Deprecated 99 public int getMaxCachedStringEntries() { 100 return myStringEncoder.getCache().getMaxCacheEntries(); 101 } 102 103 /** 104 * Returns the maximum length for a string that the stream is allowed to 105 * cache. 106 * 107 * @return The maximum length for a string that the stream is allowed to 108 * cache. 109 * @deprecated The cache {@link StringDecoderCache} should be controlled 110 * directory. This method will be removed after the 2.1.0 111 * release. 112 */ 113 @Deprecated 114 public int getMaxCachedStringLength() { 115 return myStringEncoder.getCache().getMaxCacheLength(); 116 } 117 118 /** 119 * Returns the encoder value. 120 * 121 * @return The encoder value. 122 */ 123 public StringEncoder getStringEncoder() { 124 return myStringEncoder; 125 } 126 127 /** 128 * Returns true if the visitor had an I/O error. 129 * 130 * @return True if the visitor had an I/O error, false otherwise. 131 */ 132 public boolean hasError() { 133 return (myError != null); 134 } 135 136 /** 137 * Clears any errors. 138 */ 139 public void reset() { 140 myError = null; 141 } 142 143 /** 144 * Sets the value of maximum number of strings that may have their encoded 145 * form cached. 146 * 147 * @param maxCacheEntries 148 * The new value for the maximum number of strings that may have 149 * their encoded form cached. 150 * @deprecated The cache {@link StringEncoderCache} should be controlled 151 * directory. This method will be removed after the 2.1.0 152 * release. 153 */ 154 @Deprecated 155 public void setMaxCachedStringEntries(final int maxCacheEntries) { 156 myStringEncoder.getCache().setMaxCacheEntries(maxCacheEntries); 157 } 158 159 /** 160 * Sets the value of length for a string that the stream is allowed to cache 161 * to the new value. This can be used to stop a single long string from 162 * pushing useful values out of the cache. 163 * 164 * @param maxlength 165 * The new value for the length for a string that the encoder is 166 * allowed to cache. 167 * @deprecated The cache {@link StringEncoderCache} should be controlled 168 * directory. This method will be removed after the 2.1.0 169 * release. 170 */ 171 @Deprecated 172 public void setMaxCachedStringLength(final int maxlength) { 173 myStringEncoder.getCache().setMaxCacheLength(maxlength); 174 175 } 176 177 /** 178 * Returns the size of the writing the {@link Document} as a BSON document. 179 * 180 * @param document 181 * The document to determine the size of. 182 * @return The size of the writing {@link Document} as a BSON document. 183 * @deprecated Replaced with {@link Document#size()}. This method will be 184 * removed after the 2.2.0 release. 185 */ 186 @Deprecated 187 public int sizeOf(final Document document) { 188 return (int) document.size(); 189 } 190 191 /** 192 * Returns the size of the writing the <tt>strings</tt> as a c-string. 193 * 194 * @param strings 195 * The 'C' strings to determine the size of. 196 * @return The size of the writing the <tt>strings</tt> as a c-string. 197 */ 198 public int sizeOfCString(final String... strings) { 199 int size = 0; 200 for (final String string : strings) { 201 size += myWriteVisitor.utf8Size(string); 202 } 203 return (size + 1); 204 } 205 206 /** 207 * Returns the size of the writing the <tt>string</tt> as a string. 208 * 209 * @param string 210 * The 'UTF8' string to determine the size of. 211 * @return The size of the writing the <tt>string</tt> as a string. 212 */ 213 public int sizeOfString(final String string) { 214 return 4 + myWriteVisitor.utf8Size(string) + 1; 215 } 216 217 /** 218 * Writes a single byte to the stream. 219 * 220 * @param b 221 * The byte to write. 222 */ 223 public void writeByte(final byte b) { 224 try { 225 myOutput.write(b); 226 } 227 catch (final IOException ioe) { 228 myError = ioe; 229 } 230 } 231 232 /** 233 * Writes a sequence of bytes to the under lying stream. 234 * 235 * @param data 236 * The bytes to write. 237 */ 238 public void writeBytes(final byte[] data) { 239 try { 240 myOutput.write(data); 241 } 242 catch (final IOException ioe) { 243 myError = ioe; 244 } 245 } 246 247 /** 248 * Writes a "Cstring" to the stream. 249 * 250 * @param strings 251 * The CString to write. The strings are concatenated into a 252 * single CString value. 253 */ 254 public void writeCString(final String... strings) { 255 for (final String string : strings) { 256 writeUtf8(string); 257 } 258 writeByte((byte) 0); 259 } 260 261 /** 262 * Writes a BSON {@link Document} to the stream. 263 * 264 * @param document 265 * The {@link Document} to write. 266 * @throws IOException 267 * On a failure writing the document. 268 */ 269 public void writeDocument(final Document document) throws IOException { 270 try { 271 document.accept(myWriteVisitor); 272 if (myWriteVisitor.hasError()) { 273 throw myWriteVisitor.getError(); 274 } 275 } 276 finally { 277 myWriteVisitor.reset(); 278 } 279 } 280 281 /** 282 * Write the integer value in little-endian byte order. 283 * 284 * @param value 285 * The integer to write. 286 */ 287 public void writeInt(final int value) { 288 try { 289 myOutput.write(value); 290 myOutput.write(value >> 8); 291 myOutput.write(value >> 16); 292 myOutput.write(value >> 24); 293 } 294 catch (final IOException ioe) { 295 myError = ioe; 296 } 297 } 298 299 /** 300 * Write the long value in little-endian byte order. 301 * 302 * @param value 303 * The long to write. 304 */ 305 public void writeLong(final long value) { 306 try { 307 myOutput.write((int) value); 308 myOutput.write((int) (value >> 8)); 309 myOutput.write((int) (value >> 16)); 310 myOutput.write((int) (value >> 24)); 311 myOutput.write((int) (value >> 32)); 312 myOutput.write((int) (value >> 40)); 313 myOutput.write((int) (value >> 48)); 314 myOutput.write((int) (value >> 56)); 315 } 316 catch (final IOException ioe) { 317 myError = ioe; 318 } 319 } 320 321 /** 322 * Writes a "string" to the stream. 323 * 324 * @param string 325 * The String to write. 326 */ 327 public void writeString(final String string) { 328 writeInt(myStringEncoder.encodeSize(string) + 1); 329 writeUtf8(string); 330 writeByte((byte) 0); 331 } 332 333 /** 334 * Writes a sequence of bytes to the under lying stream. 335 * 336 * @param data 337 * The bytes to write. 338 * @param offset 339 * The offset into the buffer to start writing data from. 340 * @param length 341 * The number of bytes to write. 342 */ 343 protected void writeBytes(final byte[] data, final int offset, 344 final int length) { 345 try { 346 myOutput.write(data, offset, length); 347 } 348 catch (final IOException ioe) { 349 myError = ioe; 350 } 351 } 352 353 /** 354 * Writes the string as a UTF-8 string. This method handles the 355 * "normal/easy" cases and delegates to the full character set if things get 356 * complicated. 357 * 358 * @param string 359 * The string to encode. 360 */ 361 protected void writeUtf8(final String string) { 362 try { 363 myStringEncoder.encode(string, myOutput); 364 } 365 catch (final IOException ioe) { 366 myError = ioe; 367 } 368 } 369 370 }