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.internal.document; 017 018import java.text.ParseException; 019import java.util.Date; 020import java.util.List; 021import java.util.Map; 022import java.util.Objects; 023import java.util.Properties; 024import java.util.Set; 025import java.util.UUID; 026import java.util.regex.Pattern; 027import org.modeshape.schematic.document.Array; 028import org.modeshape.schematic.document.Binary; 029import org.modeshape.schematic.document.Code; 030import org.modeshape.schematic.document.CodeWithScope; 031import org.modeshape.schematic.document.Document; 032import org.modeshape.schematic.document.EditableArray; 033import org.modeshape.schematic.document.EditableDocument; 034import org.modeshape.schematic.document.MaxKey; 035import org.modeshape.schematic.document.MinKey; 036import org.modeshape.schematic.document.Null; 037import org.modeshape.schematic.document.ObjectId; 038import org.modeshape.schematic.document.Symbol; 039 040public class DocumentEditor implements EditableDocument { 041 042 /** The serialVersionUID */ 043 private static final long serialVersionUID = 1L; 044 045 private final MutableDocument document; 046 protected final DocumentValueFactory factory; 047 048 /** 049 * Return the document that was edited. 050 * 051 * @param document the document to be edited 052 */ 053 public DocumentEditor( MutableDocument document ) { 054 assert document != null; 055 this.document = document; 056 this.factory = DefaultDocumentValueFactory.INSTANCE; 057 } 058 059 /** 060 * Return the document that was edited. 061 * 062 * @param document the document to be edited 063 * @param factory the factory that should be used to create value objects 064 */ 065 public DocumentEditor( MutableDocument document, 066 DocumentValueFactory factory ) { 067 assert document != null; 068 this.document = document; 069 this.factory = factory != null ? factory : DefaultDocumentValueFactory.INSTANCE; 070 } 071 072 @Override 073 public DocumentEditor clone() { 074 return new DocumentEditor(this.document.clone(), factory); 075 } 076 077 @Override 078 public DocumentEditor with( Map<String, Object> changedFields ) { 079 return new DocumentEditor((MutableDocument)this.document.with(changedFields), factory); 080 } 081 082 @Override 083 public Document with( String fieldName, 084 Object value ) { 085 return new DocumentEditor((MutableDocument)this.document.with(fieldName, value), factory); 086 } 087 088 @Override 089 public DocumentEditor with( ValueTransformer transformer ) { 090 return new DocumentEditor((MutableDocument)this.document.with(transformer), factory); 091 } 092 093 @Override 094 public Document withVariablesReplaced( Properties properties ) { 095 return new DocumentEditor((MutableDocument)this.document.withVariablesReplaced(properties), factory); 096 } 097 098 @Override 099 public Document withVariablesReplacedWithSystemProperties() { 100 return new DocumentEditor((MutableDocument)this.document.withVariablesReplacedWithSystemProperties(), factory); 101 } 102 103 @Override 104 public Document unwrap() { 105 return document; 106 } 107 108 @Override 109 public MutableDocument asMutableDocument() { 110 return document; 111 } 112 113 @Override 114 public Object get( String name ) { 115 return document.get(name); 116 } 117 118 @Override 119 public Boolean getBoolean( String name ) { 120 return document.getBoolean(name); 121 } 122 123 @Override 124 public boolean getBoolean( String name, 125 boolean defaultValue ) { 126 return document.getBoolean(name, defaultValue); 127 } 128 129 public Object put( String name, 130 Object value ) { 131 return doSetValue(name, value); 132 } 133 134 @Override 135 public void putAll( Document object ) { 136 doSetAllValues(object); 137 } 138 139 @Override 140 public void putAll( Map<? extends String, ?> map ) { 141 doSetAllValues(map); 142 } 143 144 @Override 145 public void merge( Document other ) { 146 if (other == this) return; 147 for (Field field : other.fields()) { 148 Document otherDoc = field.getValueAsDocument(); 149 if (!Null.matches(otherDoc)) { 150 // Get the corresponding value in this document ... 151 EditableDocument thisField = getDocument(field.getName()); 152 if (!Null.matches(thisField)) { 153 // There are docs in both sides, so merge them ... 154 thisField.merge(otherDoc); 155 } else { 156 // There is not a document on this side (perhaps another value), so replace with that other doc ... 157 doSetValue(field.getName(), otherDoc); 158 } 159 } else { 160 // The field is something other than a document, so just set it on this document ... 161 doSetValue(field.getName(), field.getValue()); 162 } 163 } 164 } 165 166 @Override 167 public Object remove( String name ) { 168 return document.remove(name); 169 } 170 171 @Override 172 public Integer getInteger( String name ) { 173 return document.getInteger(name); 174 } 175 176 @Override 177 public int getInteger( String name, 178 int defaultValue ) { 179 return document.getInteger(name, defaultValue); 180 } 181 182 @Override 183 public Long getLong( String name ) { 184 return document.getLong(name); 185 } 186 187 @Override 188 public long getLong( String name, 189 long defaultValue ) { 190 return document.getLong(name, defaultValue); 191 } 192 193 @Override 194 public Double getDouble( String name ) { 195 return document.getDouble(name); 196 } 197 198 @Override 199 public double getDouble( String name, 200 double defaultValue ) { 201 return document.getDouble(name, defaultValue); 202 } 203 204 @Override 205 public Number getNumber( String name ) { 206 return document.getNumber(name); 207 } 208 209 @Override 210 public Number getNumber( String name, 211 Number defaultValue ) { 212 return document.getNumber(name, defaultValue); 213 } 214 215 @Override 216 public Date getDate(String name) { 217 return document.getDate(name); 218 } 219 220 @Override 221 public String getString( String name ) { 222 return document.getString(name); 223 } 224 225 @Override 226 public String getString( String name, 227 String defaultValue ) { 228 return document.getString(name, defaultValue); 229 } 230 231 @Override 232 public EditableArray getArray( String name ) { 233 return editable(document.getArray(name), name); 234 } 235 236 @Override 237 public EditableArray getOrCreateArray( String name ) { 238 List<?> existing = document.getArray(name); 239 return existing != null ? editable(existing, name) : setArray(name); 240 } 241 242 @Override 243 public EditableDocument getDocument( String name ) { 244 return editable(document.getDocument(name), name); 245 } 246 247 @Override 248 public EditableDocument getOrCreateDocument( String name ) { 249 Document existing = document.getDocument(name); 250 return existing != null ? editable(existing, name) : setDocument(name); 251 } 252 253 @Override 254 public boolean isNull( String name ) { 255 return document.isNull(name); 256 } 257 258 @Override 259 public boolean isNullOrMissing( String name ) { 260 return document.isNullOrMissing(name); 261 } 262 263 @Override 264 public MaxKey getMaxKey( String name ) { 265 return document.getMaxKey(name); 266 } 267 268 @Override 269 public MinKey getMinKey( String name ) { 270 return document.getMinKey(name); 271 } 272 273 @Override 274 public Code getCode( String name ) { 275 return document.getCode(name); 276 } 277 278 @Override 279 public CodeWithScope getCodeWithScope( String name ) { 280 return document.getCodeWithScope(name); 281 } 282 283 @Override 284 public ObjectId getObjectId( String name ) { 285 return document.getObjectId(name); 286 } 287 288 @Override 289 public Binary getBinary( String name ) { 290 return document.getBinary(name); 291 } 292 293 @Override 294 public Symbol getSymbol( String name ) { 295 return document.getSymbol(name); 296 } 297 298 @Override 299 public Pattern getPattern( String name ) { 300 return document.getPattern(name); 301 } 302 303 @Override 304 public UUID getUuid( String name ) { 305 return document.getUuid(name); 306 } 307 308 @Override 309 public UUID getUuid( String name, 310 UUID defaultValue ) { 311 return document.getUuid(name, defaultValue); 312 } 313 314 @Override 315 public int getType( String name ) { 316 return document.getType(name); 317 } 318 319 @Override 320 public Map<String, ?> toMap() { 321 return document.toMap(); 322 } 323 324 @Override 325 public Iterable<Field> fields() { 326 return document.fields(); 327 } 328 329 @Override 330 public boolean containsField( String name ) { 331 return document.containsField(name); 332 } 333 334 @Override 335 public boolean containsAll( Document document ) { 336 return this.document.containsAll(document); 337 } 338 339 @Override 340 public Set<String> keySet() { 341 return document.keySet(); 342 } 343 344 @Override 345 public int size() { 346 return document.size(); 347 } 348 349 @Override 350 public boolean isEmpty() { 351 return document.isEmpty(); 352 } 353 354 @Override 355 public void removeAll() { 356 document.removeAll(); 357 } 358 359 @Override 360 public EditableDocument set( String name, 361 Object value ) { 362 doSetValue(name, value); 363 return this; 364 } 365 366 @Override 367 public EditableDocument setBoolean( String name, 368 boolean value ) { 369 doSetValue(name, factory.createBoolean(value)); 370 return this; 371 } 372 373 @Override 374 public EditableDocument setNumber( String name, 375 int value ) { 376 doSetValue(name, factory.createInt(value)); 377 return this; 378 } 379 380 @Override 381 public EditableDocument setNumber( String name, 382 long value ) { 383 doSetValue(name, factory.createLong(value)); 384 return this; 385 } 386 387 @Override 388 public EditableDocument setNumber( String name, 389 float value ) { 390 doSetValue(name, factory.createDouble(value)); 391 return this; 392 } 393 394 @Override 395 public EditableDocument setNumber( String name, 396 double value ) { 397 doSetValue(name, factory.createDouble(value)); 398 return this; 399 } 400 401 @Override 402 public EditableDocument setString( String name, 403 String value ) { 404 doSetValue(name, factory.createString(value)); 405 return this; 406 } 407 408 @Override 409 public EditableDocument setSymbol( String name, 410 String value ) { 411 doSetValue(name, factory.createSymbol(value)); 412 return this; 413 } 414 415 @Override 416 public EditableDocument setDocument( String name ) { 417 BasicDocument doc = new BasicDocument(); 418 doSetValueIfAbsent(name, doc); 419 return editable(doc, name); 420 } 421 422 @Override 423 public EditableDocument setDocument( String name, 424 Document document ) { 425 if (document instanceof DocumentEditor) document = ((DocumentEditor)document).asMutableDocument(); 426 doSetValue(name, document); 427 return editable(document, name); 428 } 429 430 @Override 431 public EditableArray setArray( String name ) { 432 List<?> array = new BasicArray(); 433 doSetValueIfAbsent(name, array); 434 return editable(array, name); 435 } 436 437 @Override 438 public EditableArray setArray( String name, 439 Array array ) { 440 if (array instanceof ArrayEditor) array = ((ArrayEditor)array).unwrap(); 441 doSetValue(name, array); 442 return editable((List<?>)array, name); 443 } 444 445 @Override 446 public EditableArray setArray( String name, 447 Object... values ) { 448 List<?> array = new BasicArray(values); 449 doSetValue(name, array); 450 return editable(array, name); 451 } 452 453 @Override 454 public EditableDocument setDate( String name, 455 Date value ) { 456 doSetValue(name, value); 457 return this; 458 } 459 460 @Override 461 public EditableDocument setDate( String name, 462 String isoDate ) throws ParseException { 463 doSetValue(name, factory.createDate(isoDate)); 464 return this; 465 } 466 467 @Override 468 public EditableDocument setTimestamp( String name, 469 int timeInSeconds, 470 int increment ) { 471 doSetValue(name, factory.createTimestamp(timeInSeconds, increment)); 472 return this; 473 } 474 475 @Override 476 public EditableDocument setObjectId( String name, 477 String hex ) { 478 doSetValue(name, factory.createObjectId(hex)); 479 return this; 480 } 481 482 @Override 483 public EditableDocument setObjectId( String name, 484 byte[] bytes ) { 485 doSetValue(name, factory.createObjectId(bytes)); 486 return this; 487 } 488 489 @Override 490 public EditableDocument setObjectId( String name, 491 int time, 492 int machine, 493 int process, 494 int inc ) { 495 doSetValue(name, factory.createObjectId(time, machine, process, inc)); 496 return this; 497 } 498 499 @Override 500 public EditableDocument setRegularExpression( String name, 501 String pattern ) { 502 doSetValue(name, factory.createRegex(pattern, null)); 503 return this; 504 } 505 506 @Override 507 public EditableDocument setRegularExpression( String name, 508 String pattern, 509 int flags ) { 510 doSetValue(name, factory.createRegex(pattern, BsonUtils.regexFlagsFor(flags))); 511 return this; 512 } 513 514 @Override 515 public EditableDocument setNull( String name ) { 516 doSetValue(name, factory.createNull()); 517 return this; 518 } 519 520 @Override 521 public EditableDocument setBinary( String name, 522 byte type, 523 byte[] data ) { 524 doSetValue(name, factory.createBinary(type, data)); 525 return this; 526 } 527 528 @Override 529 public EditableDocument setUuid( String name, 530 UUID uuid ) { 531 doSetValue(name, uuid); 532 return this; 533 } 534 535 @Override 536 public EditableDocument setCode( String name, 537 String code, 538 boolean includeScope ) { 539 if (includeScope) { 540 BasicDocument scope = new BasicDocument(); 541 doSetValue(name, factory.createCode(code, scope)); 542 return editable(scope, name); 543 } 544 doSetValue(name, factory.createCode(code)); 545 return this; 546 } 547 548 @Override 549 public EditableDocument setCode( String name, 550 String code, 551 Document scope ) { 552 if (scope != null) { 553 doSetValue(name, factory.createCode(code, scope)); 554 return editable(scope, name); 555 } 556 doSetValue(name, factory.createCode(code)); 557 return this; 558 } 559 560 /** 561 * The method that does the actual setting for all of the <code>set...</code> methods. This method may be overridden by 562 * subclasses when additional work needs to be performed during the set operations. 563 * 564 * @param name the name of the field being set 565 * @param value the new value 566 * @return the old value, or null if there was no existing value 567 */ 568 protected Object doSetValue( String name, 569 Object value ) { 570 if (value == null) { 571 value = Null.getInstance(); 572 } else { 573 value = Utility.unwrap(value); 574 } 575 return document.put(name, value); 576 } 577 578 /** 579 * The method that does the actual setting for all of the <code>set...</code> methods. This method may be overridden by 580 * subclasses when additional work needs to be performed during the set operations. 581 * 582 * @param name the name of the field being set 583 * @param value the new value 584 * @return the old value, or null if there was no existing value 585 */ 586 protected Object doSetValueIfAbsent( String name, 587 Object value ) { 588 if (value == null) { 589 value = Null.getInstance(); 590 } else { 591 value = Utility.unwrap(value); 592 } 593 return document.put(name, value); 594 } 595 596 /** 597 * The method that does the actual setting for all of the {@link #putAll(Document)} method. This method may be overridden by 598 * subclasses when additional work needs to be performed during this operation. 599 * 600 * @param values the document containing the fields to be added 601 */ 602 protected void doSetAllValues( Document values ) { 603 if (values != null) { 604 values = Utility.unwrap(values); 605 document.putAll(values); 606 } 607 } 608 609 /** 610 * The method that does the actual setting for all of the {@link #putAll(Map)} method. This method may be overridden by 611 * subclasses when additional work needs to be performed during this operation. 612 * 613 * @param values the map containing the fields to be added 614 */ 615 protected void doSetAllValues( Map<? extends String, ?> values ) { 616 if (values != null) { 617 document.putAll(Utility.unwrapValues(values)); 618 } 619 } 620 621 protected EditableDocument editable( Document doc, 622 String fieldName ) { 623 if (doc == null) return null; 624 assert !(doc instanceof DocumentEditor) : "The document value should not be a DocumentEditor instance"; 625 if (doc instanceof MutableArray) { 626 return createEditableArray((MutableArray)doc, fieldName, factory); 627 } 628 assert doc instanceof MutableDocument; 629 return createEditableDocument((MutableDocument)doc, fieldName, factory); 630 } 631 632 protected EditableArray editable( List<?> array, 633 String fieldName ) { 634 if (array == null) return null; 635 assert !(array instanceof ArrayEditor) : "The array value should not be an ArrayEditor instance"; 636 return createEditableArray((BasicArray)array, fieldName, factory); 637 } 638 639 protected EditableDocument createEditableDocument( MutableDocument document, 640 String fieldName, 641 DocumentValueFactory factory ) { 642 return new DocumentEditor(document, factory); 643 } 644 645 protected EditableArray createEditableArray( MutableArray array, 646 String fieldName, 647 DocumentValueFactory factory ) { 648 return new ArrayEditor(array, factory); 649 } 650 651 @Override 652 public String toString() { 653 return document.toString(); 654 } 655 656 @Override 657 public boolean equals(Object o) { 658 if (this == o) { 659 return true; 660 } 661 if (!(o instanceof MutableDocument) && ! (o instanceof DocumentEditor)) { 662 return false; 663 } 664 665 if (o instanceof MutableDocument) { 666 return Objects.equals(document, o); 667 } else { 668 DocumentEditor that = (DocumentEditor) o; 669 return Objects.equals(this.document, that.document); 670 } 671 } 672 673 @Override 674 public int hashCode() { 675 return Objects.hash(document); 676 } 677}