KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne.map;
57
58 import java.util.ArrayList JavaDoc;
59 import java.util.Collection JavaDoc;
60 import java.util.Collections JavaDoc;
61 import java.util.Iterator JavaDoc;
62 import java.util.LinkedList JavaDoc;
63 import java.util.List JavaDoc;
64 import java.util.Map JavaDoc;
65
66 import org.apache.commons.collections.Transformer;
67 import org.objectstyle.cayenne.CayenneRuntimeException;
68 import org.objectstyle.cayenne.ObjectId;
69 import org.objectstyle.cayenne.exp.Expression;
70 import org.objectstyle.cayenne.exp.ExpressionException;
71 import org.objectstyle.cayenne.exp.ExpressionFactory;
72 import org.objectstyle.cayenne.map.event.AttributeEvent;
73 import org.objectstyle.cayenne.map.event.DbAttributeListener;
74 import org.objectstyle.cayenne.map.event.DbEntityListener;
75 import org.objectstyle.cayenne.map.event.DbRelationshipListener;
76 import org.objectstyle.cayenne.map.event.EntityEvent;
77 import org.objectstyle.cayenne.map.event.MapEvent;
78 import org.objectstyle.cayenne.map.event.RelationshipEvent;
79 import org.objectstyle.cayenne.util.Util;
80 import org.objectstyle.cayenne.util.XMLEncoder;
81
82 /**
83  * A DbEntity is a mapping descriptor that defines a structure of a database table.
84  *
85  * @author Misha Shengaout
86  * @author Andrei Adamchik
87  */

88 public class DbEntity extends Entity implements DbEntityListener,
89                                                 DbAttributeListener,
90                                                 DbRelationshipListener {
91
92     protected String JavaDoc catalog;
93     protected String JavaDoc schema;
94     protected List JavaDoc primaryKey;
95     protected List JavaDoc primaryKeyRef;
96
97     /**
98      * @since 1.2
99      */

100     protected Collection JavaDoc generatedAttributes;
101
102     /**
103      * @since 1.2
104      */

105     protected Collection JavaDoc generatedAttributesRef;
106
107     protected DbKeyGenerator primaryKeyGenerator;
108
109     /**
110      * Creates an unnamed DbEntity.
111      */

112     public DbEntity() {
113         super();
114
115         this.primaryKey = new ArrayList JavaDoc(2);
116         this.primaryKeyRef = Collections.unmodifiableList(primaryKey);
117
118         this.generatedAttributes = new ArrayList JavaDoc(2);
119         this.generatedAttributesRef = Collections
120                 .unmodifiableCollection(generatedAttributes);
121     }
122
123     /**
124      * Creates a named DbEntity.
125      */

126     public DbEntity(String JavaDoc name) {
127         this();
128         this.setName(name);
129     }
130
131     /**
132      * Prints itself as XML to the provided XMLEncoder.
133      *
134      * @since 1.1
135      */

136     public void encodeAsXML(XMLEncoder encoder) {
137         encoder.print("<db-entity name=\"");
138         encoder.print(Util.encodeXmlAttribute(getName()));
139         encoder.print('\"');
140
141         if (getSchema() != null && getSchema().trim().length() > 0) {
142             encoder.print(" schema=\"");
143             encoder.print(Util.encodeXmlAttribute(getSchema().trim()));
144             encoder.print('\"');
145         }
146
147         if (getCatalog() != null && getCatalog().trim().length() > 0) {
148             encoder.print(" catalog=\"");
149             encoder.print(Util.encodeXmlAttribute(getCatalog().trim()));
150             encoder.print('\"');
151         }
152
153         encoder.println('>');
154
155         encoder.indent(1);
156         encoder.print(getAttributeMap());
157
158         if (getPrimaryKeyGenerator() != null) {
159             getPrimaryKeyGenerator().encodeAsXML(encoder);
160         }
161
162         encoder.indent(-1);
163         encoder.println("</db-entity>");
164     }
165
166     /**
167      * Returns table name including schema, if present.
168      */

169     public String JavaDoc getFullyQualifiedName() {
170         return (schema != null) ? schema + '.' + getName() : getName();
171     }
172
173     /**
174      * Returns database schema of this table.
175      *
176      * @return table's schema, null if not set.
177      */

178     public String JavaDoc getSchema() {
179         return schema;
180     }
181
182     /**
183      * Sets the database schema name of the table described by this DbEntity.
184      */

185     public void setSchema(String JavaDoc schema) {
186         this.schema = schema;
187     }
188
189     /**
190      * Returns the catalog name of the table described by this DbEntity.
191      */

192     public String JavaDoc getCatalog() {
193         return catalog;
194     }
195
196     /**
197      * Sets the catalog name of the table described by this DbEntity.
198      */

199     public void setCatalog(String JavaDoc catalog) {
200         this.catalog = catalog;
201     }
202
203     /**
204      * Returns an unmodifiable list of DbAttributes representing the primary key of the
205      * table described by this DbEntity.
206      */

207     public List JavaDoc getPrimaryKey() {
208         return primaryKeyRef;
209     }
210
211     /**
212      * Returns an unmodifiable collection of DbAttributes that are generated by the
213      * database.
214      *
215      * @since 1.2
216      */

217     public Collection JavaDoc getGeneratedAttributes() {
218         return generatedAttributesRef;
219     }
220
221     public void addAttribute(Attribute attr) {
222         super.addAttribute(attr);
223         this.dbAttributeAdded(new AttributeEvent(this, attr, this, MapEvent.ADD));
224     }
225
226     /**
227      * Removes attribute from the entity, removes any relationship joins containing this
228      * attribute.
229      *
230      * @see org.objectstyle.cayenne.map.Entity#removeAttribute(String)
231      */

232     public void removeAttribute(String JavaDoc attrName) {
233         Attribute attr = getAttribute(attrName);
234         if (attr == null) {
235             return;
236         }
237
238         DataMap map = getDataMap();
239         if (map != null) {
240             Iterator JavaDoc ents = map.getDbEntities().iterator();
241             while (ents.hasNext()) {
242                 DbEntity ent = (DbEntity) ents.next();
243                 Iterator JavaDoc it = ent.getRelationships().iterator();
244                 while (it.hasNext()) {
245                     DbRelationship rel = (DbRelationship) it.next();
246                     Iterator JavaDoc joins = rel.getJoins().iterator();
247                     while (joins.hasNext()) {
248                         DbJoin join = (DbJoin) joins.next();
249                         if (join.getSource() == attr || join.getTarget() == attr) {
250                             joins.remove();
251                         }
252                     }
253                 }
254             }
255         }
256
257         super.removeAttribute(attrName);
258         this.dbAttributeRemoved(new AttributeEvent(this, attr, this, MapEvent.REMOVE));
259     }
260
261     public void clearAttributes() {
262         super.clearAttributes();
263         // post dummy event for no specific attribute
264
this.dbAttributeRemoved(new AttributeEvent(this, null, this, MapEvent.REMOVE));
265     }
266
267     public Iterator JavaDoc resolvePathComponents(Expression pathExp) throws ExpressionException {
268         if (pathExp.getType() != Expression.DB_PATH) {
269             throw new ExpressionException("Invalid expression type: '"
270                     + pathExp.expName()
271                     + "', DB_PATH is expected.");
272         }
273
274         return new PathIterator((String JavaDoc) pathExp.getOperand(0));
275     }
276
277     public void setPrimaryKeyGenerator(DbKeyGenerator primaryKeyGenerator) {
278         this.primaryKeyGenerator = primaryKeyGenerator;
279         if (primaryKeyGenerator != null) {
280             primaryKeyGenerator.setDbEntity(this);
281         }
282     }
283
284     public DbKeyGenerator getPrimaryKeyGenerator() {
285         return primaryKeyGenerator;
286     }
287
288     /**
289      * DbEntity property changed.
290      * May be name, attribute or relationship added or removed, etc.
291      * Attribute and relationship property changes are handled in
292      * respective listeners.
293      * @since 1.2
294      */

295     public void dbEntityChanged(EntityEvent e){
296         if ((e == null) || (e.getEntity() != this)) {
297             // not our concern
298
return;
299         }
300         
301         // handle entity name changes
302
if (e.getId() == EntityEvent.CHANGE && e.isNameChange()){
303             String JavaDoc newName = e.getNewName();
304             DataMap map = getDataMap();
305             if (map != null) {
306                 // handle all of the relationship target names that need to be changed
307
Iterator JavaDoc ents = map.getDbEntities().iterator();
308                 while (ents.hasNext()) {
309                    DbEntity dbe = (DbEntity) ents.next();
310                    Iterator JavaDoc rit = dbe.getRelationships().iterator();
311                    while (rit.hasNext()){
312                        DbRelationship rel = (DbRelationship) rit.next();
313                        if (rel.getTargetEntity() == this){
314                            rel.setTargetEntityName(newName);
315                        }
316                    }
317                 }
318                 // get all of the related object entities
319
ents = map.getMappedEntities(this).iterator();
320                 while (ents.hasNext()) {
321                    ObjEntity oe = (ObjEntity) ents.next();
322                    if (oe.getDbEntity() == this){
323                        oe.setDbEntityName(newName);
324                    }
325                 }
326             }
327         }
328     }
329
330     /** New entity has been created/added.*/
331     public void dbEntityAdded(EntityEvent e){
332         // does nothing currently
333
}
334
335     /** Entity has been removed.*/
336     public void dbEntityRemoved(EntityEvent e){
337         // does nothing currently
338
}
339
340     public void dbAttributeAdded(AttributeEvent e) {
341         this.handleAttributeUpdate(e);
342     }
343
344     public void dbAttributeChanged(AttributeEvent e) {
345         this.handleAttributeUpdate(e);
346     }
347
348     public void dbAttributeRemoved(AttributeEvent e) {
349         this.handleAttributeUpdate(e);
350     }
351
352     private void handleAttributeUpdate(AttributeEvent e) {
353         if ((e == null) || (e.getEntity() != this)) {
354             // not our concern
355
return;
356         }
357
358         // catch clearing (event with null ('any') DbAttribute)
359
Attribute attr = e.getAttribute();
360         if ((attr == null) && (this.attributes.isEmpty())) {
361             this.primaryKey.clear();
362             this.generatedAttributes.clear();
363             return;
364         }
365
366         // make sure we handle a DbAttribute
367
if (!(attr instanceof DbAttribute)) {
368             return;
369         }
370
371         DbAttribute dbAttr = (DbAttribute) attr;
372         
373         // handle attribute name changes
374
if (e.getId() == AttributeEvent.CHANGE && e.isNameChange()){
375             String JavaDoc oldName = e.getOldName();
376             String JavaDoc newName = e.getNewName();
377             
378             DataMap map = getDataMap();
379             if (map != null) {
380                 Iterator JavaDoc ents = map.getDbEntities().iterator();
381                 while (ents.hasNext()) {
382                     DbEntity ent = (DbEntity) ents.next();
383
384                     // handle all of the dependent object entity attribute changes
385
Iterator JavaDoc it = map.getMappedEntities(ent).iterator();
386                     while (it.hasNext()) {
387                         ObjEntity oe = (ObjEntity) it.next();
388                         Iterator JavaDoc ait = oe.getAttributes().iterator();
389                         while (ait.hasNext()) {
390                             ObjAttribute oa = (ObjAttribute) ait.next();
391                             if (oa.getDbAttribute() == dbAttr){
392                                 oa.setDbAttributeName(newName);
393                             }
394                         }
395                     }
396                     
397                     // handle all of the relationships / joins that use the changed attribute
398
it = ent.getRelationships().iterator();
399                     while (it.hasNext()) {
400                         DbRelationship rel = (DbRelationship) it.next();
401                         Iterator JavaDoc joins = rel.getJoins().iterator();
402                         while (joins.hasNext()) {
403                             DbJoin join = (DbJoin) joins.next();
404                             if (join.getSource() == dbAttr){
405                                 join.setSourceName(newName);
406                             }
407                             if (join.getTarget() == dbAttr){
408                                 join.setTargetName(newName);
409                             }
410                         }
411                     }
412                 }
413             }
414
415             // clear the attribute out of the collection
416
attributes.remove(oldName);
417             
418             // add the attribute back in with the new name
419
attributes.put(newName, dbAttr);
420         }
421
422         // handle PK refresh
423
if (primaryKey.contains(dbAttr) || dbAttr.isPrimaryKey()) {
424             switch (e.getId()) {
425                 case MapEvent.ADD:
426                     this.primaryKey.add(attr);
427                     break;
428
429                 case MapEvent.REMOVE:
430                     this.primaryKey.remove(attr);
431                     break;
432
433                 default:
434                     // generic update
435
this.primaryKey.clear();
436                     Iterator JavaDoc it = this.getAttributes().iterator();
437                     while (it.hasNext()) {
438                         DbAttribute dba = (DbAttribute) it.next();
439                         if (dba.isPrimaryKey()) {
440                             this.primaryKey.add(dba);
441                         }
442                     }
443             }
444         }
445
446         // handle generated key refresh
447
if (generatedAttributes.contains(dbAttr) || dbAttr.isGenerated()) {
448             switch (e.getId()) {
449                 case MapEvent.ADD:
450                     this.generatedAttributes.add(attr);
451                     break;
452
453                 case MapEvent.REMOVE:
454                     this.generatedAttributes.remove(attr);
455                     break;
456
457                 default:
458                     // generic update
459
this.generatedAttributes.clear();
460                     Iterator JavaDoc it = this.getAttributes().iterator();
461                     while (it.hasNext()) {
462                         DbAttribute dba = (DbAttribute) it.next();
463                         if (dba.isGenerated()) {
464                             this.generatedAttributes.add(dba);
465                         }
466                     }
467             }
468         }
469     }
470
471     /** Relationship property changed. */
472     public void dbRelationshipChanged(RelationshipEvent e){
473         if ((e == null) || (e.getEntity() != this)) {
474             // not our concern
475
return;
476         }
477
478         Relationship rel = e.getRelationship();
479         // make sure we handle a DbRelationship
480
if (!(rel instanceof DbRelationship)) {
481             return;
482         }
483
484         DbRelationship dbRel = (DbRelationship) rel;
485         
486         // handle relationship name changes
487
if (e.getId() == RelationshipEvent.CHANGE && e.isNameChange()){
488             String JavaDoc oldName = e.getOldName();
489             String JavaDoc newName = e.getNewName();
490             
491             DataMap map = getDataMap();
492             if (map != null) {
493                 // finds all object entities with a db relationship path to the renamed relationship
494
Iterator JavaDoc ents = map.getObjEntities().iterator();
495                 while (ents.hasNext()) {
496                    ObjEntity oe = (ObjEntity) ents.next();
497                    Iterator JavaDoc rit = oe.getRelationships().iterator();
498                    while (rit.hasNext()) {
499                         ObjRelationship or = (ObjRelationship) rit.next();
500                         // rename the db relationship path with the new name
501
if (or.getDbRelationshipPath().equals(oldName)){
502                             or.setDbRelationshipPath(newName);
503                         }
504                     }
505                 }
506             }
507             
508             // clear the relationship out of the collection
509
relationships.remove(oldName);
510             
511             // add the relationship back in with the new name
512
relationships.put(newName, dbRel);
513         }
514     }
515     
516     /** Relationship has been created/added.*/
517     public void dbRelationshipAdded(RelationshipEvent e){
518         // does nothing currently
519
}
520     
521     /** Relationship has been removed.*/
522     public void dbRelationshipRemoved(RelationshipEvent e){
523         // does nothing currently
524
}
525
526     /**
527      * Returns true if there is full replacement id is attached to an ObjectId. "Full" means
528      * that all PK columns are present and only PK columns are present.
529      *
530      * @since 1.2
531      */

532     public boolean isFullReplacementIdAttached(ObjectId id) {
533         if (!id.isReplacementIdAttached()) {
534             return false;
535         }
536
537         Map JavaDoc replacement = id.getReplacementIdMap();
538         Collection JavaDoc pk = getPrimaryKey();
539         if(pk.size() != replacement.size()) {
540             return false;
541         }
542         
543         Iterator JavaDoc it = pk.iterator();
544         while(it.hasNext()) {
545             DbAttribute attribute = (DbAttribute) it.next();
546             if(!replacement.containsKey(attribute.getName())) {
547                 return false;
548             }
549         }
550         
551         return true;
552     }
553
554     /**
555      * Transforms Expression rooted in this entity to an analogous expression rooted in
556      * related entity.
557      *
558      * @since 1.1
559      */

560     public Expression translateToRelatedEntity(
561             Expression expression,
562             String JavaDoc relationshipPath) {
563
564         if (expression == null) {
565             return null;
566         }
567
568         if (relationshipPath == null) {
569             return expression;
570         }
571
572         return expression.transform(new RelationshipPathConverter(relationshipPath));
573     }
574
575     final class RelationshipPathConverter implements Transformer {
576
577         String JavaDoc relationshipPath;
578
579         RelationshipPathConverter(String JavaDoc relationshipPath) {
580             this.relationshipPath = relationshipPath;
581         }
582
583         public Object JavaDoc transform(Object JavaDoc input) {
584             if (!(input instanceof Expression)) {
585                 return input;
586             }
587
588             Expression expression = (Expression) input;
589
590             if (expression.getType() != Expression.DB_PATH) {
591                 return input;
592             }
593
594             String JavaDoc path = (String JavaDoc) expression.getOperand(0);
595             String JavaDoc converted = translatePath(path);
596             Expression transformed = ExpressionFactory
597                     .expressionOfType(Expression.DB_PATH);
598             transformed.setOperand(0, converted);
599             return transformed;
600         }
601
602         String JavaDoc translatePath(String JavaDoc path) {
603
604             // algorithm to determine the translated path:
605
// 1. If relationship path equals to input, travel one step back, and then one
606
// step forward.
607
// 2. If input completely includes relationship path, use input's remaining
608
// tail.
609
// 3. If relationship path and input have none or some leading components in
610
// common,
611
// (a) strip common leading part;
612
// (b) reverse the remaining relationship part;
613
// (c) append remaining input to the reversed remaining relationship.
614

615             // case (1)
616
if (path.equals(relationshipPath)) {
617
618                 LinkedList JavaDoc finalPath = new LinkedList JavaDoc();
619                 Iterator JavaDoc it = resolvePathComponents(path);
620
621                 // just do one step back and one step forward to create correct joins...
622
// find last rel...
623
DbRelationship lastDBR = null;
624
625                 while (it.hasNext()) {
626                     // relationship path components must be DbRelationships
627
lastDBR = (DbRelationship) it.next();
628                 }
629
630                 if (lastDBR != null) {
631                     prependReversedPath(finalPath, lastDBR);
632                     appendPath(finalPath, lastDBR);
633                 }
634
635                 return convertToPath(finalPath);
636             }
637
638             // case (2)
639
String JavaDoc relationshipPathWithDot = relationshipPath + Entity.PATH_SEPARATOR;
640             if (path.startsWith(relationshipPathWithDot)) {
641                 return path.substring(relationshipPathWithDot.length());
642             }
643
644             // case (3)
645
Iterator JavaDoc pathIt = resolvePathComponents(path);
646             Iterator JavaDoc relationshipIt = resolvePathComponents(relationshipPath);
647
648             // for inserts from the both ends use LinkedList
649
LinkedList JavaDoc finalPath = new LinkedList JavaDoc();
650
651             while (relationshipIt.hasNext() && pathIt.hasNext()) {
652                 // relationship path components must be DbRelationships
653
DbRelationship nextDBR = (DbRelationship) relationshipIt.next();
654
655                 // expression components may be attributes or relationships
656
MapObject next = (MapObject) pathIt.next();
657
658                 if (nextDBR != next) {
659                     // found split point
660
// consume the last iteration components,
661
// then break out to finish the iterators independenly
662
prependReversedPath(finalPath, nextDBR);
663                     appendPath(finalPath, next);
664                     break;
665                 }
666
667                 break;
668             }
669
670             // append remainder of the relationship, reversing it
671
while (relationshipIt.hasNext()) {
672                 DbRelationship nextDBR = (DbRelationship) relationshipIt.next();
673                 prependReversedPath(finalPath, nextDBR);
674             }
675
676             while (pathIt.hasNext()) {
677                 // components may be attributes or relationships
678
MapObject next = (MapObject) pathIt.next();
679                 appendPath(finalPath, next);
680             }
681
682             return convertToPath(finalPath);
683         }
684
685         private String JavaDoc convertToPath(List JavaDoc path) {
686             StringBuffer JavaDoc converted = new StringBuffer JavaDoc();
687             int len = path.size();
688             for (int i = 0; i < len; i++) {
689                 if (i > 0) {
690                     converted.append(Entity.PATH_SEPARATOR);
691                 }
692
693                 converted.append(path.get(i));
694             }
695
696             return converted.toString();
697         }
698
699         private void prependReversedPath(LinkedList JavaDoc finalPath, DbRelationship relationship) {
700             DbRelationship revNextDBR = relationship.getReverseRelationship();
701
702             if (revNextDBR == null) {
703                 throw new CayenneRuntimeException(
704                         "Unable to find reverse DbRelationship for "
705                                 + relationship.getSourceEntity().getName()
706                                 + Entity.PATH_SEPARATOR
707                                 + relationship.getName()
708                                 + ".");
709             }
710
711             finalPath.addFirst(revNextDBR.getName());
712         }
713
714         private void appendPath(LinkedList JavaDoc finalPath, MapObject pathComponent) {
715             finalPath.addLast(pathComponent.getName());
716         }
717     }
718 }
Popular Tags