1 /*
2 * #%L
3 * AbstractElement.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.element;
21
22 import static com.allanbank.mongodb.util.Assertions.assertNotNull;
23
24 import java.io.StringWriter;
25 import java.util.Collections;
26 import java.util.List;
27
28 import com.allanbank.mongodb.bson.Element;
29
30 /**
31 * A base class for the basic BSON types.
32 *
33 * @api.yes This class is part of the driver's API. Public and protected members
34 * will be deprecated for at least 1 non-bugfix release (version
35 * numbers are <major>.<minor>.<bugfix>) before being
36 * removed or modified.
37 * @copyright 2011-2014, Allanbank Consulting, Inc., All Rights Reserved
38 */
39 public abstract class AbstractElement implements Element {
40
41 /** The base type (interface) for all elements. */
42 protected static final Class<Element> ELEMENT_TYPE = Element.class;
43
44 /** Serialization version for the class. */
45 private static final long serialVersionUID = 7537761445549731633L;
46
47 /**
48 * Compares two {@code int} values numerically. The value returned is
49 * identical to what would be returned by:
50 *
51 * <pre>
52 * Integer.valueOf(x).compareTo(Integer.valueOf(y))
53 * </pre>
54 *
55 * @param x
56 * the first {@code int} to compare
57 * @param y
58 * the second {@code int} to compare
59 * @return the value {@code 0} if {@code x == y}; a value less than
60 * {@code 0} if {@code x < y}; and a value greater than {@code 0} if
61 * {@code x > y}
62 * @since 1.7
63 */
64 /* package */static int compare(final int x, final int y) {
65 return (x < y) ? -1 : ((x == y) ? 0 : 1);
66 }
67
68 /**
69 * Compares two {@code long} values numerically. The value returned is
70 * identical to what would be returned by:
71 *
72 * <pre>
73 * Long.valueOf(x).compareTo(Long.valueOf(y))
74 * </pre>
75 *
76 * @param x
77 * the first {@code long} to compare
78 * @param y
79 * the second {@code long} to compare
80 * @return the value {@code 0} if {@code x == y}; a value less than
81 * {@code 0} if {@code x < y}; and a value greater than {@code 0} if
82 * {@code x > y}
83 * @since 1.7
84 */
85 /* package */static int compare(final long x, final long y) {
86 return (x < y) ? -1 : ((x == y) ? 0 : 1);
87 }
88
89 /** The name for the BSON type. */
90 private final String myName;
91
92 /** The size of the element when encoded in bytes. */
93 private final long mySize;
94
95 /**
96 * Constructs a new {@link AbstractElement}.
97 *
98 * @param name
99 * The name for the BSON type.
100 * @param size
101 * The size of the element when encoded in bytes.
102 * @throws IllegalArgumentException
103 * If the {@code name} is <code>null</code>.
104 */
105 public AbstractElement(final String name, final long size)
106 throws IllegalArgumentException {
107 assertNotNull(name, "Cannot have an null name on an element.");
108
109 myName = name;
110 mySize = size;
111 }
112
113 /**
114 * {@inheritDoc}
115 * <p>
116 * Returns this element.
117 * </p>
118 */
119 @Override
120 public Element asElement() {
121 return this;
122 }
123
124 /**
125 * {@inheritDoc}
126 * <p>
127 * Overridden to compare the elements based on the tuple (name, type).
128 * Derived classes are responsible for the value portion of the full
129 * comparison.
130 * </p>
131 */
132 @Override
133 public int compareTo(final Element otherElement) {
134 int result = myName.compareTo(otherElement.getName());
135
136 if (result == 0) {
137 // Use the specialized comparison to match MongoDB's ordering of
138 // types.
139 result = getType().compare(otherElement.getType());
140 }
141
142 return result;
143 }
144
145 /**
146 * Determines if the passed object is of this same type as this object and
147 * if so that its fields are equal.
148 *
149 * @param object
150 * The object to compare to.
151 *
152 * @see java.lang.Object#equals(java.lang.Object)
153 */
154 @Override
155 public boolean equals(final Object object) {
156 boolean result = false;
157 if (this == object) {
158 result = true;
159 }
160 else if ((object != null) && (getClass() == object.getClass())) {
161 final AbstractElement other = (AbstractElement) object;
162
163 result = nullSafeEquals(myName, other.myName);
164 }
165 return result;
166 }
167
168 /**
169 * {@inheritDoc}
170 * <p>
171 * Returns a singleton list if the nameRegexs is empty and this element's
172 * type is assignable to E. An empty list otherwise.
173 * </p>
174 *
175 * @see Element#find
176 */
177 @Override
178 public <E extends Element> List<E> find(final Class<E> clazz,
179 final String... nameRegexs) {
180 if ((nameRegexs.length == 0) && clazz.isAssignableFrom(this.getClass())) {
181 // End of the path. Match this element.
182 return Collections.singletonList(clazz.cast(this));
183 }
184
185 return Collections.emptyList();
186 }
187
188 /**
189 * {@inheritDoc}
190 * <p>
191 * Returns a singleton list if the nameRegexs is empty. An empty list
192 * otherwise.
193 * </p>
194 *
195 * @see Element#find
196 */
197 @Override
198 public List<Element> find(final String... nameRegexs) {
199 return find(ELEMENT_TYPE, nameRegexs);
200 }
201
202 /**
203 * {@inheritDoc}
204 * <p>
205 * Returns a {@code this} if the nameRegexs is empty and this element's type
206 * is assignable to E. An empty list otherwise.
207 * </p>
208 *
209 * @see Element#findFirst
210 */
211 @Override
212 public <E extends Element> E findFirst(final Class<E> clazz,
213 final String... nameRegexs) {
214 if ((nameRegexs.length == 0) && clazz.isAssignableFrom(this.getClass())) {
215 // End of the path. Match this element.
216 return clazz.cast(this);
217 }
218
219 return null;
220 }
221
222 /**
223 * {@inheritDoc}
224 * <p>
225 * Searches this sub-elements for matching elements on the path and are of
226 * the right type.
227 * </p>
228 *
229 * @see Element#findFirst
230 */
231 @Override
232 public Element findFirst(final String... nameRegexs) {
233 return findFirst(ELEMENT_TYPE, nameRegexs);
234 }
235
236 /**
237 * Returns the name for the BSON type.
238 *
239 * @return The name for the BSON type.
240 */
241 @Override
242 public String getName() {
243 return myName;
244 }
245
246 /**
247 * {@inheritDoc}
248 * <p>
249 * Uses the {@link JsonSerializationVisitor} to encode the value. In some
250 * cases it will be more efficient to override this method with a more
251 * straight forward conversion.
252 * </p>
253 */
254 @Override
255 public String getValueAsString() {
256 final StringWriter writer = new StringWriter();
257 final JsonSerializationVisitor visitor = new JsonSerializationVisitor(
258 writer, false);
259
260 // Just the value.
261 visitor.setSuppressNames(true);
262
263 accept(visitor);
264
265 return writer.toString();
266 }
267
268 /**
269 * Computes a reasonable hash code.
270 *
271 * @return The hash code value.
272 */
273 @Override
274 public int hashCode() {
275 int result = 1;
276 result = (31 * result) + ((myName == null) ? 0 : myName.hashCode());
277 return result;
278 }
279
280 /**
281 * Returns the number of bytes that are used to encode the element.
282 *
283 * @return The bytes that are used to encode the element.
284 */
285 @Override
286 public long size() {
287 return mySize;
288 }
289
290 /**
291 * String form of the object.
292 *
293 * @return A human readable form of the object.
294 *
295 * @see java.lang.Object#toString()
296 */
297 @Override
298 public String toString() {
299 final StringWriter writer = new StringWriter();
300 final JsonSerializationVisitor visitor = new JsonSerializationVisitor(
301 writer, false);
302
303 accept(visitor);
304
305 return writer.toString();
306 }
307
308 /**
309 * Does a null safe equals comparison.
310 *
311 * @param rhs
312 * The right-hand-side of the comparison.
313 * @param lhs
314 * The left-hand-side of the comparison.
315 * @return True if the rhs equals the lhs. Note: nullSafeEquals(null, null)
316 * returns true.
317 */
318 protected boolean nullSafeEquals(final Object rhs, final Object lhs) {
319 return (rhs == lhs) || ((rhs != null) && rhs.equals(lhs));
320 }
321 }