1 19 20 package org.apache.cayenne; 21 22 import java.io.IOException ; 23 import java.io.ObjectInputStream ; 24 import java.io.ObjectOutputStream ; 25 import java.lang.reflect.Array ; 26 import java.util.Collection ; 27 import java.util.HashMap ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.StringTokenizer ; 32 33 import org.apache.cayenne.access.DataContext; 34 import org.apache.cayenne.conf.Configuration; 35 import org.apache.cayenne.map.DbAttribute; 36 import org.apache.cayenne.map.DbJoin; 37 import org.apache.cayenne.map.DbRelationship; 38 import org.apache.cayenne.map.EntityResolver; 39 import org.apache.cayenne.map.ObjAttribute; 40 import org.apache.cayenne.map.ObjEntity; 41 import org.apache.cayenne.map.ObjRelationship; 42 import org.apache.cayenne.reflect.PropertyUtils; 43 import org.apache.cayenne.validation.BeanValidationFailure; 44 import org.apache.cayenne.validation.ValidationFailure; 45 import org.apache.cayenne.validation.ValidationResult; 46 import org.apache.cayenne.xml.XMLDecoder; 47 import org.apache.cayenne.xml.XMLEncoder; 48 import org.apache.cayenne.xml.XMLSerializable; 49 50 56 public class CayenneDataObject implements DataObject, Validating, XMLSerializable { 57 58 protected long snapshotVersion = DEFAULT_VERSION; 59 60 protected ObjectId objectId; 61 protected transient int persistenceState = PersistenceState.TRANSIENT; 62 protected transient ObjectContext objectContext; 63 protected Map values = new HashMap (); 64 65 72 public DataContext getDataContext() { 73 if (objectContext == null || objectContext instanceof DataContext) { 74 return (DataContext) objectContext; 75 } 76 77 throw new CayenneRuntimeException("ObjectContext is not a DataContext: " 78 + objectContext); 79 } 80 81 86 public void setDataContext(DataContext dataContext) { 87 this.objectContext = dataContext; 88 89 if (dataContext == null) { 90 this.persistenceState = PersistenceState.TRANSIENT; 91 } 92 } 93 94 100 public ObjEntity getObjEntity() { 101 return (getObjectContext() != null) ? getObjectContext() 102 .getEntityResolver() 103 .lookupObjEntity(this) : null; 104 } 105 106 public ObjectId getObjectId() { 107 return objectId; 108 } 109 110 public void setObjectId(ObjectId objectId) { 111 this.objectId = objectId; 112 } 113 114 public int getPersistenceState() { 115 return persistenceState; 116 } 117 118 public void setPersistenceState(int persistenceState) { 119 this.persistenceState = persistenceState; 120 121 if (persistenceState == PersistenceState.HOLLOW) { 122 values.clear(); 123 } 124 } 125 126 public Object readNestedProperty(String path) { 127 Object object = null; 128 CayenneDataObject dataObject = this; 129 String [] tokenized = tokenizePath(path); 130 int length = tokenized.length; 131 132 int pathIndex = 0; 133 134 for (int i = 0; i < length; i++) { 135 pathIndex += tokenized[i].length() + 1; 136 137 object = dataObject.readSimpleProperty(tokenized[i]); 138 139 if (object == null) { 140 return null; 141 } 142 else if (object instanceof CayenneDataObject) { 143 dataObject = (CayenneDataObject) object; 144 } 145 else if (i + 1 < length) { 146 return PropertyUtils.getProperty(object, path.substring(pathIndex)); 148 } 149 } 150 151 return object; 152 } 153 154 private static final String [] tokenizePath(String path) { 155 if (path == null) { 156 throw new NullPointerException ("Null property path."); 157 } 158 159 if (path.length() == 0) { 160 throw new IllegalArgumentException ("Empty property path."); 161 } 162 163 if (path.indexOf(".") < 0) { 165 return new String [] { 166 path 167 }; 168 } 169 170 StringTokenizer tokens = new StringTokenizer (path, "."); 171 int length = tokens.countTokens(); 172 String [] tokenized = new String [length]; 173 for (int i = 0; i < length; i++) { 174 tokenized[i] = tokens.nextToken(); 175 } 176 177 return tokenized; 178 } 179 180 private final Object readSimpleProperty(String property) { 181 Object object = readProperty(property); 183 184 if (object == null && !values.containsKey(property)) { 187 object = PropertyUtils.getProperty(this, property); 188 } 189 190 return object; 191 } 192 193 public Object readProperty(String propertyName) { 194 if (objectContext != null) { 195 objectContext.prepareForAccess(this, propertyName, false); 199 } 200 201 Object object = readPropertyDirectly(propertyName); 202 203 if (object instanceof Fault) { 204 object = ((Fault) object).resolveFault(this, propertyName); 205 writePropertyDirectly(propertyName, object); 206 } 207 208 return object; 209 } 210 211 public Object readPropertyDirectly(String propName) { 212 return values.get(propName); 213 } 214 215 public void writeProperty(String propName, Object val) { 216 if (objectContext != null) { 217 objectContext.prepareForAccess(this, propName, false); 219 220 Object oldValue = readPropertyDirectly(propName); 223 objectContext.propertyChanged(this, propName, oldValue, val); 224 } 225 226 writePropertyDirectly(propName, val); 227 } 228 229 public void writePropertyDirectly(String propName, Object val) { 230 values.put(propName, val); 231 } 232 233 public void removeToManyTarget(String relName, DataObject value, boolean setReverse) { 234 235 List relList = (List ) readProperty(relName); 238 239 getObjectContext().propertyChanged(this, relName, value, null); 242 243 relList.remove(value); 244 245 if (value != null && setReverse) { 246 unsetReverseRelationship(relName, value); 247 } 248 } 249 250 public void addToManyTarget(String relName, DataObject value, boolean setReverse) { 251 if (value == null) { 252 throw new NullPointerException ("Attempt to add null target DataObject."); 253 } 254 255 willConnect(relName, value); 256 257 List list = (List ) readProperty(relName); 260 261 getObjectContext().propertyChanged(this, relName, null, value); 264 265 list.add(value); 266 267 if (value != null && setReverse) { 268 setReverseRelationship(relName, value); 269 } 270 } 271 272 public void setToOneTarget( 273 String relationshipName, 274 DataObject value, 275 boolean setReverse) { 276 277 willConnect(relationshipName, value); 278 279 Object oldTarget = readProperty(relationshipName); 280 if (oldTarget == value) { 281 return; 282 } 283 284 getObjectContext().propertyChanged(this, relationshipName, oldTarget, value); 285 286 if (setReverse) { 287 if (oldTarget instanceof DataObject) { 289 unsetReverseRelationship(relationshipName, (DataObject) oldTarget); 290 } 291 292 if (value != null) { 294 setReverseRelationship(relationshipName, value); 295 } 296 } 297 298 objectContext.prepareForAccess(this, relationshipName, false); 299 writePropertyDirectly(relationshipName, value); 300 } 301 302 310 protected void willConnect(String relationshipName, Persistent object) { 311 if (object == null || this.getObjectContext() == object.getObjectContext()) { 314 return; 315 } 316 else if (this.getObjectContext() == null && object.getObjectContext() != null) { 317 object.getObjectContext().registerNewObject(this); 318 } 319 else if (this.getObjectContext() != null && object.getObjectContext() == null) { 320 this.getObjectContext().registerNewObject(object); 321 } 322 else { 323 throw new CayenneRuntimeException( 324 "Cannot set object as destination of relationship " 325 + relationshipName 326 + " because it is in a different ObjectContext"); 327 } 328 } 329 330 335 protected void setReverseRelationship(String relName, DataObject val) { 336 ObjRelationship rel = (ObjRelationship) objectContext 337 .getEntityResolver() 338 .getObjEntity(objectId.getEntityName()) 339 .getRelationship(relName); 340 ObjRelationship revRel = rel.getReverseRelationship(); 341 if (revRel != null) { 342 if (revRel.isToMany()) 343 val.addToManyTarget(revRel.getName(), this, false); 344 else 345 val.setToOneTarget(revRel.getName(), this, false); 346 } 347 } 348 349 353 protected void unsetReverseRelationship(String relName, DataObject val) { 354 355 EntityResolver resolver = objectContext.getEntityResolver(); 356 ObjEntity entity = resolver.getObjEntity(objectId.getEntityName()); 357 358 if (entity == null) { 359 throw new IllegalStateException ("DataObject's entity is unmapped, objectId: " 360 + objectId); 361 } 362 363 ObjRelationship rel = (ObjRelationship) entity.getRelationship(relName); 364 ObjRelationship revRel = rel.getReverseRelationship(); 365 if (revRel != null) { 366 if (revRel.isToMany()) 367 val.removeToManyTarget(revRel.getName(), this, false); 368 else 369 val.setToOneTarget(revRel.getName(), null, false); 370 } 371 } 372 373 377 public StringBuffer toStringBuffer(StringBuffer buffer, boolean fullDesc) { 378 String id = (objectId != null) ? objectId.toString() : "<no id>"; 379 String state = PersistenceState.persistenceStateName(persistenceState); 380 381 buffer.append('{').append(id).append("; ").append(state).append("; "); 382 383 if (fullDesc) { 384 appendProperties(buffer); 385 } 386 387 buffer.append("}"); 388 return buffer; 389 } 390 391 protected void appendProperties(StringBuffer buffer) { 392 buffer.append("["); 393 Iterator it = values.entrySet().iterator(); 394 395 while (it.hasNext()) { 396 Map.Entry entry = (Map.Entry ) it.next(); 397 398 buffer.append(entry.getKey()).append("=>"); 399 Object value = entry.getValue(); 400 401 if (value instanceof Persistent) { 402 buffer.append('{').append(((Persistent) value).getObjectId()).append('}'); 403 } 404 else if (value instanceof Collection ) { 405 buffer.append("(..)"); 406 } 407 else if (value instanceof Fault) { 408 buffer.append('?'); 409 } 410 else { 411 buffer.append(value); 412 } 413 414 if (it.hasNext()) { 415 buffer.append("; "); 416 } 417 } 418 419 buffer.append("]"); 420 } 421 422 public String toString() { 423 return toStringBuffer(new StringBuffer (), true).toString(); 424 } 425 426 432 public void fetchFinished() { 433 } 434 435 private void writeObject(ObjectOutputStream out) throws IOException { 436 out.writeInt(persistenceState); 437 438 switch (persistenceState) { 439 case PersistenceState.TRANSIENT: 442 case PersistenceState.NEW: 443 case PersistenceState.MODIFIED: 444 case PersistenceState.DELETED: 445 out.writeObject(values); 446 break; 447 } 448 449 out.writeObject(objectId); 450 } 451 452 private void readObject(ObjectInputStream in) throws IOException , 453 ClassNotFoundException { 454 this.persistenceState = in.readInt(); 455 456 switch (persistenceState) { 457 case PersistenceState.TRANSIENT: 458 case PersistenceState.NEW: 459 case PersistenceState.MODIFIED: 460 case PersistenceState.DELETED: 461 values = (Map ) in.readObject(); 462 break; 463 case PersistenceState.COMMITTED: 464 case PersistenceState.HOLLOW: 465 this.persistenceState = PersistenceState.HOLLOW; 466 values = new HashMap (); 468 break; 469 } 470 471 this.objectId = (ObjectId) in.readObject(); 472 473 } 477 478 483 public long getSnapshotVersion() { 484 return snapshotVersion; 485 } 486 487 490 public void setSnapshotVersion(long snapshotVersion) { 491 this.snapshotVersion = snapshotVersion; 492 } 493 494 503 protected void validateForSave(ValidationResult validationResult) { 504 505 ObjEntity objEntity = getObjectContext() 506 .getEntityResolver() 507 .lookupObjEntity(this); 508 if (objEntity == null) { 509 throw new CayenneRuntimeException( 510 "No ObjEntity mapping found for DataObject " + getClass().getName()); 511 } 512 513 515 Map failedDbAttributes = null; 521 522 Iterator attributes = objEntity.getAttributes().iterator(); 523 while (attributes.hasNext()) { 524 Object next = attributes.next(); 525 526 if (!(next instanceof ObjAttribute)) { 528 continue; 529 } 530 531 ObjAttribute objAttribute = (ObjAttribute) next; 532 DbAttribute dbAttribute = objAttribute.getDbAttribute(); 533 534 if (dbAttribute.isPrimaryKey()) { 536 continue; 537 } 538 539 Object value = this.readPropertyDirectly(objAttribute.getName()); 540 if (dbAttribute.isMandatory()) { 541 ValidationFailure failure = BeanValidationFailure.validateNotNull( 542 this, 543 objAttribute.getName(), 544 value); 545 546 if (failure != null) { 547 548 if (failedDbAttributes == null) { 549 failedDbAttributes = new HashMap (); 550 } 551 552 failedDbAttributes.put(dbAttribute.getName(), failure); 553 continue; 554 } 555 } 556 557 if (value != null && dbAttribute.getMaxLength() > 0) { 559 560 if (value.getClass().isArray()) { 561 int len = Array.getLength(value); 562 if (len > dbAttribute.getMaxLength()) { 563 String message = "\"" 564 + objAttribute.getName() 565 + "\" exceeds maximum allowed length (" 566 + dbAttribute.getMaxLength() 567 + " bytes): " 568 + len; 569 validationResult.addFailure(new BeanValidationFailure( 570 this, 571 objAttribute.getName(), 572 message)); 573 } 574 } 575 else if (value instanceof CharSequence ) { 576 int len = ((CharSequence ) value).length(); 577 if (len > dbAttribute.getMaxLength()) { 578 String message = "\"" 579 + objAttribute.getName() 580 + "\" exceeds maximum allowed length (" 581 + dbAttribute.getMaxLength() 582 + " chars): " 583 + len; 584 validationResult.addFailure(new BeanValidationFailure( 585 this, 586 objAttribute.getName(), 587 message)); 588 } 589 } 590 } 591 } 592 593 Iterator relationships = objEntity.getRelationships().iterator(); 595 while (relationships.hasNext()) { 596 ObjRelationship relationship = (ObjRelationship) relationships.next(); 597 598 if (relationship.isSourceIndependentFromTargetChange()) { 599 continue; 600 } 601 602 List dbRels = relationship.getDbRelationships(); 603 if (dbRels.isEmpty()) { 604 continue; 606 } 607 608 boolean validate = true; 611 DbRelationship dbRelationship = (DbRelationship) dbRels.get(0); 612 Iterator joins = dbRelationship.getJoins().iterator(); 613 while (joins.hasNext()) { 614 DbJoin join = (DbJoin) joins.next(); 615 DbAttribute source = join.getSource(); 616 617 if (source.isMandatory()) { 618 if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) { 620 failedDbAttributes.remove(source.getName()); 621 622 624 if (!failedDbAttributes.isEmpty()) { 626 continue; 627 } 628 } 629 } 630 else { 631 validate = false; 634 } 635 } 636 637 if (validate) { 638 Object value = this.readPropertyDirectly(relationship.getName()); 639 ValidationFailure failure = BeanValidationFailure.validateNotNull( 640 this, 641 relationship.getName(), 642 value); 643 644 if (failure != null) { 645 validationResult.addFailure(failure); 646 continue; 647 } 648 } 649 650 } 651 652 if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) { 654 Iterator failedAttributes = failedDbAttributes.values().iterator(); 655 while (failedAttributes.hasNext()) { 656 validationResult.addFailure((ValidationFailure) failedAttributes.next()); 657 } 658 } 659 } 660 661 668 public void validateForInsert(ValidationResult validationResult) { 669 validateForSave(validationResult); 670 } 671 672 679 public void validateForUpdate(ValidationResult validationResult) { 680 validateForSave(validationResult); 681 } 682 683 690 public void validateForDelete(ValidationResult validationResult) { 691 } 693 694 699 public void encodeAsXML(XMLEncoder encoder) { 700 EntityResolver er = getObjectContext().getEntityResolver(); 701 ObjEntity object = er.lookupObjEntity(getClass()); 702 703 String [] fields = this.getClass().getName().split("\\."); 704 encoder.setRoot(fields[fields.length - 1], this.getClass().getName()); 705 706 for (Iterator it = object.getDeclaredAttributes().iterator(); it.hasNext();) { 707 ObjAttribute att = (ObjAttribute) it.next(); 708 String name = att.getName(); 709 encoder.encodeProperty(name, readNestedProperty(name)); 710 } 711 } 712 713 public void decodeFromXML(XMLDecoder decoder) { 714 715 EntityResolver resolver = Configuration 720 .getSharedConfiguration() 721 .getDomain() 722 .getEntityResolver(); 723 ObjEntity objectEntity = resolver.lookupObjEntity(getClass()); 724 725 for (Iterator it = objectEntity.getDeclaredAttributes().iterator(); it.hasNext();) { 726 ObjAttribute att = (ObjAttribute) it.next(); 727 String name = att.getName(); 728 writeProperty(name, decoder.decodeObject(name)); 729 } 730 } 731 732 737 public ObjectContext getObjectContext() { 738 return objectContext; 739 } 740 741 744 public void setObjectContext(ObjectContext objectContext) { 745 this.objectContext = objectContext; 746 747 if (objectContext == null) { 748 this.persistenceState = PersistenceState.TRANSIENT; 749 } 750 } 751 } 752 | Popular Tags |