1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
34
35
36
37
38
39
40
41
42
43 class WriteVisitor implements SizeAwareVisitor {
44
45
46 protected final BsonOutputStream myOutput;
47
48
49
50
51
52
53
54 public WriteVisitor(final BsonOutputStream output) {
55 myOutput = output;
56 }
57
58
59
60
61
62
63
64 public WriteVisitor(final OutputStream output) {
65 this(new BsonOutputStream(output));
66 }
67
68
69
70
71
72
73 public IOException getError() {
74 return myOutput.getError();
75 }
76
77
78
79
80
81
82 public boolean hasError() {
83 return myOutput.hasError();
84 }
85
86
87
88
89
90 public void reset() {
91 myOutput.reset();
92 }
93
94
95
96
97
98
99
100
101
102
103
104 @Deprecated
105 public int sizeOf(final Document doc) {
106 return (int) doc.size();
107 }
108
109
110
111
112
113
114
115
116
117 public int utf8Size(final String string) {
118 return StringEncoder.utf8Size(string);
119 }
120
121
122
123
124 @Override
125 public void visit(final List<Element> elements) {
126
127 int size = 4 + 1;
128 for (final Element element : elements) {
129 size += element.size();
130 }
131
132 writeElements(elements, size);
133 }
134
135
136
137
138 @Override
139 public void visitArray(final String name, final List<Element> elements) {
140 myOutput.writeByte(ElementType.ARRAY.getToken());
141 myOutput.writeCString(name);
142 visit(elements);
143 }
144
145
146
147
148 @Override
149 public void visitArray(final String name, final List<Element> elements,
150 final long totalSize) {
151 myOutput.writeByte(ElementType.ARRAY.getToken());
152 myOutput.writeCString(name);
153 writeElements(elements,
154 ((int) totalSize) - (myOutput.sizeOfCString(name) + 1));
155 }
156
157
158
159
160 @Override
161 public void visitBinary(final String name, final byte subType,
162 final byte[] data) {
163 myOutput.writeByte(ElementType.BINARY.getToken());
164 myOutput.writeCString(name);
165 switch (subType) {
166 case 2: {
167 myOutput.writeInt(data.length + 4);
168 myOutput.writeByte(subType);
169 myOutput.writeInt(data.length);
170 myOutput.writeBytes(data);
171 break;
172
173 }
174 case 0:
175 default:
176 myOutput.writeInt(data.length);
177 myOutput.writeByte(subType);
178 myOutput.writeBytes(data);
179 break;
180 }
181 }
182
183
184
185
186 @Override
187 public void visitBoolean(final String name, final boolean value) {
188
189 myOutput.writeByte(ElementType.BOOLEAN.getToken());
190 myOutput.writeCString(name);
191 myOutput.writeByte(value ? (byte) 0x01 : 0x00);
192 }
193
194
195
196
197 @SuppressWarnings("deprecation")
198 @Override
199 public void visitDBPointer(final String name, final String databaseName,
200 final String collectionName, final ObjectId id) {
201 myOutput.writeByte(ElementType.DB_POINTER.getToken());
202 myOutput.writeCString(name);
203 myOutput.writeString(databaseName + "." + collectionName);
204
205 myOutput.writeInt(EndianUtils.swap(id.getTimestamp()));
206 myOutput.writeLong(EndianUtils.swap(id.getMachineId()));
207 }
208
209
210
211
212 @Override
213 public void visitDocument(final String name, final List<Element> elements) {
214 myOutput.writeByte(ElementType.DOCUMENT.getToken());
215 myOutput.writeCString(name);
216 visit(elements);
217 }
218
219
220
221
222 @Override
223 public void visitDocument(final String name, final List<Element> elements,
224 final long totalSize) {
225 myOutput.writeByte(ElementType.DOCUMENT.getToken());
226 myOutput.writeCString(name);
227 writeElements(elements,
228 ((int) totalSize) - (myOutput.sizeOfCString(name) + 1));
229 }
230
231
232
233
234 @Override
235 public void visitDouble(final String name, final double value) {
236 myOutput.writeByte(ElementType.DOUBLE.getToken());
237 myOutput.writeCString(name);
238 myOutput.writeLong(Double.doubleToLongBits(value));
239 }
240
241
242
243
244 @Override
245 public void visitInteger(final String name, final int value) {
246 myOutput.writeByte(ElementType.INTEGER.getToken());
247 myOutput.writeCString(name);
248 myOutput.writeInt(value);
249 }
250
251
252
253
254 @Override
255 public void visitJavaScript(final String name, final String code) {
256 myOutput.writeByte(ElementType.JAVA_SCRIPT.getToken());
257 myOutput.writeCString(name);
258 myOutput.writeString(code);
259 }
260
261
262
263
264 @Override
265 public void visitJavaScript(final String name, final String code,
266 final Document scope) {
267 myOutput.writeByte(ElementType.JAVA_SCRIPT_WITH_SCOPE.getToken());
268 myOutput.writeCString(name);
269
270 myOutput.writeInt(4 + StringEncoder.computeStringSize(code)
271 + (int) scope.size());
272 myOutput.writeString(code);
273
274 scope.accept(this);
275 }
276
277
278
279
280 @Override
281 public void visitLong(final String name, final long value) {
282 myOutput.writeByte(ElementType.LONG.getToken());
283 myOutput.writeCString(name);
284 myOutput.writeLong(value);
285 }
286
287
288
289
290 @Override
291 public void visitMaxKey(final String name) {
292 myOutput.writeByte(ElementType.MAX_KEY.getToken());
293 myOutput.writeCString(name);
294 }
295
296
297
298
299 @Override
300 public void visitMinKey(final String name) {
301 myOutput.writeByte(ElementType.MIN_KEY.getToken());
302 myOutput.writeCString(name);
303 }
304
305
306
307
308 @Override
309 public void visitMongoTimestamp(final String name, final long value) {
310 myOutput.writeByte(ElementType.MONGO_TIMESTAMP.getToken());
311 myOutput.writeCString(name);
312 myOutput.writeLong(value);
313 }
314
315
316
317
318 @Override
319 public void visitNull(final String name) {
320 myOutput.writeByte(ElementType.NULL.getToken());
321 myOutput.writeCString(name);
322 }
323
324
325
326
327 @Override
328 public void visitObjectId(final String name, final ObjectId id) {
329 myOutput.writeByte(ElementType.OBJECT_ID.getToken());
330 myOutput.writeCString(name);
331
332 myOutput.writeInt(EndianUtils.swap(id.getTimestamp()));
333 myOutput.writeLong(EndianUtils.swap(id.getMachineId()));
334 }
335
336
337
338
339 @Override
340 public void visitRegularExpression(final String name, final String pattern,
341 final String options) {
342 myOutput.writeByte(ElementType.REGEX.getToken());
343 myOutput.writeCString(name);
344 myOutput.writeCString(pattern);
345 myOutput.writeCString(options);
346 }
347
348
349
350
351 @Override
352 public void visitString(final String name, final String value) {
353 myOutput.writeByte(ElementType.STRING.getToken());
354 myOutput.writeCString(name);
355 myOutput.writeString(value);
356 }
357
358
359
360
361 @Override
362 public void visitSymbol(final String name, final String symbol) {
363 myOutput.writeByte(ElementType.SYMBOL.getToken());
364 myOutput.writeCString(name);
365 myOutput.writeString(symbol);
366 }
367
368
369
370
371 @Override
372 public void visitTimestamp(final String name, final long timestamp) {
373 myOutput.writeByte(ElementType.UTC_TIMESTAMP.getToken());
374 myOutput.writeCString(name);
375 myOutput.writeLong(timestamp);
376 }
377
378
379
380
381
382
383
384
385
386 protected void writeElements(final List<Element> elements, final int size) {
387 myOutput.writeInt(size);
388 for (final Element element : elements) {
389 element.accept(this);
390 }
391 myOutput.writeByte((byte) 0);
392 }
393
394 }