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.impl;
21
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.concurrent.atomic.AtomicReference;
31
32 import com.allanbank.mongodb.bson.Document;
33 import com.allanbank.mongodb.bson.Element;
34 import com.allanbank.mongodb.bson.element.ObjectId;
35 import com.allanbank.mongodb.bson.element.ObjectIdElement;
36
37
38
39
40
41
42
43
44 public class RootDocument extends AbstractDocument {
45
46
47 private static final long serialVersionUID = -2875918328146027036L;
48
49
50
51
52
53
54
55
56
57 private static long computeSize(final List<Element> entries) {
58 long result = 5;
59 if ((entries != null) && !entries.isEmpty()) {
60 for (final Element element : entries) {
61 result += element.size();
62 }
63 }
64
65 return result;
66 }
67
68
69 final AtomicReference<List<Element>> myElements;
70
71
72
73
74
75 final boolean myIdKnownPresent;
76
77
78
79
80
81 private final AtomicReference<Map<String, Element>> myElementMap;
82
83
84 private transient long mySize;
85
86
87
88
89
90
91
92 public RootDocument(final Element... elements) {
93 this(Arrays.asList(elements), false);
94 }
95
96
97
98
99
100
101
102 public RootDocument(final List<Element> elements) {
103 this(elements, false);
104 }
105
106
107
108
109
110
111
112
113
114 public RootDocument(final List<Element> elements, final boolean idPresent) {
115 this(elements, idPresent, computeSize(elements));
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129
130 public RootDocument(final List<Element> elements, final boolean idPresent,
131 final long size) {
132 myElements = new AtomicReference<List<Element>>();
133 myElementMap = new AtomicReference<Map<String, Element>>();
134 if ((elements != null) && !elements.isEmpty()) {
135 myElements.set(Collections.unmodifiableList(new ArrayList<Element>(
136 elements)));
137 }
138 else {
139 myElements.set(EMPTY_ELEMENTS);
140 }
141 myIdKnownPresent = idPresent;
142 mySize = size;
143 }
144
145
146
147
148
149
150 @Override
151 public boolean contains(final String name) {
152 return (myIdKnownPresent && "_id".equals(name)) || super.contains(name);
153 }
154
155
156
157
158
159
160 @Override
161 public List<Element> getElements() {
162 return myElements.get();
163 }
164
165
166
167
168 public void injectId() {
169 if (!contains("_id")) {
170 final List<Element> old = myElements.get();
171
172 final ObjectIdElement toAdd = new ObjectIdElement("_id",
173 new ObjectId());
174
175 final List<Element> newElements = new ArrayList<Element>(
176 old.size() + 1);
177 newElements.add(toAdd);
178 newElements.addAll(old);
179
180 if (myElements.compareAndSet(old, newElements)) {
181 myElementMap.set(null);
182 mySize += toAdd.size();
183 }
184 }
185 }
186
187
188
189
190
191
192 @Override
193 public long size() {
194 return mySize;
195 }
196
197
198
199
200
201
202
203 @Override
204 protected Map<String, Element> getElementMap() {
205 if (myElementMap.get() == null) {
206 final List<Element> elements = myElements.get();
207 final Map<String, Element> mapping = new HashMap<String, Element>(
208 elements.size() + elements.size());
209
210 for (final Element element : elements) {
211 mapping.put(element.getName(), element);
212 }
213
214
215 myElementMap.compareAndSet(null, mapping);
216 }
217
218 return myElementMap.get();
219 }
220
221
222
223
224
225
226
227
228
229
230
231 private void readObject(final ObjectInputStream in)
232 throws ClassNotFoundException, IOException {
233 in.defaultReadObject();
234 mySize = computeSize(getElements());
235 }
236 }