View Javadoc
1   /*
2    * #%L
3    * AbstractDocument.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.bson.impl;
22  
23  import java.io.StringWriter;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.regex.Pattern;
31  import java.util.regex.PatternSyntaxException;
32  
33  import com.allanbank.mongodb.bson.Document;
34  import com.allanbank.mongodb.bson.Element;
35  import com.allanbank.mongodb.bson.Visitor;
36  import com.allanbank.mongodb.bson.element.JsonSerializationVisitor;
37  import com.allanbank.mongodb.util.PatternUtils;
38  
39  /**
40   * AbstractDocument provides a base class for all document implementations with
41   * the common functionality.
42   * 
43   * @copyright 2013, Allanbank Consulting, Inc., All Rights Reserved
44   */
45  public abstract class AbstractDocument implements Document {
46  
47      /** The empty list of elements. */
48      public static final List<Element> EMPTY_ELEMENTS = Collections.emptyList();
49  
50      /** The base type (interface) for all elements. */
51      protected static final Class<Element> ELEMENT_TYPE = Element.class;
52  
53      /** The serialization id for the class. */
54      private static final long serialVersionUID = -425294885378885212L;
55  
56      /**
57       * Creates a new AbstractDocument.
58       */
59      public AbstractDocument() {
60          super();
61      }
62  
63      /**
64       * Accepts the visitor and calls the {@link Visitor#visitDocument} method.
65       * 
66       * @see Element#accept(Visitor)
67       */
68      @Override
69      public void accept(final Visitor visitor) {
70          visitor.visit(getElements());
71      }
72  
73      /**
74       * {@inheritDoc}
75       * <p>
76       * Returns this document.
77       * </p>
78       */
79      @Override
80      public Document asDocument() {
81          return this;
82      }
83  
84      /**
85       * Returns true if the document contains an element with the specified name.
86       * 
87       * @see Document#contains(String)
88       */
89      @Override
90      public boolean contains(final String name) {
91          return getElementMap().containsKey(name);
92      }
93  
94      /**
95       * Determines if the passed object is of this same type as this object and
96       * if so that its fields are equal.
97       * 
98       * @param object
99       *            The object to compare to.
100      * 
101      * @see java.lang.Object#equals(java.lang.Object)
102      */
103     @Override
104     public boolean equals(final Object object) {
105         boolean result = false;
106         if (this == object) {
107             result = true;
108         }
109         else if ((object != null) && (object instanceof Document)) {
110             final Document other = (Document) object;
111 
112             result = getElements().equals(other.getElements());
113         }
114         return result;
115     }
116 
117     /**
118      * {@inheritDoc}
119      * <p>
120      * Searches this sub-elements for matching elements on the path and are of
121      * the right type.
122      * </p>
123      * 
124      * @see Document#find
125      */
126     @Override
127     public <E extends Element> List<E> find(final Class<E> clazz,
128             final String... nameRegexs) {
129         List<E> elements = Collections.emptyList();
130         if (0 < nameRegexs.length) {
131             final List<Element> docElements = getElements();
132             final String nameRegex = nameRegexs[0];
133             final String[] subNameRegexs = Arrays.copyOfRange(nameRegexs, 1,
134                     nameRegexs.length);
135 
136             elements = new ArrayList<E>();
137             try {
138                 final Pattern pattern = PatternUtils.toPattern(nameRegex);
139                 for (final Element element : docElements) {
140                     if (pattern.matcher(element.getName()).matches()) {
141                         elements.addAll(element.find(clazz, subNameRegexs));
142                     }
143                 }
144             }
145             catch (final PatternSyntaxException pse) {
146                 // Assume a non-pattern?
147                 for (final Element element : docElements) {
148                     if (nameRegex.equals(element.getName())) {
149                         elements.addAll(element.find(clazz, subNameRegexs));
150                     }
151                 }
152             }
153         }
154 
155         // End of the path but we are a document?
156         return elements;
157     }
158 
159     /**
160      * {@inheritDoc}
161      * <p>
162      * Searches this sub-elements for matching elements on the path.
163      * </p>
164      * 
165      * @see Document#find
166      */
167     @Override
168     public List<Element> find(final String... nameRegexs) {
169         return find(ELEMENT_TYPE, nameRegexs);
170     }
171 
172     /**
173      * {@inheritDoc}
174      * <p>
175      * Searches this sub-elements for matching elements on the path and are of
176      * the right type.
177      * </p>
178      * 
179      * @see Document#findFirst
180      */
181     @Override
182     public <E extends Element> E findFirst(final Class<E> clazz,
183             final String... nameRegexs) {
184         E element = null;
185         if (0 < nameRegexs.length) {
186             final List<Element> docElements = getElements();
187             final String nameRegex = nameRegexs[0];
188             final String[] subNameRegexs = Arrays.copyOfRange(nameRegexs, 1,
189                     nameRegexs.length);
190 
191             try {
192                 final Pattern pattern = PatternUtils.toPattern(nameRegex);
193                 final Iterator<Element> iter = docElements.iterator();
194                 while (iter.hasNext() && (element == null)) {
195                     final Element docElement = iter.next();
196                     if (pattern.matcher(docElement.getName()).matches()) {
197                         element = docElement.findFirst(clazz, subNameRegexs);
198                     }
199                 }
200             }
201             catch (final PatternSyntaxException pse) {
202                 // Assume a non-pattern?
203                 final Iterator<Element> iter = docElements.iterator();
204                 while (iter.hasNext() && (element == null)) {
205                     final Element docElement = iter.next();
206                     if (nameRegex.equals(docElement.getName())) {
207                         element = docElement.findFirst(clazz, subNameRegexs);
208                     }
209                 }
210             }
211         }
212 
213         // End of the path but we are a document?
214         return element;
215     }
216 
217     /**
218      * {@inheritDoc}
219      * <p>
220      * Searches this sub-elements for matching elements on the path and are of
221      * the right type.
222      * </p>
223      * 
224      * @see Document#findFirst
225      */
226     @Override
227     public Element findFirst(final String... nameRegexs) {
228         return findFirst(ELEMENT_TYPE, nameRegexs);
229     }
230 
231     /**
232      * Returns the element with the specified name and type or null if no
233      * element with that name and type exists.
234      * 
235      * @see Document#get(String)
236      */
237     @Override
238     public <E extends Element> E get(final Class<E> clazz, final String name) {
239         final Element element = get(name);
240         if ((element != null) && clazz.isAssignableFrom(element.getClass())) {
241             return clazz.cast(element);
242         }
243         return null;
244     }
245 
246     /**
247      * Returns the element with the specified name or null if no element with
248      * that name exists.
249      * 
250      * @see Document#get(String)
251      */
252     @Override
253     public Element get(final String name) {
254         return getElementMap().get(name);
255     }
256 
257     /**
258      * {@inheritDoc}
259      */
260     @Override
261     public abstract List<Element> getElements();
262 
263     /**
264      * Computes a reasonable hash code.
265      * 
266      * @return The hash code value.
267      */
268     @Override
269     public int hashCode() {
270         return getElements().hashCode();
271     }
272 
273     /**
274      * Returns an iterator over the documents elements.
275      * 
276      * @see Iterable#iterator()
277      */
278     @Override
279     public Iterator<Element> iterator() {
280         return getElements().iterator();
281     }
282 
283     /**
284      * String form of the object.
285      * 
286      * @return A human readable form of the object.
287      * 
288      * @see java.lang.Object#toString()
289      */
290     @Override
291     public String toString() {
292         final StringWriter writer = new StringWriter();
293         final JsonSerializationVisitor visitor = new JsonSerializationVisitor(
294                 writer, false);
295 
296         accept(visitor);
297 
298         return writer.toString();
299     }
300 
301     /**
302      * Returns the mapping from the names of elements to the element.
303      * 
304      * @return The mapping from the names of elements to the element.
305      */
306     protected abstract Map<String, Element> getElementMap();
307 
308 }