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.util.Collections; 019import java.util.Date; 020import java.util.Iterator; 021import java.util.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Properties; 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.Bson; 030import org.modeshape.schematic.document.Code; 031import org.modeshape.schematic.document.CodeWithScope; 032import org.modeshape.schematic.document.Document; 033import org.modeshape.schematic.document.Json; 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; 039import org.modeshape.schematic.internal.schema.DocumentTransformer.PropertiesTransformer; 040import org.modeshape.schematic.internal.schema.DocumentTransformer.SystemPropertiesTransformer; 041 042public class BasicDocument extends LinkedHashMap<String, Object> implements MutableDocument { 043 044 private static final long serialVersionUID = 1L; 045 046 private transient Map<String, Object> unmodifiableView; 047 048 public BasicDocument() { 049 super(); 050 } 051 052 public BasicDocument( Document document ) { 053 putAll(document); 054 } 055 056 public BasicDocument( int initialCapacity ) { 057 super(initialCapacity); 058 } 059 060 public BasicDocument( String name, 061 Object value ) { 062 super(); 063 if (name != null) put(name, value); 064 } 065 066 public BasicDocument( String name1, 067 Object value1, 068 String name2, 069 Object value2 ) { 070 super(); 071 if (name1 != null) put(name1, value1); 072 if (name2 != null) put(name2, value2); 073 } 074 075 public BasicDocument( String name1, 076 Object value1, 077 String name2, 078 Object value2, 079 String name3, 080 Object value3 ) { 081 super(); 082 if (name1 != null) put(name1, value1); 083 if (name2 != null) put(name2, value2); 084 if (name3 != null) put(name3, value3); 085 } 086 087 public BasicDocument( String name1, 088 Object value1, 089 String name2, 090 Object value2, 091 String name3, 092 Object value3, 093 String name4, 094 Object value4 ) { 095 super(); 096 if (name1 != null) put(name1, value1); 097 if (name2 != null) put(name2, value2); 098 if (name3 != null) put(name3, value3); 099 if (name4 != null) put(name4, value4); 100 } 101 102 @Override 103 public boolean containsField( String name ) { 104 return containsKey(name); 105 } 106 107 @Override 108 public boolean containsAll( Document document ) { 109 if (document == null) { 110 return true; 111 } 112 for (Field field : document.fields()) { 113 Object thisValue = this.get(field.getName()); 114 Object thatValue = field.getValue(); 115 if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) { 116 return false; 117 } 118 } 119 return true; 120 } 121 122 @Override 123 public Object get( String name ) { 124 return super.get(name); // calls Map.get(Object) 125 } 126 127 @Override 128 public Map<String, ?> toMap() { 129 if (unmodifiableView == null) unmodifiableView = Collections.unmodifiableMap(this); 130 return unmodifiableView; 131 } 132 133 @Override 134 public Iterable<Field> fields() { 135 return () -> { 136 final Iterator<Map.Entry<String, Object>> iter = BasicDocument.this.entrySet().iterator(); 137 return new Iterator<Field>() { 138 @Override 139 public boolean hasNext() { 140 return iter.hasNext(); 141 } 142 143 @Override 144 public Field next() { 145 Map.Entry<String, Object> entry = iter.next(); 146 return new ImmutableField(entry.getKey(), entry.getValue()); 147 } 148 149 @Override 150 public void remove() { 151 throw new UnsupportedOperationException(); 152 } 153 }; 154 }; 155 } 156 157 @Override 158 public Boolean getBoolean( String name ) { 159 Object value = get(name); 160 return (value instanceof Boolean) ? (Boolean)value : null; 161 } 162 163 @Override 164 public boolean getBoolean( String name, 165 boolean defaultValue ) { 166 Object value = get(name); 167 return (value instanceof Boolean) ? ((Boolean)value).booleanValue() : defaultValue; 168 } 169 170 @Override 171 public Integer getInteger( String name ) { 172 Object value = get(name); 173 return (value instanceof Integer) ? (Integer)value : null; 174 } 175 176 @Override 177 public int getInteger( String name, 178 int defaultValue ) { 179 Object value = get(name); 180 return (value instanceof Integer) ? ((Integer)value).intValue() : defaultValue; 181 } 182 183 @Override 184 public Long getLong( String name ) { 185 Object value = get(name); 186 if (value instanceof Long) return (Long)value; 187 if (value instanceof Integer) return new Long(((Integer)value).longValue()); 188 return null; 189 } 190 191 @Override 192 public long getLong( String name, 193 long defaultValue ) { 194 Object value = get(name); 195 if (value instanceof Long) return ((Long)value).longValue(); 196 if (value instanceof Integer) return ((Integer)value).longValue(); 197 return defaultValue; 198 } 199 200 @Override 201 public Double getDouble( String name ) { 202 Object value = get(name); 203 return (value instanceof Double) ? (Double)value : null; 204 } 205 206 @Override 207 public double getDouble( String name, 208 double defaultValue ) { 209 Object value = get(name); 210 return (value instanceof Double) ? ((Double)value).doubleValue() : defaultValue; 211 } 212 213 @Override 214 public Number getNumber( String name ) { 215 Object value = get(name); 216 return (value instanceof Number) ? (Number)value : null; 217 } 218 219 @Override 220 public Number getNumber( String name, 221 Number defaultValue ) { 222 Object value = get(name); 223 return (value instanceof Number) ? (Number)value : defaultValue; 224 } 225 226 @Override 227 public Date getDate(String name) { 228 Object value = get(name); 229 return (value instanceof Date) ? (Date)value : null; 230 } 231 232 @Override 233 public String getString( String name ) { 234 return getString(name, null); 235 } 236 237 @Override 238 public String getString( String name, 239 String defaultValue ) { 240 Object value = get(name); 241 if (value != null) { 242 if (value instanceof String) { 243 return (String)value; 244 } 245 if (value instanceof Symbol) { 246 return ((Symbol)value).getSymbol(); 247 } 248 } 249 return defaultValue; 250 } 251 252 @Override 253 public List<?> getArray( String name ) { 254 Object value = get(name); 255 return (value instanceof List) ? (List<?>)value : null; 256 } 257 258 @Override 259 public Document getDocument( String name ) { 260 Object value = get(name); 261 return (value instanceof Document) ? (Document)value : null; 262 } 263 264 @Override 265 public boolean isNull( String name ) { 266 return get(name) instanceof Null; 267 } 268 269 @Override 270 public boolean isNullOrMissing( String name ) { 271 return Null.matches(get(name)); 272 } 273 274 @Override 275 public MaxKey getMaxKey( String name ) { 276 Object value = get(name); 277 return (value instanceof MaxKey) ? (MaxKey)value : null; 278 } 279 280 @Override 281 public MinKey getMinKey( String name ) { 282 Object value = get(name); 283 return (value instanceof MinKey) ? (MinKey)value : null; 284 } 285 286 @Override 287 public Code getCode( String name ) { 288 Object value = get(name); 289 return (value instanceof Code) ? (Code)value : null; 290 } 291 292 @Override 293 public CodeWithScope getCodeWithScope( String name ) { 294 Object value = get(name); 295 return (value instanceof CodeWithScope) ? (CodeWithScope)value : null; 296 } 297 298 @Override 299 public ObjectId getObjectId( String name ) { 300 Object value = get(name); 301 return (value instanceof ObjectId) ? (ObjectId)value : null; 302 } 303 304 @Override 305 public Binary getBinary( String name ) { 306 Object value = get(name); 307 return (value instanceof Binary) ? (Binary)value : null; 308 } 309 310 @Override 311 public Symbol getSymbol( String name ) { 312 Object value = get(name); 313 if (value != null) { 314 if (value instanceof Symbol) { 315 return (Symbol)value; 316 } 317 if (value instanceof String) { 318 return new Symbol((String)value); 319 } 320 } 321 return null; 322 } 323 324 @Override 325 public Pattern getPattern( String name ) { 326 Object value = get(name); 327 return (value instanceof Pattern) ? (Pattern)value : null; 328 } 329 330 @Override 331 public UUID getUuid( String name ) { 332 return getUuid(name, null); 333 } 334 335 @Override 336 public UUID getUuid( String name, 337 UUID defaultValue ) { 338 Object value = get(name); 339 if (value != null) { 340 if (value instanceof UUID) { 341 return (UUID)value; 342 } 343 if (value instanceof String) { 344 try { 345 return UUID.fromString((String)value); 346 } catch (IllegalArgumentException e) { 347 // do nothing ... 348 } 349 } 350 } 351 return defaultValue; 352 } 353 354 @Override 355 public int getType( String name ) { 356 return Bson.getTypeForValue(get(name)); 357 } 358 359 @Override 360 public void putAll( Document object ) { 361 if (object != this) { 362 // Prevent going through BasicBsonObject.unmodifiableView if we can ... 363 Map<String, ?> original = object instanceof BasicDocument ? (BasicDocument)object : object.toMap(); 364 for (Map.Entry<String, ?> entry : original.entrySet()) { 365 put(entry.getKey(), unwrap(entry.getValue())); 366 } 367 } 368 } 369 370 @Override 371 public Object remove( String name ) { 372 return super.remove(name); // calls Map.remove(Object) 373 } 374 375 @Override 376 public void removeAll() { 377 super.clear(); 378 } 379 380 @Override 381 public int hashCode() { 382 return super.hashCode(); 383 } 384 385 @Override 386 public boolean equals( Object obj ) { 387 if (this == obj) { 388 return true; 389 } 390 if (obj instanceof Iterable) { 391 // Probably an array 392 return false; 393 } 394 if (obj instanceof Document) { 395 Document that = (Document)obj; 396 if (this.size() != that.size()) { 397 return false; 398 } 399 for (Field thisField : fields()) { 400 Object thisValue = thisField.getValue(); 401 Object thatValue = that.get(thisField.getName()); 402 if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) { 403 return false; 404 } 405 } 406 return true; 407 } 408 if (obj instanceof Map) { 409 Map<?, ?> that = (Map<?, ?>)obj; 410 if (this.size() != that.size()) { 411 return false; 412 } 413 if (!this.keySet().equals(that.keySet())) { 414 return false; 415 } 416 for (Field thisField : fields()) { 417 Object thisValue = thisField.getValue(); 418 Object thatValue = that.get(thisField.getName()); 419 if (!BsonUtils.valuesAreEqual(thisValue, thatValue)) { 420 return false; 421 } 422 } 423 return true; 424 } 425 return false; 426 } 427 428 @Override 429 public String toString() { 430 return Json.write(this); 431 } 432 433 @Override 434 public MutableDocument clone() { 435 BasicDocument clone = new BasicDocument(); 436 for (Field field : this.fields()) { 437 Object value = unwrap(field.getValue()); 438 if (value instanceof Array) { 439 value = ((Array)value).clone(); 440 } else if (value instanceof Document) { 441 value = ((Document)value).clone(); 442 }// every other kind of value is immutable 443 clone.put(field.getName(), value); 444 } 445 return clone; 446 } 447 448 @Override 449 public Document with( Map<String, Object> changedFields ) { 450 BasicDocument clone = new BasicDocument(); 451 for (Field field : this.fields()) { 452 String name = field.getName(); 453 Object newValue = unwrap(changedFields.get(name)); 454 if (newValue != null) { 455 clone.put(name, newValue); 456 } else { 457 Object oldValue = field.getValue(); 458 clone.put(name, oldValue); 459 } 460 } 461 return clone; 462 } 463 464 @Override 465 public Document with( String fieldName, 466 Object newValue ) { 467 BasicDocument clone = new BasicDocument(); 468 newValue = unwrap(newValue); 469 for (Field field : this.fields()) { 470 String name = field.getName(); 471 if (name.equals(fieldName)) { 472 clone.put(name, newValue); 473 } else { 474 Object oldValue = field.getValue(); 475 clone.put(name, oldValue); 476 } 477 } 478 return clone; 479 } 480 481 @Override 482 public Document with( ValueTransformer transformer ) { 483 boolean transformed = false; 484 BasicDocument clone = new BasicDocument(); 485 for (Field field : this.fields()) { 486 String name = field.getName(); 487 Object oldValue = field.getValue(); 488 Object newValue = null; 489 if (oldValue instanceof Document) { 490 newValue = ((Document)oldValue).with(transformer); 491 } else { 492 newValue = transformer.transform(name, oldValue); 493 } 494 if (newValue != oldValue) transformed = true; 495 clone.put(name, unwrap(newValue)); 496 } 497 return transformed ? clone : this; 498 } 499 500 @Override 501 public Document withVariablesReplaced( Properties properties ) { 502 return with(new PropertiesTransformer(properties)); 503 } 504 505 @Override 506 public Document withVariablesReplacedWithSystemProperties() { 507 return with(new SystemPropertiesTransformer()); 508 } 509 510 protected Object unwrap( Object value ) { 511 if (value instanceof DocumentEditor) { 512 return unwrap(((DocumentEditor)value).unwrap()); 513 } 514 if (value instanceof ArrayEditor) { 515 return unwrap(((ArrayEditor)value).unwrap()); 516 } 517 return value; 518 } 519 520}