View Javadoc
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 }