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.text.ParseException;
019import java.util.ArrayList;
020import java.util.Date;
021import java.util.Map;
022import java.util.UUID;
023import java.util.regex.Pattern;
024import org.modeshape.schematic.document.Bson.BinaryType;
025import org.modeshape.schematic.internal.document.IncrementalDocumentEditor;
026import org.modeshape.schematic.internal.document.MutableDocument;
027
028public interface EditableDocument extends Document {
029
030    /**
031     * Unwrap this editor to obtain the potentially wrapped document.
032     * 
033     * @return the wrapped document, or this object; never null
034     */
035    Document unwrap();
036
037    /**
038     * Remove the field with the supplied name, and return the value.
039     * 
040     * @param name The name of the field
041     * @return the value that was removed, or null if there was no such value
042     */
043    Object remove( String name );
044
045    /**
046     * Returns the underlying mutable document. 
047     * 
048     * @return a {@link MutableDocument} instance; never null
049     */
050    MutableDocument asMutableDocument();
051
052    /**
053     * Remove all fields from this document.
054     */
055    void removeAll();
056
057    /**
058     * Sets on this object all name/value pairs from the supplied object. If the supplied object is null, this method does
059     * nothing.
060     * 
061     * @param object the object containing the name/value pairs to be set on this object
062     */
063    void putAll( Document object );
064
065    /**
066     * Sets on this object all key/value pairs from the supplied map. If the supplied map is null, this method does nothing.
067     * 
068     * @param map the map containing the name/value pairs to be set on this object
069     */
070    void putAll( Map<? extends String, ? extends Object> map );
071
072    /**
073     * Merges the supplied document onto this document. This will set on this document each of the fields in the supplied
074     * document; nested documents in the supplied document will be merged recursively.
075     * <p>
076     * Consider the following example. If this document contains:
077     * 
078     * <pre>
079     * {
080     *   "firstName" : "Jane",
081     *   "lastName" : "Smith",
082     *   "address" : {
083     *     "street" : "Main Street",
084     *     "city" : "Springfield"
085     *   },
086     *   "phone" : "(800)555-1212"
087     * }
088     * </pre>
089     * 
090     * and another document 'other' contains:
091     * 
092     * <pre>
093     * {
094     *   "lastName" : "Doe",
095     *   "address" : {
096     *     "city" : "Memphis",
097     *     "zip" : 12345
098     *   },
099     *   "phone" : {
100     *     "home" : "(800)555-1212"
101     *   }
102     * }
103     * </pre>
104     * 
105     * then merging 'other' onto the first will result in the first being modified to contain:
106     * 
107     * <pre>
108     * {
109     *   "firstName" : "Jane",
110     *   "lastName" : "Doe",
111     *   "address" : {
112     *     "street" : "Main Street",
113     *     "city" : "Memphis",
114     *     "zip" : 12345
115     *   },
116     *   "phone" : {
117     *     "home" : "(800)555-1212"
118     *   }
119     * }
120     * </pre>
121     * 
122     * @param other the other document whose values should be merged
123     */
124    void merge( Document other );
125
126    /**
127     * Set the value for the field with the given name to the supplied value.
128     * 
129     * @param name The name of the field
130     * @param value the new value for the field
131     * @return This document, to allow for chaining methods
132     */
133    EditableDocument set( String name,
134                          Object value );
135
136    /**
137     * Set the value for the field with the given name to the supplied boolean value.
138     * 
139     * @param name The name of the field
140     * @param value the new value for the field
141     * @return This document, to allow for chaining methods
142     */
143    EditableDocument setBoolean( String name,
144                                 boolean value );
145
146    /**
147     * Set the value for the field with the given name to the supplied integer value.
148     * 
149     * @param name The name of the field
150     * @param value the new value for the field
151     * @return This document, to allow for chaining methods
152     */
153    EditableDocument setNumber( String name,
154                                int value );
155
156    /**
157     * Set the value for the field with the given name to the supplied long value.
158     * 
159     * @param name The name of the field
160     * @param value the new value for the field
161     * @return This document, to allow for chaining methods
162     */
163    EditableDocument setNumber( String name,
164                                long value );
165
166    /**
167     * Set the value for the field with the given name to the supplied float value.
168     * 
169     * @param name The name of the field
170     * @param value the new value for the field
171     * @return This document, to allow for chaining methods
172     */
173    EditableDocument setNumber( String name,
174                                float value );
175
176    /**
177     * Set the value for the field with the given name to the supplied double value.
178     * 
179     * @param name The name of the field
180     * @param value the new value for the field
181     * @return This document, to allow for chaining methods
182     */
183    EditableDocument setNumber( String name,
184                                double value );
185
186    /**
187     * Set the value for the field with the given name to the supplied string value.
188     * 
189     * @param name The name of the field
190     * @param value the new value for the field
191     * @return This document, to allow for chaining methods
192     */
193    EditableDocument setString( String name,
194                                String value );
195
196    /**
197     * Set the value for the field with the given name to a {@link Symbol} created from the supplied string value. Symbols are
198     * defined in the BSON specification as being similar to a string but which exists for those languages that have a specific
199     * symbol type. Symbols are serialized to JSON as a normal string.
200     * 
201     * @param name The name of the field
202     * @param value the new value for the field
203     * @return This document, to allow for chaining methods
204     * @see #setString(String, String)
205     */
206    EditableDocument setSymbol( String name,
207                                String value );
208
209    /**
210     * Set the value for the field with the given name to be a new, empty Document.
211     * 
212     * @param name The name of the field
213     * @return The editable document that was just created; never null
214     */
215    EditableDocument setDocument( String name );
216
217    /**
218     * Set the value for the field with the given name to be the supplied Document.
219     * 
220     * @param name The name of the field
221     * @param document the document
222     * @return The editable document that was just set as the value for the named field; never null and may or may not be the same
223     *         instance as the supplied <code>document</code>.
224     */
225    EditableDocument setDocument( String name,
226                                  Document document );
227
228    /**
229     * Get the existing document value in this document for the given field name.
230     * 
231     * @param name The name of the pair
232     * @return The editable document field value, if found, or null if there is no such pair or if the value is not a document
233     */
234    @Override
235    EditableDocument getDocument( String name );
236
237    /**
238     * Get the existing document value in this document for the given field name, or create a new document if there is no existing
239     * document at this field.
240     * 
241     * @param name The name of the pair
242     * @return The editable document field value; never null
243     */
244    EditableDocument getOrCreateDocument( String name );
245
246    /**
247     * Set the value for the field with the given name to be a new, empty array.
248     * 
249     * @param name The name of the field
250     * @return The editable array that was just created; never null
251     */
252    EditableArray setArray( String name );
253
254    /**
255     * Set the value for the field with the given name to be the supplied array.
256     * 
257     * @param name The name of the field
258     * @param array the array
259     * @return The editable array that was just set as the value for the named field; never null and may or may not be the same
260     *         instance as the supplied <code>array</code>.
261     */
262    EditableArray setArray( String name,
263                            Array array );
264
265    /**
266     * Set the value for the field with the given name to be the supplied array.
267     * 
268     * @param name The name of the field
269     * @param values the (valid) values for the array
270     * @return The editable array that was just set as the value for the named field; never null and may or may not be the same
271     *         instance as the supplied <code>array</code>.
272     */
273    EditableArray setArray( String name,
274                            Object... values );
275
276    /**
277     * Get the existing array value in this document for the given field name.
278     * 
279     * @param name The name of the pair
280     * @return The editable array field value (as a list), if found, or null if there is no such pair or if the value is not an
281     *         array
282     */
283    @Override
284    EditableArray getArray( String name );
285
286    /**
287     * Get the existing array value in this document for the given field name, or create a new array if there is no existing array
288     * at this field.
289     * 
290     * @param name The name of the pair
291     * @return The editable array field value; never null
292     */
293    EditableArray getOrCreateArray( String name );
294
295    /**
296     * Set the value for the field with the given name to the supplied date value.
297     * 
298     * @param name The name of the field
299     * @param value the new value for the field
300     * @return This document, to allow for chaining methods
301     */
302    EditableDocument setDate( String name,
303                              Date value );
304
305    /**
306     * Set the value for the field with the given name to the date value parsed from the ISO-8601 date representation.
307     * Specifically, the date string must match one of these patterns:
308     * <ul>
309     * <li>"<code><i>yyyy</i>-<i>MM</i>-<i>dd</i>T<i>HH</i>:<i>mm</i>:<i>ss</i></code>" where "<code>T</code>" is a literal
310     * character</li>
311     * <li>"<code><i>yyyy</i>-<i>MM</i>-<i>dd</i>T<i>HH</i>:<i>mm</i>:<i>ss</i>Z</code>" where "<code>T</code>" and "
312     * <code>Z</code>" are literal characters</li>
313     * <li>"<code><i>yyyy</i>-<i>MM</i>-<i>dd</i>T<i>HH</i>:<i>mm</i>:<i>ss</i>GMT+<i>00</i>:<i>00</i></code>" where "
314     * <code>T</code>", and "<code>GMT</code>" are literal characters</li>
315     * </ul>
316     * 
317     * @param name The name of the field
318     * @param isoDate the new value for the field
319     * @return This document, to allow for chaining methods
320     * @throws ParseException if the supplied value could not be parsed into a valid date
321     */
322    EditableDocument setDate( String name,
323                              String isoDate ) throws ParseException;
324
325    /**
326     * Set the value for the field with the given name to a {@link Timestamp} with the supplied time in seconds and increment.
327     * Note that {@link Date} values are recommended for most purposes, as they are better suited to most applications'
328     * representations of time instants.
329     * 
330     * @param name The name of the field
331     * @param timeInSeconds the time in seconds for the new Timestamp
332     * @param increment the time increment for the new Timestamp
333     * @return This document, to allow for chaining methods
334     * @see #setDate(String, Date)
335     */
336    EditableDocument setTimestamp( String name,
337                                   int timeInSeconds,
338                                   int increment );
339
340    /**
341     * Set the value for the field with the given name to an {@link ObjectId} created from the supplied hexadecimal binary value.
342     * Object IDs are defined by the BSON specification as 12-byte binary values designed to have a reasonably high probability of
343     * being unique when allocated. Since there is no explicit way to represent these in a JSON document, each ObjectId value is
344     * serialized in a JSON document as a nested document of the form:
345     * 
346     * <pre>
347     * { "$oid" : "<i>12bytesOfIdInBase16</i>" }
348     * </pre>
349     * 
350     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
351     * be converted to an ObjectId value.
352     * <p>
353     * For example, an ObjectId with time value of "1310745823", machine value of "1", process value of "2", and increment value
354     * of "3" would be written as
355     * 
356     * <pre>
357     * { "$oid" : "4e2064df0000010002000003" }
358     * </pre>
359     * 
360     * </p>
361     * 
362     * @param name The name of the field
363     * @param hex the hexadecimal binary value for the ObjectId
364     * @return This document, to allow for chaining methods
365     * @see #setObjectId(String, byte[])
366     * @see #setObjectId(String, int, int, int, int)
367     */
368    EditableDocument setObjectId( String name,
369                                  String hex );
370
371    /**
372     * Set the value for the field with the given name to an {@link ObjectId} created from the supplied 12-byte binary value.
373     * Object IDs are defined by the BSON specification as 12-byte binary values designed to have a reasonably high probability of
374     * being unique when allocated. Since there is no explicit way to represent these in a JSON document, each ObjectId value is
375     * serialized in a JSON document as a nested document of the form:
376     * 
377     * <pre>
378     * { "$oid" : "<i>12bytesOfIdInBase16</i>" }
379     * </pre>
380     * 
381     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
382     * be converted to an ObjectId value.
383     * <p>
384     * For example, an ObjectId with time value of "1310745823", machine value of "1", process value of "2", and increment value
385     * of "3" would be written as
386     * 
387     * <pre>
388     * { "$oid" : "4e2064df0000010002000003" }
389     * </pre>
390     * 
391     * </p>
392     * 
393     * @param name The name of the field
394     * @param bytes the 12-byte value for the ObjectId
395     * @return This document, to allow for chaining methods
396     * @see #setObjectId(String, String)
397     * @see #setObjectId(String, int, int, int, int)
398     */
399    EditableDocument setObjectId( String name,
400                                  byte[] bytes );
401
402    /**
403     * Set the value for the field with the given name to an {@link ObjectId} created from the supplied hexadecimal binary value.
404     * Object IDs are defined by the BSON specification as 12-byte binary values designed to have a reasonably high probability of
405     * being unique when allocated. Since there is no explicit way to represent these in a JSON document, each ObjectId value is
406     * serialized in a JSON document as a nested document of the form:
407     * 
408     * <pre>
409     * { "$oid" : "<i>12bytesOfIdInBase16</i>" }
410     * </pre>
411     * 
412     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
413     * be converted to an ObjectId value.
414     * <p>
415     * For example, an ObjectId with time value of "1310745823", machine value of "1", process value of "2", and increment value
416     * of "3" would be written as
417     * 
418     * <pre>
419     * { "$oid" : "4e2064df0000010002000003" }
420     * </pre>
421     * 
422     * </p>
423     * 
424     * @param name The name of the field
425     * @param time the Unix-style timestamp, which is a signed integer representing the number of seconds before or after January
426     *        1st 1970 (UTC)
427     * @param machine the first three bytes of the (md5) hash of the machine host name, or of the mac/network address, or the
428     *        virtual machine id
429     * @param process the 2 bytes of the process id (or thread id) of the process generating the object id
430     * @param inc an ever incrementing value, or a random number if a counter can't be used in the language/runtime
431     * @return This document, to allow for chaining methods
432     * @see #setObjectId(String, String)
433     * @see #setObjectId(String, byte[])
434     */
435    EditableDocument setObjectId( String name,
436                                  int time,
437                                  int machine,
438                                  int process,
439                                  int inc );
440
441    /**
442     * Set the value for the field with the given name to the supplied regular expression. Regular expression values are
443     * represented in memory using {@link Pattern} instances, and are stored natively in BSON as regular expressions. However,
444     * when serialized to JSON, regular expressions are written as nested documents of the form:
445     * 
446     * <pre>
447     * { "$regex" : "<i>pattern</i>" }
448     * </pre>
449     * 
450     * where "<i>pattern</i>" is the regular expression pattern.
451     * <p>
452     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
453     * be converted to a regular expression value.
454     * </p>
455     * 
456     * @param name The name of the field
457     * @param pattern the regular expression pattern string
458     * @return This document, to allow for chaining methods
459     * @see #setRegularExpression(String, String, int)
460     */
461    EditableDocument setRegularExpression( String name,
462                                           String pattern );
463
464    /**
465     * Set the value for the field with the given name to the supplied regular expression. Regular expression values are
466     * represented in memory using {@link Pattern} instances, and are stored natively in BSON as regular expressions. However,
467     * when serialized to JSON, regular expressions are written as nested documents of the form:
468     * 
469     * <pre>
470     * { "$regex" : "<i>pattern</i>", "$options" : "<i>flags</i>" }
471     * </pre>
472     * 
473     * where "<i>pattern</i>" is the regular expression pattern, and "<i>flags</i>" is a string representation of the regular
474     * expression options.
475     * <p>
476     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
477     * be converted to a regular expression value.
478     * </p>
479     * 
480     * @param name The name of the field
481     * @param pattern the regular expression pattern string
482     * @param flags the bitwise-anded {@link Pattern} options: {@link Pattern#CANON_EQ}, {@link Pattern#CASE_INSENSITIVE},
483     *        {@link Pattern#CASE_INSENSITIVE}, {@link Pattern#COMMENTS}, {@link Pattern#DOTALL}, {@link Pattern#LITERAL},
484     *        {@link Pattern#MULTILINE}, {@link Pattern#UNICODE_CASE}, and {@link Pattern#UNIX_LINES}
485     * @return This document, to allow for chaining methods
486     * @see #setRegularExpression(String, String)
487     */
488    EditableDocument setRegularExpression( String name,
489                                           String pattern,
490                                           int flags );
491
492    /**
493     * Set the value for the field with the given name to be a null value. Both JSON and BSON formats support null values, and
494     * {@link Null} is used for the value in the in-memory representation. The {@link #isNull(String)} methods can be used to
495     * determine if a field has been set to null, or {@link #isNullOrMissing(String)} if the field has not be set or if it has
496     * been set to null.
497     * 
498     * @param name The name of the field
499     * @return This document, to allow for chaining methods
500     * @see #isNull(String)
501     * @see #isNullOrMissing(String)
502     */
503    EditableDocument setNull( String name );
504
505    /**
506     * Set the value for the field with the given name to be a binary value. JSON does not formally support binary values, and so
507     * such values will be encoded using a nested document of the form:
508     * 
509     * <pre>
510     * { "$type" : <i>typeAsInt</i>, "$base64" : "<i>bytesInBase64</i>" }
511     * </pre>
512     * 
513     * where "<i>typeAsInt</i>" is the integer representation of the {@link BinaryType BSON type}, and "<i>bytesInBase64</i>" is
514     * the Base64 encoding of the actual Binary {@link Binary#getBytes() bytes}.
515     * <p>
516     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
517     * be converted to Binary value.
518     * </p>
519     * 
520     * @param name The name of the field
521     * @param type one of the {@link BinaryType BSON type} constants denoting the type of the {@link Binary} value
522     * @param data the bytes for the {@link Binary} value
523     * @return This document, to allow for chaining methods
524     */
525    EditableDocument setBinary( String name,
526                                byte type,
527                                byte[] data );
528
529    /**
530     * Set the value for the field with the given name to be a {@link UUID}. JSON does not formally support binary values, and so
531     * such values will be encoded using a nested document of the form:
532     * 
533     * <pre>
534     * { "$uuid" : "<i>string-form-of-uuid</i>" }
535     * </pre>
536     * 
537     * where "<i>string-form-of-uuid</i>" is the UUID's {@link UUID#toString() string representation}
538     * <p>
539     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
540     * be converted to UUID value.
541     * </p>
542     * 
543     * @param name The name of the field
544     * @param uuid the UUID value
545     * @return This document, to allow for chaining methods
546     */
547    EditableDocument setUuid( String name,
548                              UUID uuid );
549
550    /**
551     * Set the value for the field with the given name to be a {@link Code} or {@link CodeWithScope}. JSON does not formally
552     * support such values, and so when written to JSON they will be encoded using a nested document of the form:
553     * 
554     * <pre>
555     * { "$code" : "<i>code</i>" }
556     * </pre>
557     * 
558     * or, if there is a scope document
559     * 
560     * <pre>
561     * { "$code" : "<i>code</i>", "$scope" : <i>scope document</i> }
562     * </pre>
563     * 
564     * where "<i>code</i>" is the {@link Code}'s {@link Code#getCode() JavaScript code} and <i>scopeDocument</i> is the nested
565     * document representing the {@link CodeWithScope#getScope() scope} in which the JavaScript code should be evaluated.
566     * <p>
567     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
568     * be converted to {@link Code} or {@link CodeWithScope} value.
569     * </p>
570     * <p>
571     * Note that when <code>includeScope</code> is <code>true</code>, the returned {@link EditableDocument} can be used to
572     * populate the scope document.
573     * 
574     * @param name The name of the field
575     * @param code the code
576     * @param includeScope true if the code should include a scope (and if this method should return an {@link EditableDocument}
577     *        for this scope document), or false otherwise
578     * @return if <code>includeScope</code> is <code>true</code>, then the {@link EditableDocument} for the scope; otherwise, this
579     *         document to allow for chaining methods
580     * @see #setCode(String, String, Document)
581     */
582    EditableDocument setCode( String name,
583                              String code,
584                              boolean includeScope );
585
586    /**
587     * Set the value for the field with the given name to be a {@link Code} or {@link CodeWithScope}. JSON does not formally
588     * support such values, and so when written to JSON they will be encoded using a nested document of the form:
589     * 
590     * <pre>
591     * { "$code" : "<i>code</i>" }
592     * </pre>
593     * 
594     * or, if there is a scope document
595     * 
596     * <pre>
597     * { "$code" : "<i>code</i>", "$scope" : <i>scope document</i> }
598     * </pre>
599     * 
600     * where "<i>code</i>" is the {@link Code}'s {@link Code#getCode() JavaScript code} and <i>scopeDocument</i> is the nested
601     * document representing the {@link CodeWithScope#getScope() scope} in which the JavaScript code should be evaluated.
602     * <p>
603     * When nested documents of this form are read by this library's {@link Json JSON reader}, nested documents of this form will
604     * be converted to {@link Code} or {@link CodeWithScope} value.
605     * </p>
606     * 
607     * @param name The name of the field
608     * @param code the code
609     * @param scope the scope in which the JavaScript code should be evaulated, or null if there is no scope
610     * @return the {@link EditableDocument} for the scope, or null if the <code>scope</code> reference is null
611     * @see #setCode(String, String, boolean)
612     */
613    EditableDocument setCode( String name,
614                              String code,
615                              Document scope );
616
617    @Override
618    EditableDocument clone();
619
620    @Override
621    default Editor edit(boolean clone) {
622        return clone ?
623               new IncrementalDocumentEditor(this.clone().asMutableDocument(), new ArrayList<>()) :
624               new IncrementalDocumentEditor(this.asMutableDocument(), new ArrayList<>());
625    }
626
627    @Override
628    default EditableDocument editable() {
629        return this;
630    }
631}