View Javadoc
1   /*
2    * #%L
3    * JavaScriptWithScopeElement.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.util.Iterator;
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.Visitor;
30  import com.allanbank.mongodb.bson.builder.BuilderFactory;
31  import com.allanbank.mongodb.bson.builder.DocumentBuilder;
32  import com.allanbank.mongodb.bson.io.StringEncoder;
33  
34  /**
35   * A wrapper for a BSON JavaScript with Scope.
36   * 
37   * @api.yes This class is part of the driver's API. Public and protected members
38   *          will be deprecated for at least 1 non-bugfix release (version
39   *          numbers are <major>.<minor>.<bugfix>) before being
40   *          removed or modified.
41   * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
42   */
43  public class JavaScriptWithScopeElement extends JavaScriptElement {
44  
45      /** The BSON type for a string. */
46      @SuppressWarnings("hiding")
47      public static final ElementType TYPE = ElementType.JAVA_SCRIPT_WITH_SCOPE;
48  
49      /** Serialization version for the class. */
50      private static final long serialVersionUID = -5697976862389984453L;
51  
52      /**
53       * Computes and returns the number of bytes that are used to encode the
54       * element.
55       * 
56       * @param name
57       *            The name for the element.
58       * @param javaScript
59       *            The BSON JavaScript value.
60       * @param scope
61       *            The scope for the JavaScript
62       * @return The size of the element when encoded in bytes.
63       */
64      private static long computeSize(final String name, final String javaScript,
65              final Document scope) {
66          long result = 15; // type (1) + name null byte (1) + length (4)
67          // javaScript length (4) and null byte (5)
68          result += StringEncoder.utf8Size(name);
69          result += StringEncoder.utf8Size(javaScript);
70          if (scope != null) {
71              result += scope.size();
72          }
73  
74          return result;
75      }
76  
77      /** The BSON scope value. */
78      private final Document myScope;
79  
80      /**
81       * Constructs a new {@link JavaScriptWithScopeElement}.
82       * 
83       * @param name
84       *            The name for the BSON string.
85       * @param javaScript
86       *            The BSON JavaScript value.
87       * @param scope
88       *            The scope for the JavaScript
89       * @throws IllegalArgumentException
90       *             If the {@code name}, {@code javaScript}, or {@code scope} is
91       *             <code>null</code>.
92       */
93      public JavaScriptWithScopeElement(final String name,
94              final String javaScript, final Document scope) {
95          this(name, javaScript, scope, computeSize(name, javaScript, scope));
96  
97          assertNotNull(scope, "JavaScript element's scope cannot be null.");
98      }
99  
100     /**
101      * Constructs a new {@link JavaScriptWithScopeElement}.
102      * 
103      * @param name
104      *            The name for the BSON string.
105      * @param javaScript
106      *            The BSON JavaScript value.
107      * @param scope
108      *            The scope for the JavaScript
109      * @param size
110      *            The size of the element when encoded in bytes. If not known
111      *            then use the
112      *            {@link JavaScriptWithScopeElement#JavaScriptWithScopeElement(String, String, Document)}
113      *            constructor instead.
114      * @throws IllegalArgumentException
115      *             If the {@code name}, {@code javaScript}, or {@code scope} is
116      *             <code>null</code>.
117      */
118     public JavaScriptWithScopeElement(final String name,
119             final String javaScript, final Document scope, final long size) {
120         super(name, javaScript, size);
121 
122         assertNotNull(scope, "JavaScript element's scope cannot be null.");
123 
124         myScope = scope;
125     }
126 
127     /**
128      * Accepts the visitor and calls the
129      * {@link Visitor#visitJavaScript(String,String,Document)} method.
130      * 
131      * @see Element#accept(Visitor)
132      */
133     @Override
134     public void accept(final Visitor visitor) {
135         visitor.visitJavaScript(getName(), getJavaScript(), getScope());
136     }
137 
138     /**
139      * {@inheritDoc}
140      * <p>
141      * Overridden to compare the Java Script (as text) if the base class
142      * comparison is equals.
143      * </p>
144      */
145     @Override
146     public int compareTo(final Element otherElement) {
147         int result = super.compareTo(otherElement);
148 
149         if (result == 0) {
150             final JavaScriptWithScopeElement other = (JavaScriptWithScopeElement) otherElement;
151 
152             final Iterator<Element> thisIter = myScope.iterator();
153             final Iterator<Element> otherIter = other.myScope.iterator();
154             while (thisIter.hasNext() && otherIter.hasNext()) {
155                 result = thisIter.next().compareTo(otherIter.next());
156                 if (result != 0) {
157                     return result;
158                 }
159             }
160 
161             if (thisIter.hasNext()) {
162                 return 1;
163             }
164             else if (otherIter.hasNext()) {
165                 return -1;
166             }
167             else {
168                 return 0;
169             }
170         }
171 
172         return result;
173     }
174 
175     /**
176      * Determines if the passed object is of this same type as this object and
177      * if so that its fields are equal.
178      * 
179      * @param object
180      *            The object to compare to.
181      * 
182      * @see java.lang.Object#equals(java.lang.Object)
183      */
184     @Override
185     public boolean equals(final Object object) {
186         boolean result = false;
187         if (this == object) {
188             result = true;
189         }
190         else if ((object != null) && (getClass() == object.getClass())) {
191             final JavaScriptWithScopeElement other = (JavaScriptWithScopeElement) object;
192 
193             result = super.equals(object)
194                     && nullSafeEquals(myScope, other.myScope);
195         }
196         return result;
197     }
198 
199     /**
200      * Returns the BSON JavaScript scope.
201      * 
202      * @return The BSON JavaScript scope.
203      */
204     public Document getScope() {
205         return myScope;
206     }
207 
208     /**
209      * {@inheritDoc}
210      */
211     @Override
212     public ElementType getType() {
213         return TYPE;
214     }
215 
216     /**
217      * {@inheritDoc}
218      * <p>
219      * Returns a document representing the code and scope similar to the strict
220      * JSON encoding.
221      * </p>
222      * <p>
223      * <b>Note:</b> This value will not be recreated is a Object-->Element
224      * conversion. A more generic sub-document is created instead.
225      * </p>
226      */
227     @Override
228     public Document getValueAsObject() {
229         final DocumentBuilder b = BuilderFactory.start();
230         b.add("$code", getJavaScript());
231         b.add("$scope", myScope);
232 
233         return b.build();
234     }
235 
236     /**
237      * Computes a reasonable hash code.
238      * 
239      * @return The hash code value.
240      */
241     @Override
242     public int hashCode() {
243         int result = 1;
244         result = (31 * result) + super.hashCode();
245         result = (31 * result) + ((myScope != null) ? myScope.hashCode() : 3);
246         return result;
247     }
248 
249     /**
250      * {@inheritDoc}
251      * <p>
252      * Returns a new {@link JavaScriptElement}.
253      * </p>
254      */
255     @Override
256     public JavaScriptWithScopeElement withName(final String name) {
257         if (getName().equals(name)) {
258             return this;
259         }
260         return new JavaScriptWithScopeElement(name, getJavaScript(), myScope);
261     }
262 }