KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > map > DbEntity


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.map;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.LinkedList JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29
30 import org.apache.commons.collections.Transformer;
31 import org.apache.cayenne.CayenneRuntimeException;
32 import org.apache.cayenne.ObjectId;
33 import org.apache.cayenne.exp.Expression;
34 import org.apache.cayenne.exp.ExpressionException;
35 import org.apache.cayenne.exp.ExpressionFactory;
36 import org.apache.cayenne.map.event.AttributeEvent;
37 import org.apache.cayenne.map.event.DbAttributeListener;
38 import org.apache.cayenne.map.event.DbEntityListener;
39 import org.apache.cayenne.map.event.DbRelationshipListener;
40 import org.apache.cayenne.map.event.EntityEvent;
41 import org.apache.cayenne.map.event.MapEvent;
42 import org.apache.cayenne.map.event.RelationshipEvent;
43 import org.apache.cayenne.util.CayenneMapEntry;
44 import org.apache.cayenne.util.Util;
45 import org.apache.cayenne.util.XMLEncoder;
46
47 /**
48  * A DbEntity is a mapping descriptor that defines a structure of a database table.
49  *
50  * @author Misha Shengaout
51  * @author Andrus Adamchik
52  */

53 public class DbEntity extends Entity implements DbEntityListener, DbAttributeListener,
54         DbRelationshipListener {
55
56     protected String JavaDoc catalog;
57     protected String JavaDoc schema;
58     protected List JavaDoc primaryKey;
59
60     /**
61      * @since 1.2
62      */

63     protected Collection JavaDoc generatedAttributes;
64     protected DbKeyGenerator primaryKeyGenerator;
65
66     /**
67      * Creates an unnamed DbEntity.
68      */

69     public DbEntity() {
70         super();
71
72         this.primaryKey = new ArrayList JavaDoc(2);
73         this.generatedAttributes = new ArrayList JavaDoc(2);
74     }
75
76     /**
77      * Creates a named DbEntity.
78      */

79     public DbEntity(String JavaDoc name) {
80         this();
81         this.setName(name);
82     }
83
84     /**
85      * Prints itself as XML to the provided XMLEncoder.
86      *
87      * @since 1.1
88      */

89     public void encodeAsXML(XMLEncoder encoder) {
90         encoder.print("<db-entity name=\"");
91         encoder.print(Util.encodeXmlAttribute(getName()));
92         encoder.print('\"');
93
94         if (getSchema() != null && getSchema().trim().length() > 0) {
95             encoder.print(" schema=\"");
96             encoder.print(Util.encodeXmlAttribute(getSchema().trim()));
97             encoder.print('\"');
98         }
99
100         if (getCatalog() != null && getCatalog().trim().length() > 0) {
101             encoder.print(" catalog=\"");
102             encoder.print(Util.encodeXmlAttribute(getCatalog().trim()));
103             encoder.print('\"');
104         }
105
106         encoder.println('>');
107
108         encoder.indent(1);
109         encoder.print(getAttributeMap());
110
111         if (getPrimaryKeyGenerator() != null) {
112             getPrimaryKeyGenerator().encodeAsXML(encoder);
113         }
114
115         encoder.indent(-1);
116         encoder.println("</db-entity>");
117     }
118
119     /**
120      * Returns table name including schema, if present.
121      */

122     public String JavaDoc getFullyQualifiedName() {
123         return (schema != null) ? schema + '.' + getName() : getName();
124     }
125
126     /**
127      * Returns database schema of this table.
128      *
129      * @return table's schema, null if not set.
130      */

131     public String JavaDoc getSchema() {
132         return schema;
133     }
134
135     /**
136      * Sets the database schema name of the table described by this DbEntity.
137      */

138     public void setSchema(String JavaDoc schema) {
139         this.schema = schema;
140     }
141
142     /**
143      * Returns the catalog name of the table described by this DbEntity.
144      */

145     public String JavaDoc getCatalog() {
146         return catalog;
147     }
148
149     /**
150      * Sets the catalog name of the table described by this DbEntity.
151      */

152     public void setCatalog(String JavaDoc catalog) {
153         this.catalog = catalog;
154     }
155
156     /**
157      * Returns an unmodifiable list of DbAttributes representing the primary key of the
158      * table described by this DbEntity.
159      */

160     // TODO: (Andrus 09/06/2005) Change to Collection ... no reason to keep as list other
161
// than backwards compatibility
162
public List JavaDoc getPrimaryKey() {
163         return Collections.unmodifiableList(primaryKey);
164     }
165
166     /**
167      * Returns an unmodifiable collection of DbAttributes that are generated by the
168      * database.
169      *
170      * @since 1.2
171      */

172     public Collection JavaDoc getGeneratedAttributes() {
173         return Collections.unmodifiableCollection(generatedAttributes);
174     }
175
176     /**
177      * Overrides super to fire an AttributeEvent.
178      */

179     public void addAttribute(Attribute attr) {
180         super.addAttribute(attr);
181         this.dbAttributeAdded(new AttributeEvent(this, attr, this, MapEvent.ADD));
182     }
183
184     /**
185      * Removes attribute from the entity, removes any relationship joins containing this
186      * attribute.
187      *
188      * @see org.apache.cayenne.map.Entity#removeAttribute(String)
189      */

190     public void removeAttribute(String JavaDoc attrName) {
191         Attribute attr = getAttribute(attrName);
192         if (attr == null) {
193             return;
194         }
195
196         DataMap map = getDataMap();
197         if (map != null) {
198             Iterator JavaDoc ents = map.getDbEntities().iterator();
199             while (ents.hasNext()) {
200                 DbEntity ent = (DbEntity) ents.next();
201                 Iterator JavaDoc it = ent.getRelationships().iterator();
202                 while (it.hasNext()) {
203                     DbRelationship rel = (DbRelationship) it.next();
204                     Iterator JavaDoc joins = rel.getJoins().iterator();
205                     while (joins.hasNext()) {
206                         DbJoin join = (DbJoin) joins.next();
207                         if (join.getSource() == attr || join.getTarget() == attr) {
208                             joins.remove();
209                         }
210                     }
211                 }
212             }
213         }
214
215         super.removeAttribute(attrName);
216         this.dbAttributeRemoved(new AttributeEvent(this, attr, this, MapEvent.REMOVE));
217     }
218
219     public void clearAttributes() {
220         super.clearAttributes();
221         // post dummy event for no specific attribute
222
this.dbAttributeRemoved(new AttributeEvent(this, null, this, MapEvent.REMOVE));
223     }
224
225     public Iterator JavaDoc resolvePathComponents(Expression pathExp) throws ExpressionException {
226         if (pathExp.getType() != Expression.DB_PATH) {
227             throw new ExpressionException("Invalid expression type: '"
228                     + pathExp.expName()
229                     + "', DB_PATH is expected.");
230         }
231
232         return new PathIterator((String JavaDoc) pathExp.getOperand(0));
233     }
234
235     public void setPrimaryKeyGenerator(DbKeyGenerator primaryKeyGenerator) {
236         this.primaryKeyGenerator = primaryKeyGenerator;
237         if (primaryKeyGenerator != null) {
238             primaryKeyGenerator.setDbEntity(this);
239         }
240     }
241
242     public DbKeyGenerator getPrimaryKeyGenerator() {
243         return primaryKeyGenerator;
244     }
245
246     /**
247      * DbEntity property changed. May be name, attribute or relationship added or removed,
248      * etc. Attribute and relationship property changes are handled in respective
249      * listeners.
250      *
251      * @since 1.2
252      */

253     public void dbEntityChanged(EntityEvent e) {
254         if ((e == null) || (e.getEntity() != this)) {
255             // not our concern
256
return;
257         }
258
259         // handle entity name changes
260
if (e.getId() == EntityEvent.CHANGE && e.isNameChange()) {
261             String JavaDoc newName = e.getNewName();
262             DataMap map = getDataMap();
263             if (map != null) {
264                 // handle all of the relationship target names that need to be changed
265
Iterator JavaDoc ents = map.getDbEntities().iterator();
266                 while (ents.hasNext()) {
267                     DbEntity dbe = (DbEntity) ents.next();
268                     Iterator JavaDoc rit = dbe.getRelationships().iterator();
269                     while (rit.hasNext()) {
270                         DbRelationship rel = (DbRelationship) rit.next();
271                         if (rel.getTargetEntity() == this) {
272                             rel.setTargetEntityName(newName);
273                         }
274                     }
275                 }
276                 // get all of the related object entities
277
ents = map.getMappedEntities(this).iterator();
278                 while (ents.hasNext()) {
279                     ObjEntity oe = (ObjEntity) ents.next();
280                     if (oe.getDbEntity() == this) {
281                         oe.setDbEntityName(newName);
282                     }
283                 }
284             }
285         }
286     }
287
288     /** New entity has been created/added. */
289     public void dbEntityAdded(EntityEvent e) {
290         // does nothing currently
291
}
292
293     /** Entity has been removed. */
294     public void dbEntityRemoved(EntityEvent e) {
295         // does nothing currently
296
}
297
298     public void dbAttributeAdded(AttributeEvent e) {
299         this.handleAttributeUpdate(e);
300     }
301
302     public void dbAttributeChanged(AttributeEvent e) {
303         this.handleAttributeUpdate(e);
304     }
305
306     public void dbAttributeRemoved(AttributeEvent e) {
307         this.handleAttributeUpdate(e);
308     }
309
310     private void handleAttributeUpdate(AttributeEvent e) {
311         if ((e == null) || (e.getEntity() != this)) {
312             // not our concern
313
return;
314         }
315
316         // catch clearing (event with null ('any') DbAttribute)
317
Attribute attr = e.getAttribute();
318         if ((attr == null) && (this.attributes.isEmpty())) {
319             this.primaryKey.clear();
320             this.generatedAttributes.clear();
321             return;
322         }
323
324         // make sure we handle a DbAttribute
325
if (!(attr instanceof DbAttribute)) {
326             return;
327         }
328
329         DbAttribute dbAttr = (DbAttribute) attr;
330
331         // handle attribute name changes
332
if (e.getId() == AttributeEvent.CHANGE && e.isNameChange()) {
333             String JavaDoc oldName = e.getOldName();
334             String JavaDoc newName = e.getNewName();
335
336             DataMap map = getDataMap();
337             if (map != null) {
338                 Iterator JavaDoc ents = map.getDbEntities().iterator();
339                 while (ents.hasNext()) {
340                     DbEntity ent = (DbEntity) ents.next();
341
342                     // handle all of the dependent object entity attribute changes
343
Iterator JavaDoc it = map.getMappedEntities(ent).iterator();
344                     while (it.hasNext()) {
345                         ObjEntity oe = (ObjEntity) it.next();
346                         Iterator JavaDoc ait = oe.getAttributes().iterator();
347                         while (ait.hasNext()) {
348                             ObjAttribute oa = (ObjAttribute) ait.next();
349                             if (oa.getDbAttribute() == dbAttr) {
350                                 oa.setDbAttributeName(newName);
351                             }
352                         }
353                     }
354
355                     // handle all of the relationships / joins that use the changed
356
// attribute
357
it = ent.getRelationships().iterator();
358                     while (it.hasNext()) {
359                         DbRelationship rel = (DbRelationship) it.next();
360                         Iterator JavaDoc joins = rel.getJoins().iterator();
361                         while (joins.hasNext()) {
362                             DbJoin join = (DbJoin) joins.next();
363                             if (join.getSource() == dbAttr) {
364                                 join.setSourceName(newName);
365                             }
366                             if (join.getTarget() == dbAttr) {
367                                 join.setTargetName(newName);
368                             }
369                         }
370                     }
371                 }
372             }
373
374             // clear the attribute out of the collection
375
attributes.remove(oldName);
376
377             // add the attribute back in with the new name
378
super.addAttribute(dbAttr);
379         }
380
381         // handle PK refresh
382
if (primaryKey.contains(dbAttr) || dbAttr.isPrimaryKey()) {
383             switch (e.getId()) {
384                 case MapEvent.ADD:
385                     this.primaryKey.add(attr);
386                     break;
387
388                 case MapEvent.REMOVE:
389                     this.primaryKey.remove(attr);
390                     break;
391
392                 default:
393                     // generic update
394
this.primaryKey.clear();
395                     Iterator JavaDoc it = this.getAttributes().iterator();
396                     while (it.hasNext()) {
397                         DbAttribute dba = (DbAttribute) it.next();
398                         if (dba.isPrimaryKey()) {
399                             this.primaryKey.add(dba);
400                         }
401                     }
402             }
403         }
404
405         // handle generated key refresh
406
if (generatedAttributes.contains(dbAttr) || dbAttr.isGenerated()) {
407             switch (e.getId()) {
408                 case MapEvent.ADD:
409                     this.generatedAttributes.add(attr);
410                     break;
411
412                 case MapEvent.REMOVE:
413                     this.generatedAttributes.remove(attr);
414                     break;
415
416                 default:
417                     // generic update
418
this.generatedAttributes.clear();
419                     Iterator JavaDoc it = this.getAttributes().iterator();
420                     while (it.hasNext()) {
421                         DbAttribute dba = (DbAttribute) it.next();
422                         if (dba.isGenerated()) {
423                             this.generatedAttributes.add(dba);
424                         }
425                     }
426             }
427         }
428     }
429
430     /**
431      * Relationship property changed.
432      */

433     public void dbRelationshipChanged(RelationshipEvent e) {
434         if ((e == null) || (e.getEntity() != this)) {
435             // not our concern
436
return;
437         }
438
439         Relationship rel = e.getRelationship();
440         // make sure we handle a DbRelationship
441
if (!(rel instanceof DbRelationship)) {
442             return;
443         }
444
445         DbRelationship dbRel = (DbRelationship) rel;
446
447         // handle relationship name changes
448
if (e.getId() == RelationshipEvent.CHANGE && e.isNameChange()) {
449             String JavaDoc oldName = e.getOldName();
450             String JavaDoc newName = e.getNewName();
451
452             DataMap map = getDataMap();
453             if (map != null) {
454                 // finds all object entities with a db relationship path to the renamed
455
// relationship
456
Iterator JavaDoc ents = map.getObjEntities().iterator();
457                 while (ents.hasNext()) {
458                     ObjEntity oe = (ObjEntity) ents.next();
459                     Iterator JavaDoc rit = oe.getRelationships().iterator();
460                     while (rit.hasNext()) {
461                         ObjRelationship or = (ObjRelationship) rit.next();
462                         // rename the db relationship path with the new name
463
if (Util.nullSafeEquals(or.getDbRelationshipPath(), oldName)) {
464                             or.setDbRelationshipPath(newName);
465                         }
466                     }
467                 }
468             }
469
470             // clear the relationship out of the collection
471
relationships.remove(oldName);
472
473             // add the relationship back in with the new name
474
super.addRelationship(dbRel);
475         }
476     }
477
478     /** Relationship has been created/added. */
479     public void dbRelationshipAdded(RelationshipEvent e) {
480         // does nothing currently
481
}
482
483     /** Relationship has been removed. */
484     public void dbRelationshipRemoved(RelationshipEvent e) {
485         // does nothing currently
486
}
487
488     /**
489      * Returns true if there is full replacement id is attached to an ObjectId. "Full"
490      * means that all PK columns are present and only PK columns are present.
491      *
492      * @since 1.2
493      */

494     public boolean isFullReplacementIdAttached(ObjectId id) {
495         if (!id.isReplacementIdAttached()) {
496             return false;
497         }
498
499         Map JavaDoc replacement = id.getReplacementIdMap();
500         Collection JavaDoc pk = getPrimaryKey();
501         if (pk.size() != replacement.size()) {
502             return false;
503         }
504
505         Iterator JavaDoc it = pk.iterator();
506         while (it.hasNext()) {
507             DbAttribute attribute = (DbAttribute) it.next();
508             if (!replacement.containsKey(attribute.getName())) {
509                 return false;
510             }
511         }
512
513         return true;
514     }
515
516     /**
517      * Transforms Expression rooted in this entity to an analogous expression rooted in
518      * related entity.
519      *
520      * @since 1.1
521      */

522     public Expression translateToRelatedEntity(
523             Expression expression,
524             String JavaDoc relationshipPath) {
525
526         if (expression == null) {
527             return null;
528         }
529
530         if (relationshipPath == null) {
531             return expression;
532         }
533
534         return expression.transform(new RelationshipPathConverter(relationshipPath));
535     }
536
537     final class RelationshipPathConverter implements Transformer {
538
539         String JavaDoc relationshipPath;
540
541         RelationshipPathConverter(String JavaDoc relationshipPath) {
542             this.relationshipPath = relationshipPath;
543         }
544
545         public Object JavaDoc transform(Object JavaDoc input) {
546             if (!(input instanceof Expression)) {
547                 return input;
548             }
549
550             Expression expression = (Expression) input;
551
552             if (expression.getType() != Expression.DB_PATH) {
553                 return input;
554             }
555
556             String JavaDoc path = (String JavaDoc) expression.getOperand(0);
557             String JavaDoc converted = translatePath(path);
558             Expression transformed = ExpressionFactory
559                     .expressionOfType(Expression.DB_PATH);
560             transformed.setOperand(0, converted);
561             return transformed;
562         }
563
564         String JavaDoc translatePath(String JavaDoc path) {
565
566             // algorithm to determine the translated path:
567
// 1. If relationship path equals to input, travel one step back, and then one
568
// step forward.
569
// 2. If input completely includes relationship path, use input's remaining
570
// tail.
571
// 3. If relationship path and input have none or some leading components in
572
// common,
573
// (a) strip common leading part;
574
// (b) reverse the remaining relationship part;
575
// (c) append remaining input to the reversed remaining relationship.
576

577             // case (1)
578
if (path.equals(relationshipPath)) {
579
580                 LinkedList JavaDoc finalPath = new LinkedList JavaDoc();
581                 Iterator JavaDoc it = resolvePathComponents(path);
582
583                 // just do one step back and one step forward to create correct joins...
584
// find last rel...
585
DbRelationship lastDBR = null;
586
587                 while (it.hasNext()) {
588                     // relationship path components must be DbRelationships
589
lastDBR = (DbRelationship) it.next();
590                 }
591
592                 if (lastDBR != null) {
593                     prependReversedPath(finalPath, lastDBR);
594                     appendPath(finalPath, lastDBR);
595                 }
596
597                 return convertToPath(finalPath);
598             }
599
600             // case (2)
601
String JavaDoc relationshipPathWithDot = relationshipPath + Entity.PATH_SEPARATOR;
602             if (path.startsWith(relationshipPathWithDot)) {
603                 return path.substring(relationshipPathWithDot.length());
604             }
605
606             // case (3)
607
Iterator JavaDoc pathIt = resolvePathComponents(path);
608             Iterator JavaDoc relationshipIt = resolvePathComponents(relationshipPath);
609
610             // for inserts from the both ends use LinkedList
611
LinkedList JavaDoc finalPath = new LinkedList JavaDoc();
612
613             while (relationshipIt.hasNext() && pathIt.hasNext()) {
614                 // relationship path components must be DbRelationships
615
DbRelationship nextDBR = (DbRelationship) relationshipIt.next();
616
617                 // expression components may be attributes or relationships
618
CayenneMapEntry next = (CayenneMapEntry) pathIt.next();
619
620                 if (nextDBR != next) {
621                     // found split point
622
// consume the last iteration components,
623
// then break out to finish the iterators independenly
624
prependReversedPath(finalPath, nextDBR);
625                     appendPath(finalPath, next);
626                     break;
627                 }
628
629                 break;
630             }
631
632             // append remainder of the relationship, reversing it
633
while (relationshipIt.hasNext()) {
634                 DbRelationship nextDBR = (DbRelationship) relationshipIt.next();
635                 prependReversedPath(finalPath, nextDBR);
636             }
637
638             while (pathIt.hasNext()) {
639                 // components may be attributes or relationships
640
CayenneMapEntry next = (CayenneMapEntry) pathIt.next();
641                 appendPath(finalPath, next);
642             }
643
644             return convertToPath(finalPath);
645         }
646
647         private String JavaDoc convertToPath(List JavaDoc path) {
648             StringBuffer JavaDoc converted = new StringBuffer JavaDoc();
649             int len = path.size();
650             for (int i = 0; i < len; i++) {
651                 if (i > 0) {
652                     converted.append(Entity.PATH_SEPARATOR);
653                 }
654
655                 converted.append(path.get(i));
656             }
657
658             return converted.toString();
659         }
660
661         private void prependReversedPath(LinkedList JavaDoc finalPath, DbRelationship relationship) {
662             DbRelationship revNextDBR = relationship.getReverseRelationship();
663
664             if (revNextDBR == null) {
665                 throw new CayenneRuntimeException(
666                         "Unable to find reverse DbRelationship for "
667                                 + relationship.getSourceEntity().getName()
668                                 + Entity.PATH_SEPARATOR
669                                 + relationship.getName()
670                                 + ".");
671             }
672
673             finalPath.addFirst(revNextDBR.getName());
674         }
675
676         private void appendPath(LinkedList JavaDoc finalPath, CayenneMapEntry pathComponent) {
677             finalPath.addLast(pathComponent.getName());
678         }
679     }
680 }
681
Popular Tags