1 56 package org.objectstyle.cayenne.map; 57 58 import java.util.ArrayList ; 59 import java.util.Collections ; 60 import java.util.EventListener ; 61 import java.util.Iterator ; 62 import java.util.List ; 63 import java.util.ListIterator ; 64 65 import org.apache.commons.lang.builder.ToStringBuilder; 66 import org.apache.log4j.Logger; 67 import org.objectstyle.cayenne.CayenneRuntimeException; 68 import org.objectstyle.cayenne.event.EventManager; 69 import org.objectstyle.cayenne.exp.ExpressionException; 70 import org.objectstyle.cayenne.exp.parser.ASTDbPath; 71 import org.objectstyle.cayenne.map.event.RelationshipEvent; 72 import org.objectstyle.cayenne.util.Util; 73 import org.objectstyle.cayenne.util.XMLEncoder; 74 75 81 public class ObjRelationship extends Relationship implements EventListener { 82 83 private static Logger logObj = Logger.getLogger(ObjRelationship.class); 84 85 int deleteRule = DeleteRule.NO_ACTION; 86 boolean readOnly; 87 boolean dbRelationshipsRefreshNeeded = true; 88 89 protected boolean usedForLocking; 91 protected String dbRelationshipPath; 92 93 List dbRelationships = new ArrayList (); 94 List dbRelationshipsRef = Collections.unmodifiableList(dbRelationships); 95 96 public ObjRelationship() { 97 } 98 99 public ObjRelationship(String name) { 100 super(name); 101 } 102 103 108 public void encodeAsXML(XMLEncoder encoder) { 109 ObjEntity source = (ObjEntity) getSourceEntity(); 110 if (source == null) { 111 logObj 112 .warn("No source entity, will not encode ObjRelationship: " 113 + getName()); 114 return; 115 } 116 117 encoder.print("<obj-relationship name=\"" + getName()); 118 encoder.print("\" source=\"" + source.getName()); 119 120 ObjEntity target = (ObjEntity) getTargetEntity(); 121 if (target != null) { 122 encoder.print("\" target=\"" + target.getName()); 123 } 124 125 if (isUsedForLocking()) { 126 encoder.print("\" lock=\"true"); 127 } 128 129 String deleteRule = DeleteRule.deleteRuleName(getDeleteRule()); 130 if (getDeleteRule() != DeleteRule.NO_ACTION && deleteRule != null) { 131 encoder.print("\" deleteRule=\"" + deleteRule); 132 } 133 134 String path = getValidRelationshipPath(); 137 if (path != null) { 138 encoder.print("\" db-relationship-path=\"" + path); 139 } 140 141 encoder.println("\"/>"); 142 } 143 144 public Entity getTargetEntity() { 145 String targetName = getTargetEntityName(); 146 if (targetName == null) { 147 return null; 148 } 149 150 return getNonNullNamespace().getObjEntity(targetName); 151 } 152 153 157 public ObjRelationship getReverseRelationship() { 158 List reversed = new ArrayList (); 160 Iterator rit = this.getDbRelationships().iterator(); 161 while (rit.hasNext()) { 162 DbRelationship rel = (DbRelationship) rit.next(); 163 DbRelationship reverse = rel.getReverseRelationship(); 164 if (reverse == null) 165 return null; 166 167 reversed.add(0, reverse); 168 } 169 170 Entity target = this.getTargetEntity(); 171 Entity src = this.getSourceEntity(); 172 173 Iterator it = target.getRelationships().iterator(); 174 while (it.hasNext()) { 175 ObjRelationship rel = (ObjRelationship) it.next(); 176 if (rel.getTargetEntity() != src) 177 continue; 178 179 List otherRels = rel.getDbRelationships(); 180 if (reversed.size() != otherRels.size()) 181 continue; 182 183 int len = reversed.size(); 184 boolean relsMatch = true; 185 for (int i = 0; i < len; i++) { 186 if (otherRels.get(i) != reversed.get(i)) { 187 relsMatch = false; 188 break; 189 } 190 } 191 192 if (relsMatch) 193 return rel; 194 } 195 196 return null; 197 } 198 199 202 public List getDbRelationships() { 203 refreshFromPath(true); 204 return dbRelationshipsRef; 205 } 206 207 208 public void addDbRelationship(DbRelationship dbRel) { 209 refreshFromPath(true); 210 211 int numDbRelationships = dbRelationships.size(); 215 if (numDbRelationships > 0) { 216 DbRelationship lastRel = (DbRelationship) dbRelationships 217 .get(numDbRelationships - 1); 218 if (!lastRel.getTargetEntityName().equals(dbRel.getSourceEntity().getName())) { 219 throw new CayenneRuntimeException("Error adding db relationship " 220 + dbRel 221 + " to ObjRelationship " 222 + this 223 + " because the source of the newly added relationship " 224 + "is not the target of the previous relationship " 225 + "in the chain"); 226 } 227 } 228 229 EventManager.getDefaultManager().addListener(this, 230 "dbRelationshipDidChange", 231 RelationshipEvent.class, 232 DbRelationship.PROPERTY_DID_CHANGE, 233 dbRel); 234 235 dbRelationships.add(dbRel); 236 237 this.calculateReadOnlyValue(); 238 this.calculateToManyValue(); 239 } 240 241 244 public void removeDbRelationship(DbRelationship dbRel) { 245 refreshFromPath(true); 246 247 dbRelationships.remove(dbRel); 248 EventManager.getDefaultManager().removeListener(this, 250 DbRelationship.PROPERTY_DID_CHANGE, 251 dbRel); 252 253 this.calculateReadOnlyValue(); 254 this.calculateToManyValue(); 255 } 256 257 public void clearDbRelationships() { 258 this.dbRelationshipPath = null; 259 this.dbRelationshipsRefreshNeeded = false; 260 this.dbRelationships.clear(); 261 this.readOnly = false; 262 this.toMany = false; 263 } 264 265 271 public boolean isSourceIndependentFromTargetChange() { 272 return isToMany() || isFlattened() || isToDependentEntity() || !isToPK(); 275 } 276 277 280 public boolean isToDependentEntity() { 281 return ((DbRelationship) getDbRelationships().get(0)).isToDependentPK(); 282 } 283 284 290 public boolean isToPK() { 291 return ((DbRelationship) getDbRelationships().get(0)).isToPK(); 292 } 293 294 304 public boolean isFlattened() { 305 return getDbRelationships().size() > 1; 306 } 307 308 314 public boolean isReadOnly() { 315 refreshFromPath(true); 316 return readOnly; 317 } 318 319 public boolean isToMany() { 320 refreshFromPath(true); 321 return super.isToMany(); 322 } 323 324 332 public int getDeleteRule() { 333 return deleteRule; 334 } 335 336 344 public void setDeleteRule(int value) { 345 if ((value != DeleteRule.CASCADE) 346 && (value != DeleteRule.DENY) 347 && (value != DeleteRule.NULLIFY) 348 && (value != DeleteRule.NO_ACTION)) { 349 350 throw new IllegalArgumentException ("Delete rule value " 351 + value 352 + " is not a constant from the DeleteRule class"); 353 } 354 355 this.deleteRule = value; 356 } 357 358 public void dbRelationshipDidChange(RelationshipEvent event) { 359 this.calculateToManyValue(); 360 } 361 362 367 public boolean isUsedForLocking() { 368 return usedForLocking; 369 } 370 371 376 public void setUsedForLocking(boolean usedForLocking) { 377 this.usedForLocking = usedForLocking; 378 } 379 380 385 public String getDbRelationshipPath() { 386 if (dbRelationshipsRefreshNeeded) { 387 return dbRelationshipPath; 388 } 389 else { 390 if (getDbRelationships().isEmpty()) { 392 return null; 393 } 394 395 StringBuffer path = new StringBuffer (); 396 Iterator it = getDbRelationships().iterator(); 397 while (it.hasNext()) { 398 DbRelationship next = (DbRelationship) it.next(); 399 path.append(next.getName()); 400 if (it.hasNext()) { 401 path.append(Entity.PATH_SEPARATOR); 402 } 403 } 404 405 return path.toString(); 406 } 407 } 408 409 414 public String getReverseDbRelationshipPath() throws ExpressionException { 415 416 List relationships = getDbRelationships(); 417 if (relationships == null || relationships.isEmpty()) { 418 return null; 419 } 420 421 StringBuffer buffer = new StringBuffer (); 422 423 ListIterator it = relationships.listIterator(relationships.size()); 425 while (it.hasPrevious()) { 426 427 DbRelationship relationship = (DbRelationship) it.previous(); 428 DbRelationship reverse = relationship.getReverseRelationship(); 429 430 if (reverse == null) { 432 throw new CayenneRuntimeException("No reverse relationship exist for " 433 + relationship); 434 } 435 436 if (buffer.length() > 0) { 437 buffer.append(Entity.PATH_SEPARATOR); 438 } 439 440 buffer.append(reverse.getName()); 441 } 442 443 return buffer.toString(); 444 } 445 446 449 public void setDbRelationshipPath(String relationshipPath) { 450 if (!Util.nullSafeEquals(this.dbRelationshipPath, relationshipPath)) { 451 this.dbRelationshipPath = relationshipPath; 452 this.dbRelationshipsRefreshNeeded = true; 453 } 454 } 455 456 460 String getValidRelationshipPath() { 461 String path = getDbRelationshipPath(); 462 if (path == null) { 463 return null; 464 } 465 466 ObjEntity entity = (ObjEntity) getSourceEntity(); 467 if (entity == null) { 468 throw new CayenneRuntimeException( 469 "Can't resolve DbRelationships, null source ObjEntity"); 470 } 471 472 StringBuffer validPath = new StringBuffer (); 473 474 try { 475 Iterator it = entity.resolvePathComponents(new ASTDbPath(path)); 476 while (it.hasNext()) { 477 DbRelationship relationship = (DbRelationship) it.next(); 478 479 if (validPath.length() > 0) { 480 validPath.append(Entity.PATH_SEPARATOR); 481 } 482 validPath.append(relationship.getName()); 483 } 484 } 485 catch (ExpressionException ex) { 486 487 } 488 489 return validPath.toString(); 490 } 491 492 495 final void refreshFromPath(boolean stripInvalid) { 496 if (!dbRelationshipsRefreshNeeded) { 497 return; 498 } 499 500 synchronized (this) { 501 if (!dbRelationshipsRefreshNeeded) { 503 return; 504 } 505 506 EventManager eventLoop = EventManager.getDefaultManager(); 507 508 Iterator removeIt = dbRelationships.iterator(); 510 while (removeIt.hasNext()) { 511 DbRelationship relationship = (DbRelationship) removeIt.next(); 512 eventLoop.removeListener(this, 513 DbRelationship.PROPERTY_DID_CHANGE, 514 relationship); 515 516 removeIt.remove(); 517 } 518 519 if (this.dbRelationshipPath != null) { 520 521 ObjEntity entity = (ObjEntity) getSourceEntity(); 522 if (entity == null) { 523 throw new CayenneRuntimeException( 524 "Can't resolve DbRelationships, null source ObjEntity"); 525 } 526 527 try { 528 Iterator it = entity.resolvePathComponents(new ASTDbPath( 530 this.dbRelationshipPath)); 531 532 while (it.hasNext()) { 533 DbRelationship relationship = (DbRelationship) it.next(); 534 535 eventLoop.addListener(this, 537 "dbRelationshipDidChange", 538 RelationshipEvent.class, 539 DbRelationship.PROPERTY_DID_CHANGE, 540 relationship); 541 542 dbRelationships.add(relationship); 543 } 544 } 545 catch (ExpressionException ex) { 546 if (!stripInvalid) { 547 throw ex; 548 } 549 } 550 } 551 552 calculateToManyValue(); 553 calculateReadOnlyValue(); 554 555 dbRelationshipsRefreshNeeded = false; 556 } 557 } 558 559 563 final void calculateToManyValue() { 564 Iterator dbRelIterator = this.dbRelationships.iterator(); 569 while (dbRelIterator.hasNext()) { 570 DbRelationship thisRel = (DbRelationship) dbRelIterator.next(); 571 if (thisRel.isToMany()) { 572 this.toMany = true; 573 return; 574 } 575 } 576 577 this.toMany = false; 578 } 579 580 583 final void calculateReadOnlyValue() { 584 if (dbRelationships.size() < 2) { 586 this.readOnly = false; 587 return; 588 } 589 590 if (dbRelationships.size() > 2) { 592 this.readOnly = true; 593 return; 594 } 595 596 DbRelationship firstRel = (DbRelationship) dbRelationships.get(0); 597 DbRelationship secondRel = (DbRelationship) dbRelationships.get(1); 598 599 if (!firstRel.isToMany() || secondRel.isToMany()) { 601 this.readOnly = true; 602 return; 603 } 604 605 DataMap map = firstRel.getTargetEntity().getDataMap(); 606 if (map == null) { 607 throw new CayenneRuntimeException(this.getClass().getName() 608 + " could not obtain a DataMap for the destination of " 609 + firstRel.getName()); 610 } 611 612 if (!secondRel.isToPK()) { 614 this.readOnly = true; 615 return; 616 } 617 618 DbRelationship firstReverseRel = firstRel.getReverseRelationship(); 619 if (firstReverseRel == null || !firstReverseRel.isToPK()) { 620 this.readOnly = true; 621 return; 622 } 623 624 this.readOnly = false; 625 } 626 627 public String toString() { 628 return new ToStringBuilder(this) 629 .append("name", getName()) 630 .append("dbRelationshipPath", dbRelationshipPath) 631 .toString(); 632 } 633 } | Popular Tags |