Coverage Report - com.allanbank.mongodb.client.message.Update
 
Classes in this File Line Coverage Branch Coverage Complexity
Update
100%
77/77
100%
36/36
2.214
 
 1  
 /*
 2  
  * #%L
 3  
  * Update.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.client.message;
 21  
 
 22  
 import java.io.IOException;
 23  
 
 24  
 import com.allanbank.mongodb.ReadPreference;
 25  
 import com.allanbank.mongodb.bson.Document;
 26  
 import com.allanbank.mongodb.bson.io.BsonInputStream;
 27  
 import com.allanbank.mongodb.bson.io.BsonOutputStream;
 28  
 import com.allanbank.mongodb.bson.io.BufferingBsonOutputStream;
 29  
 import com.allanbank.mongodb.bson.io.StringEncoder;
 30  
 import com.allanbank.mongodb.client.Message;
 31  
 import com.allanbank.mongodb.client.Operation;
 32  
 import com.allanbank.mongodb.error.DocumentToLargeException;
 33  
 
 34  
 /**
 35  
  * Message to apply an <a href=
 36  
  * "http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-OPUPDATE"
 37  
  * >update</a> to a document.
 38  
  * 
 39  
  * <pre>
 40  
  * <code>
 41  
  * struct OP_UPDATE {
 42  
  *     MsgHeader header;             // standard message header
 43  
  *     int32     ZERO;               // 0 - reserved for future use
 44  
  *     cstring   fullCollectionName; // "dbname.collectionname"
 45  
  *     int32     flags;              // bit vector. see below
 46  
  *     document  selector;           // the query to select the document
 47  
  *     document  update;             // specification of the update to perform
 48  
  * }
 49  
  * </code>
 50  
  * </pre>
 51  
  * 
 52  
  * 
 53  
  * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
 54  
  *         mutated in incompatible ways between any two releases of the driver.
 55  
  * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
 56  
  */
 57  
 public class Update extends AbstractMessage {
 58  
 
 59  
     /** Flag bit for a multi-update. */
 60  
     public static final int MULTIUPDATE_FLAG_BIT = 0x02;
 61  
 
 62  
     /** Flag bit for an upsert. */
 63  
     public static final int UPSERT_FLAG_BIT = 0x01;
 64  
 
 65  
     /**
 66  
      * If true then all documents matching the query should be updated,
 67  
      * otherwise only the first document should be updated.
 68  
      */
 69  
     private final boolean myMultiUpdate;
 70  
 
 71  
     /** The query to select the document to update. */
 72  
     private final Document myQuery;
 73  
 
 74  
     /** The updates to apply to the selected document(s). */
 75  
     private final Document myUpdate;
 76  
 
 77  
     /**
 78  
      * If true then a document should be inserted if none match the query
 79  
      * criteria.
 80  
      */
 81  
     private final boolean myUpsert;
 82  
 
 83  
     /**
 84  
      * Creates a new Update.
 85  
      * 
 86  
      * @param in
 87  
      *            The input stream to read the update message from.
 88  
      * @throws IOException
 89  
      *             On an error reading the update message.
 90  
      */
 91  1025
     public Update(final BsonInputStream in) throws IOException {
 92  1025
         in.readInt(); // 0 - reserved.
 93  1025
         init(in.readCString());
 94  1025
         final int flags = in.readInt();
 95  1025
         myQuery = in.readDocument();
 96  1025
         myUpdate = in.readDocument();
 97  
 
 98  1025
         myUpsert = (flags & UPSERT_FLAG_BIT) == UPSERT_FLAG_BIT;
 99  1025
         myMultiUpdate = (flags & MULTIUPDATE_FLAG_BIT) == MULTIUPDATE_FLAG_BIT;
 100  1025
     }
 101  
 
 102  
     /**
 103  
      * Creates a new Update.
 104  
      * 
 105  
      * @param databaseName
 106  
      *            The name of the database.
 107  
      * @param collectionName
 108  
      *            The name of the collection.
 109  
      * @param query
 110  
      *            The query to select the document to update.
 111  
      * @param update
 112  
      *            The updates to apply to the selected document(s).
 113  
      * @param multiUpdate
 114  
      *            If true then all documents matching the query should be
 115  
      *            updated, otherwise only the first document should be updated.
 116  
      * @param upsert
 117  
      *            If true then a document should be inserted if none match the
 118  
      *            query criteria.
 119  
      */
 120  
     public Update(final String databaseName, final String collectionName,
 121  
             final Document query, final Document update,
 122  
             final boolean multiUpdate, final boolean upsert) {
 123  3144
         super(databaseName, collectionName, ReadPreference.PRIMARY);
 124  3144
         myQuery = query;
 125  3144
         myUpdate = update;
 126  3144
         myMultiUpdate = multiUpdate;
 127  3144
         myUpsert = upsert;
 128  3144
     }
 129  
 
 130  
     /**
 131  
      * Determines if the passed object is of this same type as this object and
 132  
      * if so that its fields are equal.
 133  
      * 
 134  
      * @param object
 135  
      *            The object to compare to.
 136  
      * 
 137  
      * @see java.lang.Object#equals(java.lang.Object)
 138  
      */
 139  
     @Override
 140  
     public boolean equals(final Object object) {
 141  529950
         boolean result = false;
 142  529950
         if (this == object) {
 143  1028
             result = true;
 144  
         }
 145  528922
         else if ((object != null) && (getClass() == object.getClass())) {
 146  525849
             final Update other = (Update) object;
 147  
 
 148  525849
             result = super.equals(object)
 149  
                     && (myMultiUpdate == other.myMultiUpdate)
 150  
                     && (myUpsert == other.myUpsert)
 151  
                     && myUpdate.equals(other.myUpdate)
 152  
                     && myQuery.equals(other.myQuery);
 153  
         }
 154  529950
         return result;
 155  
     }
 156  
 
 157  
     /**
 158  
      * {@inheritDoc}
 159  
      * <p>
 160  
      * Overridden to return the name of the operation: "UPDATE".
 161  
      * </p>
 162  
      */
 163  
     @Override
 164  
     public String getOperationName() {
 165  1
         return Operation.UPDATE.name();
 166  
     }
 167  
 
 168  
     /**
 169  
      * Returns the query to select the document to update.
 170  
      * 
 171  
      * @return The query to select the document to update.
 172  
      */
 173  
     public Document getQuery() {
 174  7
         return myQuery;
 175  
     }
 176  
 
 177  
     /**
 178  
      * Returns the updates to apply to the selected document(s).
 179  
      * 
 180  
      * @return The updates to apply to the selected document(s).
 181  
      */
 182  
     public Document getUpdate() {
 183  7
         return myUpdate;
 184  
     }
 185  
 
 186  
     /**
 187  
      * Computes a reasonable hash code.
 188  
      * 
 189  
      * @return The hash code value.
 190  
      */
 191  
     @Override
 192  
     public int hashCode() {
 193  1049601
         int result = 1;
 194  1049601
         result = (31 * result) + super.hashCode();
 195  1049601
         result = (31 * result) + (myMultiUpdate ? 1 : 3);
 196  1049601
         result = (31 * result) + (myUpsert ? 1 : 3);
 197  1049601
         result = (31 * result) + myUpdate.hashCode();
 198  1049601
         result = (31 * result) + myQuery.hashCode();
 199  1049601
         return result;
 200  
     }
 201  
 
 202  
     /**
 203  
      * Returns true if all documents matching the query should be updated,
 204  
      * otherwise only the first document should be updated.
 205  
      * 
 206  
      * @return True if all documents matching the query should be updated,
 207  
      *         otherwise only the first document should be updated.
 208  
      */
 209  
     public boolean isMultiUpdate() {
 210  7
         return myMultiUpdate;
 211  
     }
 212  
 
 213  
     /**
 214  
      * Returns true if the document should be inserted if none match the query
 215  
      * criteria.
 216  
      * 
 217  
      * @return True if the document should be inserted if none match the query
 218  
      *         criteria.
 219  
      */
 220  
     public boolean isUpsert() {
 221  7
         return myUpsert;
 222  
     }
 223  
 
 224  
     /**
 225  
      * {@inheritDoc}
 226  
      * <p>
 227  
      * Overridden to return the size of the {@link Query}.
 228  
      * </p>
 229  
      */
 230  
     @Override
 231  
     public int size() {
 232  
 
 233  1024
         int size = HEADER_SIZE + 10; // See below.
 234  
         // size += 4; // 0 - reserved.
 235  1024
         size += StringEncoder.utf8Size(myDatabaseName);
 236  
         // size += 1; // StringEncoder.utf8Size(".");
 237  1024
         size += StringEncoder.utf8Size(myCollectionName);
 238  
         // size += 1; // \0 on the CString.
 239  
         // size += 4; // flags
 240  1024
         size += myQuery.size();
 241  1024
         size += myUpdate.size();
 242  
 
 243  1024
         return size;
 244  
     }
 245  
 
 246  
     /**
 247  
      * {@inheritDoc}
 248  
      * <p>
 249  
      * Overridden to ensure the inserted documents are not too large in
 250  
      * aggregate.
 251  
      * </p>
 252  
      */
 253  
     @Override
 254  
     public void validateSize(final int maxDocumentSize)
 255  
             throws DocumentToLargeException {
 256  12
         final long querySize = (myQuery != null) ? myQuery.size() : 0;
 257  12
         if (maxDocumentSize < querySize) {
 258  1
             throw new DocumentToLargeException((int) querySize,
 259  
                     maxDocumentSize, myQuery);
 260  
         }
 261  11
         final long updateSize = (myUpdate != null) ? myUpdate.size() : 0;
 262  11
         if (maxDocumentSize < updateSize) {
 263  1
             throw new DocumentToLargeException((int) updateSize,
 264  
                     maxDocumentSize, myUpdate);
 265  
         }
 266  10
     }
 267  
 
 268  
     /**
 269  
      * {@inheritDoc}
 270  
      * <p>
 271  
      * Overridden to write the update message.
 272  
      * </p>
 273  
      * 
 274  
      * @see Message#write(int, BsonOutputStream)
 275  
      */
 276  
     @Override
 277  
     public void write(final int messageId, final BsonOutputStream out)
 278  
             throws IOException {
 279  
 
 280  1025
         final int flags = computeFlags();
 281  
 
 282  1025
         int size = HEADER_SIZE;
 283  1025
         size += 4; // 0 - reserved.
 284  1025
         size += out.sizeOfCString(myDatabaseName, ".", myCollectionName);
 285  1025
         size += 4; // flags
 286  1025
         size += myQuery.size();
 287  1025
         size += myUpdate.size();
 288  
 
 289  1025
         writeHeader(out, messageId, 0, Operation.UPDATE, size);
 290  1025
         out.writeInt(0);
 291  1025
         out.writeCString(myDatabaseName, ".", myCollectionName);
 292  1025
         out.writeInt(flags);
 293  1025
         out.writeDocument(myQuery);
 294  1025
         out.writeDocument(myUpdate);
 295  1025
     }
 296  
 
 297  
     /**
 298  
      * {@inheritDoc}
 299  
      * <p>
 300  
      * Overridden to write the update message.
 301  
      * </p>
 302  
      * 
 303  
      * @see Message#write(int, BsonOutputStream)
 304  
      */
 305  
     @Override
 306  
     public void write(final int messageId, final BufferingBsonOutputStream out)
 307  
             throws IOException {
 308  
 
 309  6
         final int flags = computeFlags();
 310  
 
 311  6
         final long start = writeHeader(out, messageId, 0, Operation.UPDATE);
 312  6
         out.writeInt(0);
 313  6
         out.writeCString(myDatabaseName, ".", myCollectionName);
 314  6
         out.writeInt(flags);
 315  6
         out.writeDocument(myQuery);
 316  6
         out.writeDocument(myUpdate);
 317  6
         finishHeader(out, start);
 318  
 
 319  6
         out.flushBuffer();
 320  6
     }
 321  
 
 322  
     /**
 323  
      * Computes the message flags bit field.
 324  
      * 
 325  
      * @return The message flags bit field.
 326  
      */
 327  
     private int computeFlags() {
 328  1031
         int flags = 0;
 329  1031
         if (myMultiUpdate) {
 330  514
             flags += MULTIUPDATE_FLAG_BIT;
 331  
         }
 332  1031
         if (myUpsert) {
 333  514
             flags += UPSERT_FLAG_BIT;
 334  
         }
 335  1031
         return flags;
 336  
     }
 337  
 }