1 56 package org.objectstyle.cayenne; 57 58 import java.io.IOException ; 59 import java.io.ObjectInputStream ; 60 import java.io.ObjectOutputStream ; 61 import java.lang.reflect.InvocationTargetException ; 62 import java.util.HashMap ; 63 import java.util.Iterator ; 64 import java.util.List ; 65 import java.util.Map ; 66 import java.util.StringTokenizer ; 67 68 import org.objectstyle.cayenne.access.DataContext; 69 import org.objectstyle.cayenne.access.DataNode; 70 import org.objectstyle.cayenne.access.types.ExtendedTypeMap; 71 import org.objectstyle.cayenne.conf.Configuration; 72 import org.objectstyle.cayenne.map.DbAttribute; 73 import org.objectstyle.cayenne.map.DbJoin; 74 import org.objectstyle.cayenne.map.DbRelationship; 75 import org.objectstyle.cayenne.map.EntityResolver; 76 import org.objectstyle.cayenne.map.ObjAttribute; 77 import org.objectstyle.cayenne.map.ObjEntity; 78 import org.objectstyle.cayenne.map.ObjRelationship; 79 import org.objectstyle.cayenne.util.PropertyComparator; 80 import org.objectstyle.cayenne.validation.BeanValidationFailure; 81 import org.objectstyle.cayenne.validation.ValidationFailure; 82 import org.objectstyle.cayenne.validation.ValidationResult; 83 import org.objectstyle.cayenne.xml.XMLDecoder; 84 import org.objectstyle.cayenne.xml.XMLEncoder; 85 import org.objectstyle.cayenne.xml.XMLSerializable; 86 87 88 94 public class CayenneDataObject implements DataObject, XMLSerializable { 95 96 protected long snapshotVersion = DEFAULT_VERSION; 97 98 protected ObjectId objectId; 99 protected transient int persistenceState = PersistenceState.TRANSIENT; 100 protected transient DataContext dataContext; 101 protected Map values = new HashMap (); 102 103 108 public DataContext getDataContext() { 109 return dataContext; 110 } 111 112 115 public void setDataContext(DataContext dataContext) { 116 this.dataContext = dataContext; 117 118 if (dataContext == null) { 119 this.persistenceState = PersistenceState.TRANSIENT; 120 } 121 } 122 123 129 public ObjEntity getObjEntity() { 131 return (getDataContext() != null) ? getDataContext() 132 .getEntityResolver() 133 .lookupObjEntity(this) : null; 134 } 135 136 public ObjectId getObjectId() { 137 return objectId; 138 } 139 140 public void setObjectId(ObjectId objectId) { 141 this.objectId = objectId; 142 } 143 144 public int getPersistenceState() { 145 return persistenceState; 146 } 147 148 public void setPersistenceState(int persistenceState) { 149 this.persistenceState = persistenceState; 150 151 if (persistenceState == PersistenceState.HOLLOW) { 152 values.clear(); 153 } 154 } 155 156 public Object readNestedProperty(String path) { 157 Object object = null; 158 CayenneDataObject dataObject = this; 159 String [] tokenized = tokenizePath(path); 160 int length = tokenized.length; 161 162 for (int i = 0; i < length; i++) { 163 164 object = dataObject.readSimpleProperty(tokenized[i]); 165 166 if (object == null) { 167 return null; 168 } 169 else if (object instanceof CayenneDataObject) { 170 dataObject = (CayenneDataObject) object; 171 } 172 else if (i + 1 < length) { 173 throw new CayenneRuntimeException("Invalid path: " + path); 174 } 175 } 176 177 return object; 178 } 179 180 private static final String [] tokenizePath(String path) { 181 if (path == null) { 182 throw new NullPointerException ("Null property path."); 183 } 184 185 if (path.length() == 0) { 186 throw new IllegalArgumentException ("Empty property path."); 187 } 188 189 if (path.indexOf(".") < 0) { 191 return new String [] { 192 path 193 }; 194 } 195 196 StringTokenizer tokens = new StringTokenizer (path, "."); 197 int length = tokens.countTokens(); 198 String [] tokenized = new String [length]; 199 for (int i = 0; i < length; i++) { 200 tokenized[i] = tokens.nextToken(); 201 } 202 203 return tokenized; 204 } 205 206 private final Object readSimpleProperty(String property) { 207 Object object = readProperty(property); 209 210 if (object == null && !values.containsKey(property)) { 213 try { 214 object = PropertyComparator.readProperty(property, this); 215 } 216 catch (IllegalAccessException e) { 217 throw new CayenneRuntimeException("Error reading property '" 218 + property 219 + "'.", e); 220 } 221 catch (InvocationTargetException e) { 222 throw new CayenneRuntimeException("Error reading property '" 223 + property 224 + "'.", e); 225 } 226 catch (NoSuchMethodException e) { 227 } 229 } 230 231 return object; 232 } 233 234 237 public void resolveFault() { 238 if (getPersistenceState() == PersistenceState.HOLLOW && dataContext != null) { 239 dataContext.getObjectStore().resolveHollow(this); 240 if (getPersistenceState() != PersistenceState.COMMITTED) { 241 throw new FaultFailureException( 242 "Error resolving fault, no matching row exists in the database for ObjectId: " 243 + getObjectId()); 244 } 245 } 246 } 247 248 public Object readProperty(String propName) { 249 resolveFault(); 250 251 Object object = readPropertyDirectly(propName); 252 253 if (object instanceof Fault) { 255 object = ((Fault) object).resolveFault(this, propName); 256 writePropertyDirectly(propName, object); 257 } 258 259 return object; 260 } 261 262 public Object readPropertyDirectly(String propName) { 263 return values.get(propName); 264 } 265 266 public void writeProperty(String propName, Object val) { 267 resolveFault(); 268 269 if (persistenceState == PersistenceState.COMMITTED) { 272 persistenceState = PersistenceState.MODIFIED; 273 dataContext.getObjectStore().retainSnapshot(this); 274 } 275 278 writePropertyDirectly(propName, val); 279 } 280 281 public void writePropertyDirectly(String propName, Object val) { 282 values.put(propName, val); 283 } 284 285 public void removeToManyTarget(String relName, DataObject value, boolean setReverse) { 286 287 ObjRelationship relationship = this.getRelationshipNamed(relName); 288 289 if (relationship == null) { 290 throw new NullPointerException ("Can't find relationship: " + relName); 291 } 292 293 getDataContext().getObjectStore().objectRelationshipUnset( 295 this, 296 value, 297 relationship, 298 setReverse); 299 300 List relList = (List ) readProperty(relName); 303 relList.remove(value); 304 if (persistenceState == PersistenceState.COMMITTED) { 305 persistenceState = PersistenceState.MODIFIED; 306 } 307 308 if (value != null && setReverse) { 309 unsetReverseRelationship(relName, value); 310 } 311 } 312 313 public void addToManyTarget(String relName, DataObject value, boolean setReverse) { 314 if (value == null) { 315 throw new NullPointerException ("Attempt to add null target DataObject."); 316 } 317 318 willConnect(relName, value); 319 320 ObjRelationship relationship = this.getRelationshipNamed(relName); 321 if (relationship == null) { 322 throw new NullPointerException ("Can't find relationship: " + relName); 323 } 324 325 getDataContext().getObjectStore().objectRelationshipSet( 326 this, 327 value, 328 relationship, 329 setReverse); 330 331 List list = (List ) readProperty(relName); 334 list.add(value); 335 if (persistenceState == PersistenceState.COMMITTED) { 336 persistenceState = PersistenceState.MODIFIED; 337 338 dataContext.getObjectStore().retainSnapshot(this); 341 } 342 343 if (value != null && setReverse) { 344 setReverseRelationship(relName, value); 345 } 346 } 347 348 public void setToOneTarget( 349 String relationshipName, 350 DataObject value, 351 boolean setReverse) { 352 353 willConnect(relationshipName, value); 354 355 Object oldTarget = readProperty(relationshipName); 356 if (oldTarget == value) { 357 return; 358 } 359 360 ObjRelationship relationship = this 361 .getRelationshipNamed(relationshipName); 362 if (relationship == null) { 363 throw new NullPointerException ("Can't find relationship: " 364 + relationshipName); 365 } 366 367 getDataContext().getObjectStore().objectRelationshipSet(this, value, 370 relationship, setReverse); 371 372 if (setReverse) { 373 if (oldTarget instanceof DataObject) { 375 unsetReverseRelationship(relationshipName, 376 (DataObject) oldTarget); 377 } 378 379 if (value != null) { 381 setReverseRelationship(relationshipName, value); 382 } 383 } 384 385 writeProperty(relationshipName, value); 386 } 387 388 397 protected void willConnect(String relationshipName, DataObject dataObject) { 398 if (dataObject == null 401 || this.getDataContext() == dataObject.getDataContext()) { 402 return; 403 } else if (this.getDataContext() == null 404 && dataObject.getDataContext() != null) { 405 dataObject.getDataContext().registerNewObject(this); 406 } else if (this.getDataContext() != null 407 && dataObject.getDataContext() == null) { 408 this.getDataContext().registerNewObject(dataObject); 409 } else { 410 throw new CayenneRuntimeException( 411 "Cannot set object as destination of relationship " 412 + relationshipName 413 + " because it is in a different DataContext"); 414 } 415 } 416 417 private ObjRelationship getRelationshipNamed(String relName) { 418 return (ObjRelationship) dataContext 419 .getEntityResolver() 420 .lookupObjEntity(this) 421 .getRelationship(relName); 422 } 423 424 429 protected void setReverseRelationship(String relName, DataObject val) { 430 ObjRelationship rel = (ObjRelationship) dataContext 431 .getEntityResolver() 432 .lookupObjEntity(objectId.getObjectClass()) 433 .getRelationship(relName); 434 ObjRelationship revRel = rel.getReverseRelationship(); 435 if (revRel != null) { 436 if (revRel.isToMany()) 437 val.addToManyTarget(revRel.getName(), this, false); 438 else 439 val.setToOneTarget(revRel.getName(), this, false); 440 } 441 } 442 443 447 protected void unsetReverseRelationship(String relName, DataObject val) { 448 Class aClass = objectId.getObjectClass(); 449 EntityResolver resolver = dataContext.getEntityResolver(); 450 ObjEntity entity = resolver.lookupObjEntity(aClass); 451 452 if (entity == null) { 453 String className = (aClass != null) ? aClass.getName() : "<null>"; 454 throw new IllegalStateException ("DataObject's class is unmapped: " 455 + className); 456 } 457 458 ObjRelationship rel = (ObjRelationship) entity.getRelationship(relName); 459 ObjRelationship revRel = rel.getReverseRelationship(); 460 if (revRel != null) { 461 if (revRel.isToMany()) 462 val.removeToManyTarget(revRel.getName(), this, false); 463 else 464 val.setToOneTarget(revRel.getName(), null, false); 465 } 466 } 467 468 472 public StringBuffer toStringBuffer(StringBuffer buf, boolean fullDesc) { 473 buf.append('{'); 475 476 if (fullDesc) 477 appendProperties(buf); 478 479 buf.append("<oid: ").append(objectId).append("; state: ").append( 480 PersistenceState.persistenceStateName(persistenceState)).append(">}\n"); 481 return buf; 482 } 483 484 protected void appendProperties(StringBuffer buf) { 485 buf.append("["); 486 Iterator it = values.keySet().iterator(); 487 while (it.hasNext()) { 488 Object key = it.next(); 489 buf.append('\t').append(key).append(" => "); 490 Object val = values.get(key); 491 492 if (val instanceof CayenneDataObject) { 493 ((CayenneDataObject) val).toStringBuffer(buf, false); 494 } 495 else if (val instanceof List ) { 496 buf.append('(').append(val.getClass().getName()).append(')'); 497 } 498 else 499 buf.append(val); 500 501 buf.append('\n'); 502 } 503 504 buf.append("]"); 505 } 506 507 public String toString() { 508 return toStringBuffer(new StringBuffer (), true).toString(); 509 } 510 511 516 public void fetchFinished() { 517 } 518 519 private void writeObject(ObjectOutputStream out) throws IOException { 520 out.writeInt(persistenceState); 521 522 switch (persistenceState) { 523 case PersistenceState.TRANSIENT: 526 case PersistenceState.NEW: 527 case PersistenceState.MODIFIED: 528 case PersistenceState.DELETED: 529 out.writeObject(values); 530 break; 531 } 532 533 out.writeObject(objectId); 534 } 535 536 private void readObject(ObjectInputStream in) throws IOException , 537 ClassNotFoundException { 538 this.persistenceState = in.readInt(); 539 540 switch (persistenceState) { 541 case PersistenceState.TRANSIENT: 542 case PersistenceState.NEW: 543 case PersistenceState.MODIFIED: 544 case PersistenceState.DELETED: 545 values = (Map ) in.readObject(); 546 break; 547 case PersistenceState.COMMITTED: 548 case PersistenceState.HOLLOW: 549 this.persistenceState = PersistenceState.HOLLOW; 550 values = new HashMap (); 552 break; 553 } 554 555 this.objectId = (ObjectId) in.readObject(); 556 557 } 561 562 567 public long getSnapshotVersion() { 568 return snapshotVersion; 569 } 570 571 574 public void setSnapshotVersion(long snapshotVersion) { 575 this.snapshotVersion = snapshotVersion; 576 } 577 578 587 protected void validateForSave(ValidationResult validationResult) { 588 589 ObjEntity objEntity = getDataContext().getEntityResolver().lookupObjEntity(this); 590 if (objEntity == null) { 591 throw new CayenneRuntimeException( 592 "No ObjEntity mapping found for DataObject " + getClass().getName()); 593 } 594 595 DataNode node = getDataContext().lookupDataNode(objEntity.getDataMap()); 596 if (node == null) { 597 throw new CayenneRuntimeException("No DataNode found for objEntity: " 598 + objEntity.getName()); 599 } 600 601 ExtendedTypeMap types = node.getAdapter().getExtendedTypes(); 602 603 605 Map failedDbAttributes = null; 611 612 Iterator attributes = objEntity.getAttributes().iterator(); 613 while (attributes.hasNext()) { 614 ObjAttribute objAttribute = (ObjAttribute) attributes.next(); 615 DbAttribute dbAttribute = objAttribute.getDbAttribute(); 616 617 Object value = this.readPropertyDirectly(objAttribute.getName()); 618 if (dbAttribute.isMandatory()) { 619 ValidationFailure failure = BeanValidationFailure.validateNotNull( 620 this, 621 objAttribute.getName(), 622 value); 623 624 if (failure != null) { 625 626 if (failedDbAttributes == null) { 627 failedDbAttributes = new HashMap (); 628 } 629 630 failedDbAttributes.put(dbAttribute.getName(), failure); 631 continue; 632 } 633 } 634 635 if (value != null) { 636 637 640 types.getRegisteredType(value.getClass()).validateProperty( 641 this, 642 objAttribute.getName(), 643 value, 644 dbAttribute, 645 validationResult); 646 } 647 } 648 649 Iterator relationships = objEntity.getRelationships().iterator(); 651 while (relationships.hasNext()) { 652 ObjRelationship relationship = (ObjRelationship) relationships.next(); 653 654 if (relationship.isSourceIndependentFromTargetChange()) { 655 continue; 656 } 657 658 List dbRels = relationship.getDbRelationships(); 659 if (dbRels.isEmpty()) { 660 continue; 662 } 663 664 boolean validate = false; 667 DbRelationship dbRelationship = (DbRelationship) dbRels.get(0); 668 Iterator joins = dbRelationship.getJoins().iterator(); 669 while (joins.hasNext()) { 670 DbJoin join = (DbJoin) joins.next(); 671 DbAttribute source = join.getSource(); 672 673 if (source.isMandatory()) { 674 validate = true; 675 676 if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) { 678 failedDbAttributes.remove(source.getName()); 679 680 if (!failedDbAttributes.isEmpty()) { 683 continue; 684 } 685 } 686 687 break; 688 } 689 } 690 691 if (validate) { 692 Object value = this.readPropertyDirectly(relationship.getName()); 693 ValidationFailure failure = BeanValidationFailure.validateNotNull( 694 this, 695 relationship.getName(), 696 value); 697 698 if (failure != null) { 699 validationResult.addFailure(failure); 700 continue; 701 } 702 } 703 704 } 705 706 if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) { 708 Iterator failedAttributes = failedDbAttributes.values().iterator(); 709 while (failedAttributes.hasNext()) { 710 validationResult.addFailure((ValidationFailure) failedAttributes.next()); 711 } 712 } 713 } 714 715 722 public void validateForInsert(ValidationResult validationResult) { 723 validateForSave(validationResult); 724 } 725 726 733 public void validateForUpdate(ValidationResult validationResult) { 734 validateForSave(validationResult); 735 } 736 737 744 public void validateForDelete(ValidationResult validationResult) { 745 } 747 748 749 750 755 public void encodeAsXML(XMLEncoder encoder) { 756 EntityResolver er = getDataContext().getEntityResolver(); 757 ObjEntity object = er.lookupObjEntity(getClass()); 758 759 String [] fields = this.getClass().getName().split("\\."); 761 encoder.setRoot(fields[fields.length - 1], this.getClass().getName()); 762 763 for (Iterator it = object.getDeclaredAttributes().iterator(); it.hasNext();) { 764 ObjAttribute att = (ObjAttribute) it.next(); 765 String name = att.getName(); 766 encoder.encodeProperty(name, readNestedProperty(name)); 767 } 768 } 769 770 public void decodeFromXML(XMLDecoder decoder) { 771 ObjEntity object = null; 772 773 for (Iterator it = Configuration.getSharedConfiguration().getDomain().getDataNodes().iterator(); it.hasNext();) { 777 DataNode dn = (DataNode) it.next(); 778 779 EntityResolver er = dn.getEntityResolver(); 780 object = er.lookupObjEntity(getClass()); 781 782 if (null != object) { 783 break; 784 } 785 } 786 787 for (Iterator it = object.getDeclaredAttributes().iterator(); it.hasNext();) { 788 ObjAttribute att = (ObjAttribute) it.next(); 789 String name = att.getName(); 790 writeProperty(name, decoder.decodeObject(name)); 791 } 792 } 793 794 799 public Object getOid() { 800 return getObjectId(); 801 } 802 803 808 public void setOid(Object oid) { 809 if (oid == null || oid instanceof ObjectId) { 810 setObjectId((ObjectId) oid); 811 } 812 813 throw new IllegalArgumentException ( 814 "CayenneDataObject only supports ObjectId ids, got: " + oid); 815 } 816 817 822 public ObjectContext getObjectContext() { 823 return (dataContext == null || dataContext instanceof ObjectContext) 824 ? (ObjectContext) dataContext 825 : null; 826 } 827 828 831 public void setObjectContext(ObjectContext objectContext) { 832 if (objectContext == null || objectContext instanceof DataContext) { 833 setDataContext((DataContext) objectContext); 834 } 835 836 throw new IllegalArgumentException ( 837 "CayenneDataObject only supports DataContext for ObjectContext, got: " 838 + objectContext); 839 } 840 } | Popular Tags |