001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.schematic.document;
017
018import java.io.Serializable;
019import java.util.Date;
020import java.util.List;
021import java.util.Map;
022import java.util.Properties;
023import java.util.Set;
024import java.util.UUID;
025import java.util.regex.Pattern;
026import org.modeshape.schematic.SchemaLibrary;
027
028/**
029 * Primary read-only interface for an in-memory representation of JSON/BSON objects.
030 * 
031 * @author Randall Hauch <rhauch@redhat.com> (C) 2011 Red Hat Inc.
032 */
033public interface Document extends Serializable {
034
035    /**
036     * Gets the value in this document for the given field name.
037     * 
038     * @param name The name of the pair
039     * @return The field value, if found, or null otherwise
040     */
041    Object get( String name );
042
043    /**
044     * Get the boolean value in this document for the given field name.
045     * 
046     * @param name The name of the pair
047     * @return The boolean field value, if found, or null if there is no such pair or if the value is not a boolean
048     */
049    Boolean getBoolean( String name );
050
051    /**
052     * Get the boolean value in this document for the given field name.
053     * 
054     * @param name The name of the pair
055     * @param defaultValue the default value to return if there is no such pair or if the value is not a boolean
056     * @return The boolean field value if found, or <code>defaultValue</code> if there is no such pair or if the value is not a
057     *         boolean
058     */
059    boolean getBoolean( String name,
060                        boolean defaultValue );
061
062    /**
063     * Get the integer value in this document for the given field name.
064     * 
065     * @param name The name of the pair
066     * @return The integer field value, if found, or null if there is no such pair or if the value is not an integer
067     */
068    Integer getInteger( String name );
069
070    /**
071     * Get the integer value in this document for the given field name.
072     * 
073     * @param name The name of the pair
074     * @param defaultValue the default value to return if there is no such pair or if the value is not a integer
075     * @return The integer field value if found, or <code>defaultValue</code> if there is no such pair or if the value is not a
076     *         integer
077     */
078    int getInteger( String name,
079                    int defaultValue );
080
081    /**
082     * Get the integer value in this document for the given field name.
083     * 
084     * @param name The name of the pair
085     * @return The long field value, if found, or null if there is no such pair or if the value is not a long value
086     */
087    Long getLong( String name );
088
089    /**
090     * Get the long value in this document for the given field name.
091     * 
092     * @param name The name of the pair
093     * @param defaultValue the default value to return if there is no such pair or if the value is not a long value
094     * @return The long field value if found, or <code>defaultValue</code> if there is no such pair or if the value is not a long
095     *         value
096     */
097    long getLong( String name,
098                  long defaultValue );
099
100    /**
101     * Get the double value in this document for the given field name.
102     * 
103     * @param name The name of the pair
104     * @return The double field value, if found, or null if there is no such pair or if the value is not a double
105     */
106    Double getDouble( String name );
107
108    /**
109     * Get the double value in this document for the given field name.
110     * 
111     * @param name The name of the pair
112     * @param defaultValue the default value to return if there is no such pair or if the value is not a double
113     * @return The double field value if found, or <code>defaultValue</code> if there is no such pair or if the value is not a
114     *         double
115     */
116    double getDouble( String name,
117                      double defaultValue );
118
119    /**
120     * Get the number value in this document for the given field name.
121     * 
122     * @param name The name of the pair
123     * @return The double field value, if found, or null if there is no such pair or if the value is not a number
124     */
125    Number getNumber( String name );
126
127    /**
128     * Get the number value in this document for the given field name.
129     * 
130     * @param name The name of the pair
131     * @param defaultValue the default value to return if there is no such pair or if the value is not a number
132     * @return The number field value if found, or <code>defaultValue</code> if there is no such pair or if the value is not a
133     *         number
134     */
135    Number getNumber( String name,
136                      Number defaultValue );
137
138    /**
139     * Get the string value in this document for the given field name. This method will return the string even if the actual value
140     * is a {@link Symbol}.
141     * 
142     * @param name The name of the pair
143     * @return The string field value, if found, or null if there is no such pair or if the value is not a string
144     */
145    String getString( String name );
146
147    /**
148     * Get the string value in this document for the given field name. This method will return the string even if the actual value
149     * is a {@link Symbol}.
150     * 
151     * @param name The name of the pair
152     * @param defaultValue the default value to return if there is no such pair or if the value is not a string
153     * @return The string field value if found, or <code>defaultValue</code> if there is no such pair or if the value is not a
154     *         string
155     */
156    String getString( String name,
157                      String defaultValue );
158
159    /**
160     * Get the array value in this document for the given field name.
161     * 
162     * @param name The name of the pair
163     * @return The array field value (as a list), if found, or null if there is no such pair or if the value is not an array
164     */
165    List<?> getArray( String name );
166
167    /**
168     * Get the document value in this document for the given field name.
169     * 
170     * @param name The name of the pair
171     * @return The document field value, if found, or null if there is no such pair or if the value is not a document
172     */
173    Document getDocument( String name );
174
175    /**
176     * Determine whether this object has a pair with the given the name and the value is null. This is equivalent to calling:
177     * 
178     * <pre>
179     * this.get(name) instanceof Null;
180     * </pre>
181     * 
182     * @param name The name of the pair
183     * @return <code>true</code> if the field has been set to a {@link Null} value, or false otherwise
184     * @see #isNullOrMissing(String)
185     */
186    boolean isNull( String name );
187
188    /**
189     * Determine whether this object has a pair with the given the name and the value is null, or if this object has no field with
190     * the given name. This is equivalent to calling:
191     * 
192     * <pre>
193     * Null.matches(this.get(name));
194     * </pre>
195     * 
196     * @param name The name of the pair
197     * @return <code>true</code> if the field value for the name is null or if there is no such field.
198     * @see #isNull(String)
199     */
200    boolean isNullOrMissing( String name );
201
202    /**
203     * Get the {@link MaxKey} value in this document for the given field name.
204     * 
205     * @param name The name of the pair
206     * @return The {@link MaxKey} field value, if found, or null if there is no such pair or if the value is not a {@link MaxKey}
207     */
208    MaxKey getMaxKey( String name );
209
210    /**
211     * Get the {@link MinKey} value in this document for the given field name.
212     * 
213     * @param name The name of the pair
214     * @return The {@link MinKey} field value, if found, or null if there is no such pair or if the value is not a {@link MinKey}
215     */
216    MinKey getMinKey( String name );
217
218    /**
219     * Get the {@link Code} value in this document for the given field name.
220     * 
221     * @param name The name of the pair
222     * @return The {@link Code} field value, if found, or null if there is no such pair or if the value is not a {@link Code}
223     */
224    Code getCode( String name );
225
226    /**
227     * Get the {@link CodeWithScope} value in this document for the given field name.
228     * 
229     * @param name The name of the pair
230     * @return The {@link CodeWithScope} field value, if found, or null if there is no such pair or if the value is not a
231     *         {@link CodeWithScope}
232     */
233    CodeWithScope getCodeWithScope( String name );
234
235    /**
236     * Get the {@link ObjectId} value in this document for the given field name.
237     * 
238     * @param name The name of the pair
239     * @return The {@link ObjectId} field value, if found, or null if there is no such pair or if the value is not a
240     *         {@link ObjectId}
241     */
242    ObjectId getObjectId( String name );
243
244    /**
245     * Get the {@link Binary} value in this document for the given field name.
246     * 
247     * @param name The name of the pair
248     * @return The {@link Binary} field value, if found, or null if there is no such pair or if the value is not a {@link Binary}
249     */
250    Binary getBinary( String name );
251    /**
252     * Get the {@link Date} value in this document for the given field name.
253     * 
254     * @param name The name of the pair
255     * @return The {@link Date} field value, if found, or null if there is no such pair or if the value is not a {@link Date}
256     */
257    Date getDate( String name );
258
259    /**
260     * Get the {@link Symbol} value in this document for the given field name.
261     * 
262     * @param name The name of the pair
263     * @return The {@link Symbol} field value, if found, or null if there is no such pair or if the value is not a {@link Symbol}
264     */
265    Symbol getSymbol( String name );
266
267    /**
268     * Get the {@link Pattern} value in this document for the given field name.
269     * 
270     * @param name The name of the pair
271     * @return The {@link Pattern} field value, if found, or null if there is no such pair or if the value is not a
272     *         {@link Pattern}
273     */
274    Pattern getPattern( String name );
275
276    /**
277     * Get the {@link UUID} value in this document for the given field name.
278     * 
279     * @param name The name of the pair
280     * @return The {@link UUID} field value, if found, or null if there is no such pair or if the value is not a {@link UUID}
281     */
282    UUID getUuid( String name );
283
284    /**
285     * Get the {@link UUID} value in this document for the given field name.
286     * 
287     * @param name The name of the pair
288     * @param defaultValue the default value to return if there is no such pair or if the value is not a string
289     * @return The {@link UUID} field value if found, or <code>defaultValue</code> if there is no such pair or if the value is not
290     *         a UUID (or a string that is convertable from a UUID)
291     */
292    UUID getUuid( String name,
293                  UUID defaultValue );
294
295    /**
296     * Get the {@link Bson.Type} constant that describes the type of value for the given field name.
297     * 
298     * @param name The name of the pair
299     * @return the {@link Bson.Type} constant describing the value, or -1 if there is no field with the supplied name
300     */
301    int getType( String name );
302
303    /**
304     * Returns a map representing this BSONObject.
305     * 
306     * @return the map
307     */
308    Map<String, ?> toMap();
309
310    /**
311     * Obtain an iterator over the {@link Field}s in this object.
312     * 
313     * @return a field iterator; never null
314     */
315    Iterable<Field> fields();
316
317    /**
318     * Checks if this object contains a field with the given name.
319     * 
320     * @param name The name of the pair for which to check
321     * @return true if this document contains a field with the supplied name, or false otherwise
322     */
323    boolean containsField( String name );
324
325    /**
326     * Checks if this object contains all of the fields in the supplied document.
327     * 
328     * @param document The document with the fields that should be in this document
329     * @return true if this document contains all of the fields in the supplied document, or false otherwise
330     */
331    boolean containsAll( Document document );
332
333    /**
334     * Returns this object's fields' names
335     * 
336     * @return The names of the fields in this object
337     */
338    Set<String> keySet();
339
340    /**
341     * Return the number of name-value pairs in this object.
342     * 
343     * @return the number of name-value pairs; never negative
344     */
345    int size();
346
347    /**
348     * Return whether this document contains no fields and is therefore empty.
349     * 
350     * @return true if there are no fields in this document, or false if there is at least one.
351     */
352    boolean isEmpty();
353
354    /**
355     * Obtain a clone of this document.
356     * 
357     * @return the clone of this document; never null
358     */
359    Document clone();
360
361    /**
362     * Obtain a clone of this document, but with the supplied fields replaced.
363     * 
364     * @param changedFields the fields that should be changed; may be null
365     * @return the clone of this document with the change fields, or this document if there are no changes
366     */
367    Document with( Map<String, Object> changedFields );
368
369    /**
370     * Obtain a clone of this document, but with the supplied fields replaced.
371     * 
372     * @param fieldName the name of the file that should be changed; may be null
373     * @param value the new value for the field
374     * @return the clone of this document with the change fields, or this document if there are no changes
375     */
376    Document with( String fieldName,
377                   Object value );
378
379    /**
380     * Obtain a clone of this document, but with the field values transformed using the supplied {@link ValueTransformer}.
381     * 
382     * @param transformer the transformer that should be used to transform each field value; may not be null
383     * @return the clone of this document with transformed fields, or this document if the transformer changed none of the values
384     */
385    Document with( ValueTransformer transformer );
386
387    /**
388     * Obtain a clone of this document, but with all variables in string field values replaced with the referenced values from the
389     * supplied properties.
390     * <p>
391     * Variables may appear anywhere within a string value, and multiple variables can be used within the same value. Variables
392     * take the form:
393     * 
394     * <pre>
395     *    variable := '${' variableNames [ ':' defaultValue ] '}'
396     *    
397     *    variableNames := variableName [ ',' variableNames ]
398     *    
399     *    variableName := /* any characters except ',' and ':' and '}'
400     *    
401     *    defaultValue := /* any characters except
402     * </pre>
403     * 
404     * Note that <i>variableName</i> is the name used to look up the {@link Properties} property.
405     * </p>
406     * Notice that the syntax supports multiple <i>variables</i>. The logic will process the <i>variables</i> from let to right,
407     * until an existing System property is found. And at that point, it will stop and will not attempt to find values for the
408     * other <i>variables</i>.
409     * <p>
410     * 
411     * @param properties the properties keyed by variable name
412     * @return the clone of this document with variables in fields string values replaced with values from the properties object,
413     *         or this document if no variables were found
414     * @see #withVariablesReplacedWithSystemProperties()
415     * @see #with(ValueTransformer)
416     * @see SchemaLibrary#convertValues(Document, String)
417     */
418    Document withVariablesReplaced( Properties properties );
419
420    /**
421     * Obtain a clone of this document, but with all variables in string field values replaced with the referenced values from the
422     * System properties.
423     * <p>
424     * Variables may appear anywhere within a string value, and multiple variables can be used within the same value. Variables
425     * take the form:
426     * 
427     * <pre>
428     *    variable := '${' variableNames [ ':' defaultValue ] '}'
429     *    
430     *    variableNames := variableName [ ',' variableNames ]
431     *    
432     *    variableName := /* any characters except ',' and ':' and '}'
433     *    
434     *    defaultValue := /* any characters except
435     * </pre>
436     * 
437     * Note that <i>variableName</i> is the name used to look up a System property via {@link System#getProperty(String)}.
438     * </p>
439     * Notice that the syntax supports multiple <i>variables</i>. The logic will process the <i>variables</i> from let to right,
440     * until an existing System property is found. And at that point, it will stop and will not attempt to find values for the
441     * other <i>variables</i>.
442     * <p>
443     * <p>
444     * Because only string values can contain variables, the resulting values are left as strings. This may not be valid according
445     * to the document's JSON Schema, so see {@link SchemaLibrary#convertValues(Document, String)} to convert the string values
446     * after variable substitution into the expected non-string types.
447     * </p>
448     * 
449     * @return the clone of this document with variables in fields string values replaced with values from the System properties,
450     *         or this document if no variables were found
451     * @see #withVariablesReplaced(Properties)
452     * @see #with(ValueTransformer)
453     * @see SchemaLibrary#convertValues(Document, String)
454     */
455    Document withVariablesReplacedWithSystemProperties();
456
457    /**
458     * Obtains an editor for the supplied document. The editor allows the caller to make changes to the document and to obtain
459     * these changes as a {@link Changes serializable memento} that can be applied to another document.
460     * 
461     * @return an {@link Editor} instance which can used to change the document; never {@code null} 
462     */
463    Editor edit(boolean clone);
464
465    /**
466     * Returns an editable view of the given document. Any changes will be reflected directly in the underlying document
467     * 
468     * @return a {@link EditableDocument} instance, never {@code null}
469     */
470    EditableDocument editable();
471
472    /**
473     * A component that can transform field values, via {@link Document#with(ValueTransformer)}. Implementations do not need to
474     * worry about {@link Document} values, since the transformer is never called on such values.
475     */
476    interface ValueTransformer {
477        /**
478         * Transform the supplied field value.
479         * 
480         * @param name the name of the field; never null
481         * @param value the existing value for the field
482         * @return the transformed value; never null but may be the same <code>value</code> object if no transformation should be
483         *         made
484         */
485        Object transform( String name,
486                          Object value );
487    }
488
489    interface Field extends Comparable<Field> {
490
491        /**
492         * Get the name of the field
493         * 
494         * @return the field's name; never null
495         */
496        String getName();
497
498        /**
499         * Get the value of the field.
500         * 
501         * @return the field's value; may be null
502         */
503        Object getValue();
504
505        String getValueAsString();
506
507        Integer getValueAsInt();
508
509        boolean getValueAsBoolean();
510
511        Binary getValueAsBinary();
512
513        Number getValueAsNumber();
514
515        Pattern getValueAsPattern();
516
517        Double getValueAsDouble();
518
519        UUID getValueAsUuid();
520
521        Document getValueAsDocument();
522    }
523}