| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| CommandCursorTranslator |
|
| 2.6;2.6 |
| 1 | /* | |
| 2 | * #%L | |
| 3 | * CommandCursorTranslator.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 | ||
| 21 | package com.allanbank.mongodb.client.callback; | |
| 22 | ||
| 23 | import java.util.ArrayList; | |
| 24 | import java.util.Collections; | |
| 25 | import java.util.List; | |
| 26 | ||
| 27 | import com.allanbank.mongodb.bson.Document; | |
| 28 | import com.allanbank.mongodb.bson.NumericElement; | |
| 29 | import com.allanbank.mongodb.bson.builder.BuilderFactory; | |
| 30 | import com.allanbank.mongodb.bson.element.ArrayElement; | |
| 31 | import com.allanbank.mongodb.bson.element.DocumentElement; | |
| 32 | import com.allanbank.mongodb.client.message.Reply; | |
| 33 | ||
| 34 | /** | |
| 35 | * CommandCursorTranslator provides static utility methods to translate cursor | |
| 36 | * documents. | |
| 37 | * | |
| 38 | * @copyright 2013, Allanbank Consulting, Inc., All Rights Reserved | |
| 39 | */ | |
| 40 | public class CommandCursorTranslator { | |
| 41 | ||
| 42 | /** | |
| 43 | * Translates the reply from a single document command reply into a standard | |
| 44 | * reply with the appropriate bit flips. | |
| 45 | * <p> | |
| 46 | * There are two possible formats we can receive: | |
| 47 | * <ul> | |
| 48 | * <li> | |
| 49 | * Traditional embedded documents under a {@code results} array. For this | |
| 50 | * format all of the Documents are in the single reply and there is no | |
| 51 | * cursor established.<blockquote> | |
| 52 | * | |
| 53 | * <pre> | |
| 54 | * <code> | |
| 55 | * { | |
| 56 | * results : [ | |
| 57 | * { ... }, | |
| 58 | * ... | |
| 59 | * ], | |
| 60 | * ok : 1 | |
| 61 | * } | |
| 62 | * </code> | |
| 63 | * </pre> | |
| 64 | * | |
| 65 | * </blockquote></li> | |
| 66 | * <li> | |
| 67 | * A {@code cursor} sub-document containing the cursor's {@code id} and | |
| 68 | * {@code firstBatch}. This reply establishes (possibly) a cursor for the | |
| 69 | * results.<blockquote> | |
| 70 | * | |
| 71 | * <pre> | |
| 72 | * <code> | |
| 73 | * { | |
| 74 | * cursor : { | |
| 75 | * id : 1234567, | |
| 76 | * firstBatch : [ | |
| 77 | * { ... }, | |
| 78 | * ... | |
| 79 | * ] | |
| 80 | * } | |
| 81 | * ok : 1 | |
| 82 | * } | |
| 83 | * </code> | |
| 84 | * </pre> | |
| 85 | * | |
| 86 | * </blockquote></li> | |
| 87 | * | |
| 88 | * | |
| 89 | * @param reply | |
| 90 | * The reply to translate. | |
| 91 | * @return The translated reply. | |
| 92 | */ | |
| 93 | /* package */static Reply translate(final Reply reply) { | |
| 94 | 5 | Reply result = reply; |
| 95 | ||
| 96 | 5 | final List<Reply> replies = translateAll(reply); |
| 97 | 5 | if (replies.size() == 1) { |
| 98 | 5 | result = replies.get(0); |
| 99 | } | |
| 100 | ||
| 101 | 5 | return result; |
| 102 | } | |
| 103 | ||
| 104 | /** | |
| 105 | * Translates the reply from a single document command reply into a list of | |
| 106 | * standard replies with the appropriate bit flips. | |
| 107 | * <p> | |
| 108 | * There are three possible formats we can receive: | |
| 109 | * <ul> | |
| 110 | * <li> | |
| 111 | * Traditional embedded documents under a {@code results} array. For this | |
| 112 | * format all of the Documents are in the single reply and there is no | |
| 113 | * cursor established.<blockquote> | |
| 114 | * | |
| 115 | * <pre> | |
| 116 | * <code> | |
| 117 | * { | |
| 118 | * results : [ | |
| 119 | * { ... }, | |
| 120 | * ... | |
| 121 | * ], | |
| 122 | * ok : 1 | |
| 123 | * } | |
| 124 | * </code> | |
| 125 | * </pre> | |
| 126 | * | |
| 127 | * </blockquote></li> | |
| 128 | * <li> | |
| 129 | * A {@code cursor} sub-document containing the cursor's {@code id} and | |
| 130 | * {@code firstBatch}. This reply establishes (possibly) a cursor for the | |
| 131 | * results.<blockquote> | |
| 132 | * | |
| 133 | * <pre> | |
| 134 | * <code> | |
| 135 | * { | |
| 136 | * cursor : { | |
| 137 | * id : 1234567, | |
| 138 | * firstBatch : [ | |
| 139 | * { ... }, | |
| 140 | * ... | |
| 141 | * ] | |
| 142 | * } | |
| 143 | * ok : 1 | |
| 144 | * } | |
| 145 | * </code> | |
| 146 | * </pre> | |
| 147 | * | |
| 148 | * </blockquote></li> | |
| 149 | * <li> | |
| 150 | * A {@code cursor} sub-array with a sub-document as each element of the | |
| 151 | * array. Each sub-document contains a {@code cursor} document as described | |
| 152 | * above.<blockquote> | |
| 153 | * | |
| 154 | * <pre> | |
| 155 | * <code> | |
| 156 | * { | |
| 157 | * cursors: [ | |
| 158 | * { | |
| 159 | * cursor : { | |
| 160 | * id : 1234567, | |
| 161 | * firstBatch : [ | |
| 162 | * { ... }, | |
| 163 | * ... | |
| 164 | * ] | |
| 165 | * } | |
| 166 | * }, | |
| 167 | * { | |
| 168 | * cursor : { | |
| 169 | * id : 1234568, | |
| 170 | * firstBatch : [ | |
| 171 | * { ... }, | |
| 172 | * ... | |
| 173 | * ] | |
| 174 | * } | |
| 175 | * }, | |
| 176 | * ... | |
| 177 | * ] | |
| 178 | * ok : 1 | |
| 179 | * } | |
| 180 | * </code> | |
| 181 | * </pre> | |
| 182 | * | |
| 183 | * </blockquote></li> | |
| 184 | * | |
| 185 | * | |
| 186 | * @param reply | |
| 187 | * The reply to translate. | |
| 188 | * @return The translated reply. | |
| 189 | */ | |
| 190 | /* package */static List<Reply> translateAll(final Reply reply) { | |
| 191 | 8 | List<Reply> results = Collections.singletonList(reply); |
| 192 | ||
| 193 | // Check for a single Document. All formats to translate are single | |
| 194 | // documents. | |
| 195 | 8 | final List<Document> docs = reply.getResults(); |
| 196 | 8 | if (docs.size() == 1) { |
| 197 | 8 | final Document replyDoc = docs.get(0); |
| 198 | ||
| 199 | List<DocumentElement> resultDocs; | |
| 200 | ||
| 201 | // Traditional first since it is more probable in the short term. | |
| 202 | 8 | final ArrayElement resultArray = replyDoc.get(ArrayElement.class, |
| 203 | "result"); | |
| 204 | 8 | if (resultArray != null) { |
| 205 | 5 | resultDocs = replyDoc.find(DocumentElement.class, "result", |
| 206 | ".*"); | |
| 207 | 5 | results = Collections.singletonList(translate(reply, 0L, |
| 208 | resultDocs)); | |
| 209 | } | |
| 210 | else { | |
| 211 | 3 | final DocumentElement cursor = replyDoc.get( |
| 212 | DocumentElement.class, "cursor"); | |
| 213 | 3 | if (cursor != null) { |
| 214 | 0 | results = translate(reply, |
| 215 | Collections.singletonList(cursor)); | |
| 216 | } | |
| 217 | else { | |
| 218 | 3 | final List<DocumentElement> cursors = replyDoc.find( |
| 219 | DocumentElement.class, "cursors", ".*", "cursor"); | |
| 220 | 3 | if (!cursors.isEmpty()) { |
| 221 | 3 | results = translate(reply, cursors); |
| 222 | } | |
| 223 | } | |
| 224 | } | |
| 225 | } | |
| 226 | ||
| 227 | 8 | return results; |
| 228 | } | |
| 229 | ||
| 230 | /** | |
| 231 | * Translates a list of cursor documents into a list of {@link Reply} | |
| 232 | * objects. | |
| 233 | * | |
| 234 | * @param reply | |
| 235 | * The original reply. | |
| 236 | * @param cursors | |
| 237 | * The cursor sub documents. | |
| 238 | * @return The translated replies. | |
| 239 | */ | |
| 240 | private static List<Reply> translate(final Reply reply, | |
| 241 | final List<DocumentElement> cursors) { | |
| 242 | ||
| 243 | 3 | final List<Reply> results = new ArrayList<Reply>(cursors.size()); |
| 244 | 3 | for (final DocumentElement cursor : cursors) { |
| 245 | 3 | final NumericElement id = cursor.get(NumericElement.class, "id"); |
| 246 | 3 | final List<DocumentElement> resultDocs = cursor.find( |
| 247 | DocumentElement.class, "firstBatch", ".*"); | |
| 248 | ||
| 249 | 3 | results.add(translate(reply, (id == null) ? 0 : id.getLongValue(), |
| 250 | resultDocs)); | |
| 251 | 3 | } |
| 252 | 3 | return results; |
| 253 | } | |
| 254 | ||
| 255 | /** | |
| 256 | * Creates a new reply based on the original reply and the specified cursor | |
| 257 | * id and document list. | |
| 258 | * | |
| 259 | * @param reply | |
| 260 | * The original reply to copy from. | |
| 261 | * @param cursorId | |
| 262 | * The cursor id that has been established. | |
| 263 | * @param results | |
| 264 | * The results to include in the reply. | |
| 265 | * @return The translated reply. | |
| 266 | */ | |
| 267 | private static Reply translate(final Reply reply, final long cursorId, | |
| 268 | final List<DocumentElement> results) { | |
| 269 | ||
| 270 | // Strip off the element-ness of the documents. | |
| 271 | 8 | final List<Document> docs = new ArrayList<Document>(results.size()); |
| 272 | 8 | for (final DocumentElement docElement : results) { |
| 273 | 5 | docs.add(BuilderFactory.start(docElement).build()); |
| 274 | 5 | } |
| 275 | ||
| 276 | 8 | return new Reply(reply.getResponseToId(), cursorId, |
| 277 | reply.getCursorOffset(), docs, reply.isAwaitCapable(), | |
| 278 | reply.isCursorNotFound(), reply.isQueryFailed(), | |
| 279 | reply.isShardConfigStale()); | |
| 280 | } | |
| 281 | ||
| 282 | /** | |
| 283 | * Creates a new CommandCursorTranslator. | |
| 284 | */ | |
| 285 | 0 | private CommandCursorTranslator() { |
| 286 | // Static class. | |
| 287 | 0 | } |
| 288 | ||
| 289 | } |