1 19 20 package org.apache.cayenne.map; 21 22 import java.util.ArrayList ; 23 import java.util.Collections ; 24 import java.util.EventListener ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.ListIterator ; 28 29 import org.apache.commons.lang.builder.ToStringBuilder; 30 import org.apache.cayenne.CayenneRuntimeException; 31 import org.apache.cayenne.event.EventManager; 32 import org.apache.cayenne.exp.ExpressionException; 33 import org.apache.cayenne.exp.parser.ASTDbPath; 34 import org.apache.cayenne.map.event.RelationshipEvent; 35 import org.apache.cayenne.util.Util; 36 import org.apache.cayenne.util.XMLEncoder; 37 38 44 public class ObjRelationship extends Relationship implements EventListener { 45 46 boolean readOnly; 47 boolean dbRelationshipsRefreshNeeded = true; 48 49 protected int deleteRule = DeleteRule.NO_ACTION; 50 protected boolean usedForLocking; 51 protected String dbRelationshipPath; 52 53 protected List dbRelationships = new ArrayList (); 54 55 public ObjRelationship() { 56 this(null); 57 } 58 59 public ObjRelationship(String name) { 60 super(name); 61 } 62 63 68 public void encodeAsXML(XMLEncoder encoder) { 69 ObjEntity source = (ObjEntity) getSourceEntity(); 70 if (source == null) { 71 return; 72 } 73 74 encoder.print("<obj-relationship name=\"" + getName()); 75 encoder.print("\" source=\"" + source.getName()); 76 77 ObjEntity target = (ObjEntity) getTargetEntity(); 78 if (target != null) { 79 encoder.print("\" target=\"" + target.getName()); 80 } 81 82 if (isUsedForLocking()) { 83 encoder.print("\" lock=\"true"); 84 } 85 86 String deleteRule = DeleteRule.deleteRuleName(getDeleteRule()); 87 if (getDeleteRule() != DeleteRule.NO_ACTION && deleteRule != null) { 88 encoder.print("\" deleteRule=\"" + deleteRule); 89 } 90 91 String path = getValidRelationshipPath(); 94 if (path != null) { 95 encoder.print("\" db-relationship-path=\"" + path); 96 } 97 98 encoder.println("\"/>"); 99 } 100 101 105 public Entity getTargetEntity() { 106 String targetName = getTargetEntityName(); 107 if (targetName == null) { 108 return null; 109 } 110 111 return getNonNullNamespace().getObjEntity(targetName); 112 } 113 114 120 public String getReverseRelationshipName() { 121 ObjRelationship reverse = getReverseRelationship(); 122 return (reverse != null) ? reverse.getName() : null; 123 } 124 125 129 public ObjRelationship getReverseRelationship() { 130 List reversed = new ArrayList (); 132 Iterator rit = this.getDbRelationships().iterator(); 133 while (rit.hasNext()) { 134 DbRelationship rel = (DbRelationship) rit.next(); 135 DbRelationship reverse = rel.getReverseRelationship(); 136 if (reverse == null) 137 return null; 138 139 reversed.add(0, reverse); 140 } 141 142 Entity target = this.getTargetEntity(); 143 if (target == null) { 144 return null; 145 } 146 147 Entity src = this.getSourceEntity(); 148 149 Iterator it = target.getRelationships().iterator(); 150 while (it.hasNext()) { 151 ObjRelationship rel = (ObjRelationship) it.next(); 152 if (rel.getTargetEntity() != src) 153 continue; 154 155 List otherRels = rel.getDbRelationships(); 156 if (reversed.size() != otherRels.size()) 157 continue; 158 159 int len = reversed.size(); 160 boolean relsMatch = true; 161 for (int i = 0; i < len; i++) { 162 if (otherRels.get(i) != reversed.get(i)) { 163 relsMatch = false; 164 break; 165 } 166 } 167 168 if (relsMatch) 169 return rel; 170 } 171 172 return null; 173 } 174 175 178 public List getDbRelationships() { 179 refreshFromPath(true); 180 return Collections.unmodifiableList(dbRelationships); 181 } 182 183 184 public void addDbRelationship(DbRelationship dbRel) { 185 refreshFromPath(true); 186 187 int numDbRelationships = dbRelationships.size(); 191 if (numDbRelationships > 0) { 192 DbRelationship lastRel = (DbRelationship) dbRelationships 193 .get(numDbRelationships - 1); 194 if (!lastRel.getTargetEntityName().equals(dbRel.getSourceEntity().getName())) { 195 throw new CayenneRuntimeException("Error adding db relationship " 196 + dbRel 197 + " to ObjRelationship " 198 + this 199 + " because the source of the newly added relationship " 200 + "is not the target of the previous relationship " 201 + "in the chain"); 202 } 203 } 204 205 EventManager.getDefaultManager().addListener( 206 this, 207 "dbRelationshipDidChange", 208 RelationshipEvent.class, 209 DbRelationship.PROPERTY_DID_CHANGE, 210 dbRel); 211 212 dbRelationships.add(dbRel); 213 214 this.calculateReadOnlyValue(); 215 this.calculateToManyValue(); 216 } 217 218 221 public void removeDbRelationship(DbRelationship dbRel) { 222 refreshFromPath(true); 223 224 dbRelationships.remove(dbRel); 225 EventManager.getDefaultManager().removeListener( 227 this, 228 DbRelationship.PROPERTY_DID_CHANGE, 229 dbRel); 230 231 this.calculateReadOnlyValue(); 232 this.calculateToManyValue(); 233 } 234 235 public void clearDbRelationships() { 236 this.dbRelationshipPath = null; 237 this.dbRelationshipsRefreshNeeded = false; 238 this.dbRelationships.clear(); 239 this.readOnly = false; 240 this.toMany = false; 241 } 242 243 249 public boolean isSourceIndependentFromTargetChange() { 250 return isToMany() || isFlattened() || isToDependentEntity() || !isToPK(); 253 } 254 255 258 public boolean isToDependentEntity() { 259 return ((DbRelationship) getDbRelationships().get(0)).isToDependentPK(); 260 } 261 262 268 public boolean isToPK() { 269 return ((DbRelationship) getDbRelationships().get(0)).isToPK(); 270 } 271 272 282 public boolean isFlattened() { 283 return getDbRelationships().size() > 1; 284 } 285 286 292 public boolean isReadOnly() { 293 refreshFromPath(true); 294 return readOnly; 295 } 296 297 public boolean isToMany() { 298 refreshFromPath(true); 299 return super.isToMany(); 300 } 301 302 310 public int getDeleteRule() { 311 return deleteRule; 312 } 313 314 322 public void setDeleteRule(int value) { 323 if ((value != DeleteRule.CASCADE) 324 && (value != DeleteRule.DENY) 325 && (value != DeleteRule.NULLIFY) 326 && (value != DeleteRule.NO_ACTION)) { 327 328 throw new IllegalArgumentException ("Delete rule value " 329 + value 330 + " is not a constant from the DeleteRule class"); 331 } 332 333 this.deleteRule = value; 334 } 335 336 public void dbRelationshipDidChange(RelationshipEvent event) { 337 calculateToManyValue(); 338 calculateReadOnlyValue(); 339 } 340 341 346 public boolean isUsedForLocking() { 347 return usedForLocking; 348 } 349 350 355 public void setUsedForLocking(boolean usedForLocking) { 356 this.usedForLocking = usedForLocking; 357 } 358 359 364 public String getDbRelationshipPath() { 365 if (dbRelationshipsRefreshNeeded) { 366 return dbRelationshipPath; 367 } 368 else { 369 if (getDbRelationships().isEmpty()) { 371 return null; 372 } 373 374 StringBuffer path = new StringBuffer (); 375 Iterator it = getDbRelationships().iterator(); 376 while (it.hasNext()) { 377 DbRelationship next = (DbRelationship) it.next(); 378 path.append(next.getName()); 379 if (it.hasNext()) { 380 path.append(Entity.PATH_SEPARATOR); 381 } 382 } 383 384 return path.toString(); 385 } 386 } 387 388 393 public String getReverseDbRelationshipPath() throws ExpressionException { 394 395 List relationships = getDbRelationships(); 396 if (relationships == null || relationships.isEmpty()) { 397 return null; 398 } 399 400 StringBuffer buffer = new StringBuffer (); 401 402 ListIterator it = relationships.listIterator(relationships.size()); 404 while (it.hasPrevious()) { 405 406 DbRelationship relationship = (DbRelationship) it.previous(); 407 DbRelationship reverse = relationship.getReverseRelationship(); 408 409 if (reverse == null) { 411 throw new CayenneRuntimeException("No reverse relationship exist for " 412 + relationship); 413 } 414 415 if (buffer.length() > 0) { 416 buffer.append(Entity.PATH_SEPARATOR); 417 } 418 419 buffer.append(reverse.getName()); 420 } 421 422 return buffer.toString(); 423 } 424 425 428 public void setDbRelationshipPath(String relationshipPath) { 429 if (!Util.nullSafeEquals(this.dbRelationshipPath, relationshipPath)) { 430 this.dbRelationshipPath = relationshipPath; 431 this.dbRelationshipsRefreshNeeded = true; 432 } 433 } 434 435 439 String getValidRelationshipPath() { 440 String path = getDbRelationshipPath(); 441 if (path == null) { 442 return null; 443 } 444 445 ObjEntity entity = (ObjEntity) getSourceEntity(); 446 if (entity == null) { 447 throw new CayenneRuntimeException( 448 "Can't resolve DbRelationships, null source ObjEntity"); 449 } 450 451 StringBuffer validPath = new StringBuffer (); 452 453 try { 454 Iterator it = entity.resolvePathComponents(new ASTDbPath(path)); 455 while (it.hasNext()) { 456 DbRelationship relationship = (DbRelationship) it.next(); 457 458 if (validPath.length() > 0) { 459 validPath.append(Entity.PATH_SEPARATOR); 460 } 461 validPath.append(relationship.getName()); 462 } 463 } 464 catch (ExpressionException ex) { 465 466 } 467 468 return validPath.toString(); 469 } 470 471 474 final void refreshFromPath(boolean stripInvalid) { 475 if (!dbRelationshipsRefreshNeeded) { 476 return; 477 } 478 479 synchronized (this) { 480 if (!dbRelationshipsRefreshNeeded) { 482 return; 483 } 484 485 EventManager eventLoop = EventManager.getDefaultManager(); 486 487 Iterator removeIt = dbRelationships.iterator(); 489 while (removeIt.hasNext()) { 490 DbRelationship relationship = (DbRelationship) removeIt.next(); 491 eventLoop.removeListener( 492 this, 493 DbRelationship.PROPERTY_DID_CHANGE, 494 relationship); 495 496 removeIt.remove(); 497 } 498 499 if (this.dbRelationshipPath != null) { 500 501 ObjEntity entity = (ObjEntity) getSourceEntity(); 502 if (entity == null) { 503 throw new CayenneRuntimeException( 504 "Can't resolve DbRelationships, null source ObjEntity"); 505 } 506 507 try { 508 Iterator it = entity.resolvePathComponents(new ASTDbPath( 510 this.dbRelationshipPath)); 511 512 while (it.hasNext()) { 513 DbRelationship relationship = (DbRelationship) it.next(); 514 515 eventLoop.addListener( 517 this, 518 "dbRelationshipDidChange", 519 RelationshipEvent.class, 520 DbRelationship.PROPERTY_DID_CHANGE, 521 relationship); 522 523 dbRelationships.add(relationship); 524 } 525 } 526 catch (ExpressionException ex) { 527 if (!stripInvalid) { 528 throw ex; 529 } 530 } 531 } 532 533 calculateToManyValue(); 534 calculateReadOnlyValue(); 535 536 dbRelationshipsRefreshNeeded = false; 537 } 538 } 539 540 544 final void calculateToManyValue() { 545 Iterator dbRelIterator = this.dbRelationships.iterator(); 550 while (dbRelIterator.hasNext()) { 551 DbRelationship thisRel = (DbRelationship) dbRelIterator.next(); 552 if (thisRel.isToMany()) { 553 this.toMany = true; 554 return; 555 } 556 } 557 558 this.toMany = false; 559 } 560 561 564 final void calculateReadOnlyValue() { 565 if (dbRelationships.size() < 2) { 567 this.readOnly = false; 568 return; 569 } 570 571 if (dbRelationships.size() > 2) { 573 this.readOnly = true; 574 return; 575 } 576 577 DbRelationship firstRel = (DbRelationship) dbRelationships.get(0); 578 DbRelationship secondRel = (DbRelationship) dbRelationships.get(1); 579 580 if (!firstRel.isToMany() || secondRel.isToMany()) { 582 this.readOnly = true; 583 return; 584 } 585 586 DataMap map = firstRel.getTargetEntity().getDataMap(); 587 if (map == null) { 588 throw new CayenneRuntimeException(this.getClass().getName() 589 + " could not obtain a DataMap for the destination of " 590 + firstRel.getName()); 591 } 592 593 if (!secondRel.isToPK()) { 595 this.readOnly = true; 596 return; 597 } 598 599 DbRelationship firstReverseRel = firstRel.getReverseRelationship(); 600 if (firstReverseRel == null || !firstReverseRel.isToPK()) { 601 this.readOnly = true; 602 return; 603 } 604 605 this.readOnly = false; 606 } 607 608 public String toString() { 609 return new ToStringBuilder(this).append("name", getName()).append( 610 "dbRelationshipPath", 611 dbRelationshipPath).toString(); 612 } 613 614 620 public ObjRelationship getClientRelationship() { 621 ObjRelationship reverse = getReverseRelationship(); 622 String reverseName = reverse != null ? reverse.getName() : null; 623 624 ObjRelationship relationship = new ClientObjRelationship( 625 getName(), 626 reverseName, 627 isToMany(), 628 isReadOnly()); 629 630 relationship.setTargetEntityName(getTargetEntityName()); 631 relationship.setDeleteRule(getDeleteRule()); 632 633 635 return relationship; 636 } 637 } 638 | Popular Tags |