1 56 package org.objectstyle.cayenne.map; 57 58 import java.util.ArrayList ; 59 import java.util.Collection ; 60 import java.util.Collections ; 61 import java.util.HashMap ; 62 import java.util.Iterator ; 63 import java.util.List ; 64 import java.util.Map ; 65 66 import org.apache.commons.collections.CollectionUtils; 67 import org.apache.commons.collections.Transformer; 68 import org.objectstyle.cayenne.CayenneRuntimeException; 69 import org.objectstyle.cayenne.event.EventManager; 70 import org.objectstyle.cayenne.event.EventSubject; 71 import org.objectstyle.cayenne.map.event.RelationshipEvent; 72 import org.objectstyle.cayenne.util.Util; 73 import org.objectstyle.cayenne.util.XMLEncoder; 74 75 82 public class DbRelationship extends Relationship { 83 84 public static final EventSubject PROPERTY_DID_CHANGE = EventSubject 86 .getSubject(DbRelationship.class, "PropertyDidChange"); 87 88 protected List joins = new ArrayList (); 90 91 protected boolean toDependentPK; 95 96 public DbRelationship() { 97 super(); 98 } 99 100 public DbRelationship(String name) { 101 super(name); 102 } 103 104 109 public void encodeAsXML(XMLEncoder encoder) { 110 encoder.print("<db-relationship name=\""); 111 encoder.print(Util.encodeXmlAttribute(getName())); 112 encoder.print("\" source=\""); 113 encoder.print(getSourceEntity().getName()); 114 115 if (getTargetEntityName() != null && getTargetEntity() != null) { 116 encoder.print("\" target=\""); 117 encoder.print(getTargetEntityName()); 118 } 119 120 if (isToDependentPK() && isValidForDepPk()) { 121 encoder.print("\" toDependentPK=\"true"); 122 } 123 124 encoder.print("\" toMany=\""); 125 encoder.print(isToMany()); 126 encoder.println("\">"); 127 128 encoder.indent(1); 129 encoder.print(getJoins()); 130 encoder.indent(-1); 131 132 encoder.println("</db-relationship>"); 133 } 134 135 139 public Entity getTargetEntity() { 140 String targetName = getTargetEntityName(); 141 if (targetName == null) { 142 return null; 143 } 144 145 return getNonNullNamespace().getDbEntity(targetName); 146 } 147 148 153 public Collection getTargetAttributes() { 154 if (joins.size() == 0) { 155 return Collections.EMPTY_LIST; 156 } 157 158 return CollectionUtils.collect(joins, JoinTransformers.targetExtractor); 159 } 160 161 166 public Collection getSourceAttributes() { 167 if (joins.size() == 0) { 168 return Collections.EMPTY_LIST; 169 } 170 171 return CollectionUtils.collect(joins, JoinTransformers.sourceExtractor); 172 } 173 174 180 public DbRelationship createReverseRelationship() { 181 DbRelationship reverse = new DbRelationship(); 182 reverse.setSourceEntity(getTargetEntity()); 183 reverse.setTargetEntityName(getSourceEntity().getName()); 184 185 188 reverse.setToMany(!toMany); 189 190 Iterator it = joins.iterator(); 191 while (it.hasNext()) { 192 DbJoin join = (DbJoin) it.next(); 193 DbJoin reverseJoin = join.createReverseJoin(); 194 reverseJoin.setRelationship(reverse); 195 reverse.addJoin(reverseJoin); 196 } 197 198 return reverse; 199 } 200 201 206 public DbRelationship getReverseRelationship() { 207 Entity target = this.getTargetEntity(); 208 209 if (target == null) { 210 return null; 211 } 212 213 Entity src = this.getSourceEntity(); 214 215 if (target == src && joins.size() == 0) { 217 return null; 218 } 219 220 TestJoin testJoin = new TestJoin(this); 221 Iterator it = target.getRelationships().iterator(); 222 while (it.hasNext()) { 223 DbRelationship rel = (DbRelationship) it.next(); 224 if (rel.getTargetEntity() != src) 225 continue; 226 227 List otherJoins = rel.getJoins(); 228 if (otherJoins.size() != joins.size()) { 229 continue; 230 } 231 232 Iterator jit = otherJoins.iterator(); 233 boolean joinsMatch = true; 234 while (jit.hasNext()) { 235 DbJoin join = (DbJoin) jit.next(); 236 237 testJoin.setSourceName(join.getTargetName()); 239 testJoin.setTargetName(join.getSourceName()); 240 if (!joins.contains(testJoin)) { 241 joinsMatch = false; 242 break; 243 } 244 } 245 246 if (joinsMatch) { 247 return rel; 248 } 249 } 250 251 return null; 252 } 253 254 260 public boolean isToPK() { 261 Iterator it = getJoins().iterator(); 262 while (it.hasNext()) { 263 DbJoin join = (DbJoin) it.next(); 264 if (join.getTarget() == null) { 265 return false; 266 } 267 268 if (join.getTarget().isPrimaryKey()) { 269 return true; 270 } 271 } 272 273 return false; 274 } 275 276 280 public boolean isToMasterPK() { 281 if (isToMany() || isToDependentPK()) { 282 return false; 283 } 284 285 DbRelationship revRel = getReverseRelationship(); 286 return (revRel != null) ? revRel.isToDependentPK() : false; 287 } 288 289 294 public boolean isToDependentPK() { 295 return toDependentPK; 296 } 297 298 public void setToDependentPK(boolean flag) { 299 toDependentPK = flag; 300 } 301 302 305 public boolean isValidForDepPk() { 306 Iterator it = getJoins().iterator(); 307 if (!it.hasNext()) { 309 return false; 310 } 311 312 while (it.hasNext()) { 313 DbJoin join = (DbJoin) it.next(); 314 DbAttribute target = join.getTarget(); 315 DbAttribute source = join.getSource(); 316 317 if ((target != null && !target.isPrimaryKey()) 318 || (source != null && !source.isPrimaryKey())) { 319 return false; 320 } 321 } 322 323 return true; 324 } 325 326 330 public List getJoins() { 331 return joins; 332 } 333 334 339 public void addJoin(DbJoin join) { 340 if (join != null) { 341 joins.add(join); 342 } 343 } 344 345 public void removeJoin(DbJoin join) { 346 joins.remove(join); 347 } 348 349 public void removeAllJoins() { 350 joins.clear(); 351 } 352 353 public void setJoins(Collection newJoins) { 354 this.removeAllJoins(); 355 356 if (newJoins != null) { 357 joins.addAll(newJoins); 358 } 359 } 360 361 368 public Map targetPkSnapshotWithSrcSnapshot(Map srcSnapshot) { 369 370 if (isToMany()) { 371 throw new CayenneRuntimeException( 372 "Only 'to one' relationships support this method."); 373 } 374 375 Map idMap; 376 377 int numJoins = joins.size(); 378 int foundNulls = 0; 379 380 if (numJoins == 1) { 382 DbJoin join = (DbJoin) joins.get(0); 383 Object val = srcSnapshot.get(join.getSourceName()); 384 if (val == null) { 385 foundNulls++; 386 idMap = Collections.EMPTY_MAP; 387 } 388 else { 389 idMap = Collections.singletonMap(join.getTargetName(), val); 390 } 391 } 392 else { 394 idMap = new HashMap (numJoins * 2); 395 for (int i = 0; i < numJoins; i++) { 396 DbJoin join = (DbJoin) joins.get(i); 397 Object val = srcSnapshot.get(join.getSourceName()); 398 if (val == null) { 399 foundNulls++; 400 } 401 else { 402 idMap.put(join.getTargetName(), val); 403 } 404 } 405 } 406 407 if (foundNulls == 0) { 408 return idMap; 409 } 410 else if (foundNulls == numJoins) { 411 return null; 412 } 413 else { 414 throw new CayenneRuntimeException("Some parts of FK are missing in snapshot," 415 + " relationship: " 416 + this); 417 } 418 } 419 420 424 private Map srcSnapshotWithTargetSnapshot(Map targetSnapshot) { 425 int len = joins.size(); 426 427 if (len == 1) { 429 DbJoin join = (DbJoin) joins.get(0); 430 Object val = targetSnapshot.get(join.getTargetName()); 431 if (val == null) { 432 throw new CayenneRuntimeException( 433 "Some parts of FK are missing in snapshot, join: " + join); 434 } 435 436 return Collections.singletonMap(join.getSourceName(), val); 437 438 } 439 440 Map idMap = new HashMap (len * 2); 442 for (int i = 0; i < len; i++) { 443 DbJoin join = (DbJoin) joins.get(i); 444 Object val = targetSnapshot.get(join.getTargetName()); 445 if (val == null) { 446 throw new CayenneRuntimeException( 447 "Some parts of FK are missing in snapshot, join: " + join); 448 } 449 450 idMap.put(join.getSourceName(), val); 451 } 452 453 return idMap; 454 } 455 456 462 public Map srcFkSnapshotWithTargetSnapshot(Map targetSnapshot) { 463 464 if (isToMany()) 465 throw new CayenneRuntimeException( 466 "Only 'to one' relationships support this method."); 467 return srcSnapshotWithTargetSnapshot(targetSnapshot); 468 } 469 470 476 public Map srcPkSnapshotWithTargetSnapshot(Map targetSnapshot) { 477 if (!isToMany()) 478 throw new CayenneRuntimeException( 479 "Only 'to many' relationships support this method."); 480 return srcSnapshotWithTargetSnapshot(targetSnapshot); 481 } 482 483 486 public void setToMany(boolean toMany) { 487 this.toMany = toMany; 488 this.firePropertyDidChange(); 489 } 490 491 protected void firePropertyDidChange() { 492 RelationshipEvent event = new RelationshipEvent(this, this, this 493 .getSourceEntity()); 494 EventManager.getDefaultManager().postEvent(event, PROPERTY_DID_CHANGE); 495 } 496 497 final static class JoinTransformers { 498 499 static final Transformer targetExtractor = new Transformer() { 500 501 public Object transform(Object input) { 502 return (input instanceof DbJoin) ? ((DbJoin) input).getTarget() : input; 503 } 504 }; 505 506 static final Transformer sourceExtractor = new Transformer() { 507 508 public Object transform(Object input) { 509 return (input instanceof DbJoin) ? ((DbJoin) input).getSource() : input; 510 } 511 }; 512 } 513 514 final static class TestJoin extends DbJoin { 516 517 TestJoin(DbRelationship relationship) { 518 super(relationship); 519 } 520 521 public boolean equals(Object o) { 522 if (o == null) { 523 return false; 524 } 525 526 if (o == this) { 527 return true; 528 } 529 530 if (!(o instanceof DbJoin)) { 531 return false; 532 } 533 534 DbJoin j = (DbJoin) o; 535 return j.relationship == this.relationship 536 && Util.nullSafeEquals(j.sourceName, this.sourceName) 537 && Util.nullSafeEquals(j.targetName, this.targetName); 538 } 539 } 540 } 541 | Popular Tags |