KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > CayenneDataObject


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;
57
58 import java.io.IOException JavaDoc;
59 import java.io.ObjectInputStream JavaDoc;
60 import java.io.ObjectOutputStream JavaDoc;
61 import java.lang.reflect.InvocationTargetException JavaDoc;
62 import java.util.HashMap JavaDoc;
63 import java.util.Iterator JavaDoc;
64 import java.util.List JavaDoc;
65 import java.util.Map JavaDoc;
66 import java.util.StringTokenizer JavaDoc;
67
68 import org.objectstyle.cayenne.access.DataContext;
69 import org.objectstyle.cayenne.access.DataNode;
70 import org.objectstyle.cayenne.access.types.ExtendedTypeMap;
71 import org.objectstyle.cayenne.conf.Configuration;
72 import org.objectstyle.cayenne.map.DbAttribute;
73 import org.objectstyle.cayenne.map.DbJoin;
74 import org.objectstyle.cayenne.map.DbRelationship;
75 import org.objectstyle.cayenne.map.EntityResolver;
76 import org.objectstyle.cayenne.map.ObjAttribute;
77 import org.objectstyle.cayenne.map.ObjEntity;
78 import org.objectstyle.cayenne.map.ObjRelationship;
79 import org.objectstyle.cayenne.util.PropertyComparator;
80 import org.objectstyle.cayenne.validation.BeanValidationFailure;
81 import org.objectstyle.cayenne.validation.ValidationFailure;
82 import org.objectstyle.cayenne.validation.ValidationResult;
83 import org.objectstyle.cayenne.xml.XMLDecoder;
84 import org.objectstyle.cayenne.xml.XMLEncoder;
85 import org.objectstyle.cayenne.xml.XMLSerializable;
86
87
88 /**
89  * A default implementation of DataObject interface. It is normally used as a superclass
90  * of Cayenne persistent objects.
91  *
92  * @author Andrei Adamchik
93  */

94 public class CayenneDataObject implements DataObject, XMLSerializable {
95
96     protected long snapshotVersion = DEFAULT_VERSION;
97
98     protected ObjectId objectId;
99     protected transient int persistenceState = PersistenceState.TRANSIENT;
100     protected transient DataContext dataContext;
101     protected Map JavaDoc values = new HashMap JavaDoc();
102
103     /**
104      * Returns a DataContext that holds this object. Object becomes assocaiated with a
105      * DataContext either when the object is fetched using a query, or when a new object
106      * is registered explicitly with a DataContext.
107      */

108     public DataContext getDataContext() {
109         return dataContext;
110     }
111
112     /**
113      * Initializes DataObject's persistence context.
114      */

115     public void setDataContext(DataContext dataContext) {
116         this.dataContext = dataContext;
117
118         if (dataContext == null) {
119             this.persistenceState = PersistenceState.TRANSIENT;
120         }
121     }
122     
123     /**
124      * Returns mapped ObjEntity for this object. If an object is transient or is not
125      * mapped returns null.
126      *
127      * @since 1.2
128      */

129     // TODO: maybe move to an already overloaded DataObject interface?
130
public ObjEntity getObjEntity() {
131         return (getDataContext() != null) ? getDataContext()
132                 .getEntityResolver()
133                 .lookupObjEntity(this) : null;
134     }
135
136     public ObjectId getObjectId() {
137         return objectId;
138     }
139
140     public void setObjectId(ObjectId objectId) {
141         this.objectId = objectId;
142     }
143
144     public int getPersistenceState() {
145         return persistenceState;
146     }
147
148     public void setPersistenceState(int persistenceState) {
149         this.persistenceState = persistenceState;
150
151         if (persistenceState == PersistenceState.HOLLOW) {
152             values.clear();
153         }
154     }
155
156     public Object JavaDoc readNestedProperty(String JavaDoc path) {
157         Object JavaDoc object = null;
158         CayenneDataObject dataObject = this;
159         String JavaDoc[] tokenized = tokenizePath(path);
160         int length = tokenized.length;
161
162         for (int i = 0; i < length; i++) {
163
164             object = dataObject.readSimpleProperty(tokenized[i]);
165
166             if (object == null) {
167                 return null;
168             }
169             else if (object instanceof CayenneDataObject) {
170                 dataObject = (CayenneDataObject) object;
171             }
172             else if (i + 1 < length) {
173                 throw new CayenneRuntimeException("Invalid path: " + path);
174             }
175         }
176
177         return object;
178     }
179
180     private static final String JavaDoc[] tokenizePath(String JavaDoc path) {
181         if (path == null) {
182             throw new NullPointerException JavaDoc("Null property path.");
183         }
184
185         if (path.length() == 0) {
186             throw new IllegalArgumentException JavaDoc("Empty property path.");
187         }
188
189         // take a shortcut for simple properties
190
if (path.indexOf(".") < 0) {
191             return new String JavaDoc[] {
192                 path
193             };
194         }
195
196         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(path, ".");
197         int length = tokens.countTokens();
198         String JavaDoc[] tokenized = new String JavaDoc[length];
199         for (int i = 0; i < length; i++) {
200             tokenized[i] = tokens.nextToken();
201         }
202
203         return tokenized;
204     }
205
206     private final Object JavaDoc readSimpleProperty(String JavaDoc property) {
207         // side effect - resolves HOLLOW object
208
Object JavaDoc object = readProperty(property);
209
210         // if a null value is returned, there is still a chance to
211
// find a non-persistent property via reflection
212
if (object == null && !values.containsKey(property)) {
213             try {
214                 object = PropertyComparator.readProperty(property, this);
215             }
216             catch (IllegalAccessException JavaDoc e) {
217                 throw new CayenneRuntimeException("Error reading property '"
218                         + property
219                         + "'.", e);
220             }
221             catch (InvocationTargetException JavaDoc e) {
222                 throw new CayenneRuntimeException("Error reading property '"
223                         + property
224                         + "'.", e);
225             }
226             catch (NoSuchMethodException JavaDoc e) {
227                 // ignoring, no such property exists
228
}
229         }
230
231         return object;
232     }
233
234     /**
235      * @since 1.1
236      */

237     public void resolveFault() {
238         if (getPersistenceState() == PersistenceState.HOLLOW && dataContext != null) {
239             dataContext.getObjectStore().resolveHollow(this);
240             if (getPersistenceState() != PersistenceState.COMMITTED) {
241                 throw new FaultFailureException(
242                         "Error resolving fault, no matching row exists in the database for ObjectId: "
243                                 + getObjectId());
244             }
245         }
246     }
247
248     public Object JavaDoc readProperty(String JavaDoc propName) {
249         resolveFault();
250
251         Object JavaDoc object = readPropertyDirectly(propName);
252
253         // must resolve faults immediately
254
if (object instanceof Fault) {
255             object = ((Fault) object).resolveFault(this, propName);
256             writePropertyDirectly(propName, object);
257         }
258
259         return object;
260     }
261
262     public Object JavaDoc readPropertyDirectly(String JavaDoc propName) {
263         return values.get(propName);
264     }
265
266     public void writeProperty(String JavaDoc propName, Object JavaDoc val) {
267         resolveFault();
268
269         // 1. retain object snapshot to allow clean changes tracking
270
// 2. change object state
271
if (persistenceState == PersistenceState.COMMITTED) {
272             persistenceState = PersistenceState.MODIFIED;
273             dataContext.getObjectStore().retainSnapshot(this);
274         }
275         // else....
276
// other persistence states can't be changed to MODIFIED
277

278         writePropertyDirectly(propName, val);
279     }
280
281     public void writePropertyDirectly(String JavaDoc propName, Object JavaDoc val) {
282         values.put(propName, val);
283     }
284
285     public void removeToManyTarget(String JavaDoc relName, DataObject value, boolean setReverse) {
286
287         ObjRelationship relationship = this.getRelationshipNamed(relName);
288
289         if (relationship == null) {
290             throw new NullPointerException JavaDoc("Can't find relationship: " + relName);
291         }
292
293         // if "setReverse" is false, avoid unneeded processing of flattened relationship
294
getDataContext().getObjectStore().objectRelationshipUnset(
295                 this,
296                 value,
297                 relationship,
298                 setReverse);
299
300         // Now do the rest of the normal handling (regardless of whether it was
301
// flattened or not)
302
List JavaDoc relList = (List JavaDoc) readProperty(relName);
303         relList.remove(value);
304         if (persistenceState == PersistenceState.COMMITTED) {
305             persistenceState = PersistenceState.MODIFIED;
306         }
307
308         if (value != null && setReverse) {
309             unsetReverseRelationship(relName, value);
310         }
311     }
312
313     public void addToManyTarget(String JavaDoc relName, DataObject value, boolean setReverse) {
314         if (value == null) {
315             throw new NullPointerException JavaDoc("Attempt to add null target DataObject.");
316         }
317
318         willConnect(relName, value);
319         
320         ObjRelationship relationship = this.getRelationshipNamed(relName);
321         if (relationship == null) {
322             throw new NullPointerException JavaDoc("Can't find relationship: " + relName);
323         }
324
325         getDataContext().getObjectStore().objectRelationshipSet(
326                 this,
327                 value,
328                 relationship,
329                 setReverse);
330
331         // Now do the rest of the normal handling (regardless of whether it was
332
// flattened or not)
333
List JavaDoc list = (List JavaDoc) readProperty(relName);
334         list.add(value);
335         if (persistenceState == PersistenceState.COMMITTED) {
336             persistenceState = PersistenceState.MODIFIED;
337
338             // retaining a snapshot here is wasteful, but we have to do this for
339
// consistency (see CAY-213)
340
dataContext.getObjectStore().retainSnapshot(this);
341         }
342
343         if (value != null && setReverse) {
344             setReverseRelationship(relName, value);
345         }
346     }
347
348     public void setToOneTarget(
349             String JavaDoc relationshipName,
350             DataObject value,
351             boolean setReverse) {
352
353         willConnect(relationshipName, value);
354
355         Object JavaDoc oldTarget = readProperty(relationshipName);
356         if (oldTarget == value) {
357             return;
358         }
359
360         ObjRelationship relationship = this
361                 .getRelationshipNamed(relationshipName);
362         if (relationship == null) {
363             throw new NullPointerException JavaDoc("Can't find relationship: "
364                     + relationshipName);
365         }
366
367         // if "setReverse" is false, avoid unneeded processing of flattened
368
// relationship
369
getDataContext().getObjectStore().objectRelationshipSet(this, value,
370                 relationship, setReverse);
371
372         if (setReverse) {
373             // unset old reverse relationship
374
if (oldTarget instanceof DataObject) {
375                 unsetReverseRelationship(relationshipName,
376                         (DataObject) oldTarget);
377             }
378
379             // set new reverse relationship
380
if (value != null) {
381                 setReverseRelationship(relationshipName, value);
382             }
383         }
384
385         writeProperty(relationshipName, value);
386     }
387     
388     /**
389      * Called before establishing a relationship with another object. Applies
390      * "persistence by reachability" logic, pulling one of the two objects to a
391      * DataConext of another object in case one of the objects is transient. If
392      * both objects are persistent, and they don't have the same DataContext,
393      * CayenneRuntimeException is thrown.
394      *
395      * @since 1.2
396      */

397     protected void willConnect(String JavaDoc relationshipName, DataObject dataObject) {
398         // first handle most common case - both objects are in the same
399
// DataContext or target is null
400
if (dataObject == null
401                 || this.getDataContext() == dataObject.getDataContext()) {
402             return;
403         } else if (this.getDataContext() == null
404                 && dataObject.getDataContext() != null) {
405             dataObject.getDataContext().registerNewObject(this);
406         } else if (this.getDataContext() != null
407                 && dataObject.getDataContext() == null) {
408             this.getDataContext().registerNewObject(dataObject);
409         } else {
410             throw new CayenneRuntimeException(
411                     "Cannot set object as destination of relationship "
412                             + relationshipName
413                             + " because it is in a different DataContext");
414         }
415     }
416
417     private ObjRelationship getRelationshipNamed(String JavaDoc relName) {
418         return (ObjRelationship) dataContext
419                 .getEntityResolver()
420                 .lookupObjEntity(this)
421                 .getRelationship(relName);
422     }
423
424     /**
425      * Initializes reverse relationship from object <code>val</code> to this object.
426      *
427      * @param relName name of relationship from this object to <code>val</code>.
428      */

429     protected void setReverseRelationship(String JavaDoc relName, DataObject val) {
430         ObjRelationship rel = (ObjRelationship) dataContext
431                 .getEntityResolver()
432                 .lookupObjEntity(objectId.getObjectClass())
433                 .getRelationship(relName);
434         ObjRelationship revRel = rel.getReverseRelationship();
435         if (revRel != null) {
436             if (revRel.isToMany())
437                 val.addToManyTarget(revRel.getName(), this, false);
438             else
439                 val.setToOneTarget(revRel.getName(), this, false);
440         }
441     }
442
443     /**
444      * Removes current object from reverse relationship of object <code>val</code> to
445      * this object.
446      */

447     protected void unsetReverseRelationship(String JavaDoc relName, DataObject val) {
448         Class JavaDoc aClass = objectId.getObjectClass();
449         EntityResolver resolver = dataContext.getEntityResolver();
450         ObjEntity entity = resolver.lookupObjEntity(aClass);
451
452         if (entity == null) {
453             String JavaDoc className = (aClass != null) ? aClass.getName() : "<null>";
454             throw new IllegalStateException JavaDoc("DataObject's class is unmapped: "
455                     + className);
456         }
457
458         ObjRelationship rel = (ObjRelationship) entity.getRelationship(relName);
459         ObjRelationship revRel = rel.getReverseRelationship();
460         if (revRel != null) {
461             if (revRel.isToMany())
462                 val.removeToManyTarget(revRel.getName(), this, false);
463             else
464                 val.setToOneTarget(revRel.getName(), null, false);
465         }
466     }
467
468     /**
469      * A variation of "toString" method, that may be more efficient in some cases. For
470      * example when printing a list of objects into the same String.
471      */

472     public StringBuffer JavaDoc toStringBuffer(StringBuffer JavaDoc buf, boolean fullDesc) {
473         // log all properties
474
buf.append('{');
475
476         if (fullDesc)
477             appendProperties(buf);
478
479         buf.append("<oid: ").append(objectId).append("; state: ").append(
480                 PersistenceState.persistenceStateName(persistenceState)).append(">}\n");
481         return buf;
482     }
483
484     protected void appendProperties(StringBuffer JavaDoc buf) {
485         buf.append("[");
486         Iterator JavaDoc it = values.keySet().iterator();
487         while (it.hasNext()) {
488             Object JavaDoc key = it.next();
489             buf.append('\t').append(key).append(" => ");
490             Object JavaDoc val = values.get(key);
491
492             if (val instanceof CayenneDataObject) {
493                 ((CayenneDataObject) val).toStringBuffer(buf, false);
494             }
495             else if (val instanceof List JavaDoc) {
496                 buf.append('(').append(val.getClass().getName()).append(')');
497             }
498             else
499                 buf.append(val);
500
501             buf.append('\n');
502         }
503
504         buf.append("]");
505     }
506
507     public String JavaDoc toString() {
508         return toStringBuffer(new StringBuffer JavaDoc(), true).toString();
509     }
510
511     /**
512      * Default implementation does nothing.
513      *
514      * @see org.objectstyle.cayenne.DataObject#fetchFinished()
515      */

516     public void fetchFinished() {
517     }
518
519     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
520         out.writeInt(persistenceState);
521
522         switch (persistenceState) {
523             //New, modified or transient or deleted - write the whole shebang
524
//The other states (committed, hollow) all need just ObjectId
525
case PersistenceState.TRANSIENT:
526             case PersistenceState.NEW:
527             case PersistenceState.MODIFIED:
528             case PersistenceState.DELETED:
529                 out.writeObject(values);
530                 break;
531         }
532
533         out.writeObject(objectId);
534     }
535
536     private void readObject(ObjectInputStream JavaDoc in) throws IOException JavaDoc,
537             ClassNotFoundException JavaDoc {
538         this.persistenceState = in.readInt();
539
540         switch (persistenceState) {
541             case PersistenceState.TRANSIENT:
542             case PersistenceState.NEW:
543             case PersistenceState.MODIFIED:
544             case PersistenceState.DELETED:
545                 values = (Map JavaDoc) in.readObject();
546                 break;
547             case PersistenceState.COMMITTED:
548             case PersistenceState.HOLLOW:
549                 this.persistenceState = PersistenceState.HOLLOW;
550                 //props will be populated when required (readProperty called)
551
values = new HashMap JavaDoc();
552                 break;
553         }
554
555         this.objectId = (ObjectId) in.readObject();
556
557         // DataContext will be set *IF* the DataContext it came from is also
558
// deserialized. Setting of DataContext is handled by the DataContext
559
// itself
560
}
561
562     /**
563      * Returns a version of a DataRow snapshot that was used to create this object.
564      *
565      * @since 1.1
566      */

567     public long getSnapshotVersion() {
568         return snapshotVersion;
569     }
570
571     /**
572      * @since 1.1
573      */

574     public void setSnapshotVersion(long snapshotVersion) {
575         this.snapshotVersion = snapshotVersion;
576     }
577
578     /**
579      * Performs property validation of the object, appending any validation failures to
580      * the provided validationResult object. This method is invoked from "validateFor.."
581      * before committing a NEW or MODIFIED object to the database. Validation includes
582      * checking for null values and value sizes. CayenneDataObject subclasses may override
583      * this method, calling super.
584      *
585      * @since 1.1
586      */

587     protected void validateForSave(ValidationResult validationResult) {
588
589         ObjEntity objEntity = getDataContext().getEntityResolver().lookupObjEntity(this);
590         if (objEntity == null) {
591             throw new CayenneRuntimeException(
592                     "No ObjEntity mapping found for DataObject " + getClass().getName());
593         }
594         
595         DataNode node = getDataContext().lookupDataNode(objEntity.getDataMap());
596         if (node == null) {
597             throw new CayenneRuntimeException("No DataNode found for objEntity: "
598                     + objEntity.getName());
599         }
600         
601         ExtendedTypeMap types = node.getAdapter().getExtendedTypes();
602
603         // validate mandatory attributes
604

605         // handling a special case - meaningful mandatory FK... defer failures until
606
// relationship validation is done... This is just a temporary solution, as
607
// handling meaningful keys within the object lifecycle requires something more,
608
// namely read/write methods for relationships and direct values should be
609
// synchronous with each other..
610
Map JavaDoc failedDbAttributes = null;
611
612         Iterator JavaDoc attributes = objEntity.getAttributes().iterator();
613         while (attributes.hasNext()) {
614             ObjAttribute objAttribute = (ObjAttribute) attributes.next();
615             DbAttribute dbAttribute = objAttribute.getDbAttribute();
616
617             Object JavaDoc value = this.readPropertyDirectly(objAttribute.getName());
618             if (dbAttribute.isMandatory()) {
619                 ValidationFailure failure = BeanValidationFailure.validateNotNull(
620                         this,
621                         objAttribute.getName(),
622                         value);
623
624                 if (failure != null) {
625
626                     if (failedDbAttributes == null) {
627                         failedDbAttributes = new HashMap JavaDoc();
628                     }
629
630                     failedDbAttributes.put(dbAttribute.getName(), failure);
631                     continue;
632                 }
633             }
634
635             if (value != null) {
636
637                 // TODO: should we pass null values for validation as well?
638
// if so, class can be obtained from ObjAttribute...
639

640                 types.getRegisteredType(value.getClass()).validateProperty(
641                         this,
642                         objAttribute.getName(),
643                         value,
644                         dbAttribute,
645                         validationResult);
646             }
647         }
648
649         // validate mandatory relationships
650
Iterator JavaDoc relationships = objEntity.getRelationships().iterator();
651         while (relationships.hasNext()) {
652             ObjRelationship relationship = (ObjRelationship) relationships.next();
653
654             if (relationship.isSourceIndependentFromTargetChange()) {
655                 continue;
656             }
657
658             List JavaDoc dbRels = relationship.getDbRelationships();
659             if (dbRels.isEmpty()) {
660                 // Wha?
661
continue;
662             }
663
664             // if db relationship is not based on a PK and is based on mandatory
665
// attributes, see if we have a target object set
666
boolean validate = false;
667             DbRelationship dbRelationship = (DbRelationship) dbRels.get(0);
668             Iterator JavaDoc joins = dbRelationship.getJoins().iterator();
669             while (joins.hasNext()) {
670                 DbJoin join = (DbJoin) joins.next();
671                 DbAttribute source = join.getSource();
672
673                 if (source.isMandatory()) {
674                     validate = true;
675
676                     // clear attribute failures...
677
if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) {
678                         failedDbAttributes.remove(source.getName());
679
680                         // loop through all joins if there were previous mandatory
681
// attribute failures.... otherwise we can safely break away
682
if (!failedDbAttributes.isEmpty()) {
683                             continue;
684                         }
685                     }
686
687                     break;
688                 }
689             }
690
691             if (validate) {
692                 Object JavaDoc value = this.readPropertyDirectly(relationship.getName());
693                 ValidationFailure failure = BeanValidationFailure.validateNotNull(
694                         this,
695                         relationship.getName(),
696                         value);
697
698                 if (failure != null) {
699                     validationResult.addFailure(failure);
700                     continue;
701                 }
702             }
703
704         }
705
706         // deal with previously found attribute failures...
707
if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) {
708             Iterator JavaDoc failedAttributes = failedDbAttributes.values().iterator();
709             while (failedAttributes.hasNext()) {
710                 validationResult.addFailure((ValidationFailure) failedAttributes.next());
711             }
712         }
713     }
714
715     /**
716      * Calls {@link #validateForSave(ValidationResult)}. CayenneDataObject subclasses may
717      * override it providing validation logic that should be executed for the newly
718      * created objects before saving them.
719      *
720      * @since 1.1
721      */

722     public void validateForInsert(ValidationResult validationResult) {
723         validateForSave(validationResult);
724     }
725
726     /**
727      * Calls {@link #validateForSave(ValidationResult)}. CayenneDataObject subclasses may
728      * override it providing validation logic that should be executed for the modified
729      * objects before saving them.
730      *
731      * @since 1.1
732      */

733     public void validateForUpdate(ValidationResult validationResult) {
734         validateForSave(validationResult);
735     }
736
737     /**
738      * This implementation does nothing. CayenneDataObject subclasses may override it
739      * providing validation logic that should be executed for the deleted objects before
740      * committing them.
741      *
742      * @since 1.1
743      */

744     public void validateForDelete(ValidationResult validationResult) {
745         // does nothing
746
}
747     
748
749
750     /**
751      * Encodes object to XML using provided encoder.
752      *
753      * @since 1.2
754      */

755     public void encodeAsXML(XMLEncoder encoder) {
756         EntityResolver er = getDataContext().getEntityResolver();
757         ObjEntity object = er.lookupObjEntity(getClass());
758
759         // TODO: "split" is JDK 1.4... most of Cayenne is still 1.3 compatible
760
String JavaDoc[] fields = this.getClass().getName().split("\\.");
761         encoder.setRoot(fields[fields.length - 1], this.getClass().getName());
762
763         for (Iterator JavaDoc it = object.getDeclaredAttributes().iterator(); it.hasNext();) {
764             ObjAttribute att = (ObjAttribute) it.next();
765             String JavaDoc name = att.getName();
766             encoder.encodeProperty(name, readNestedProperty(name));
767         }
768     }
769     
770     public void decodeFromXML(XMLDecoder decoder) {
771         ObjEntity object = null;
772         
773         // TODO: relying on singleton Configuration is a bad idea...
774
// Probably decoder itself can optionally store a DataContext or an EntityResolver
775
// to provide "context" appropriate for a given environment
776
for (Iterator JavaDoc it = Configuration.getSharedConfiguration().getDomain().getDataNodes().iterator(); it.hasNext();) {
777             DataNode dn = (DataNode) it.next();
778             
779             EntityResolver er = dn.getEntityResolver();
780             object = er.lookupObjEntity(getClass());
781             
782             if (null != object) {
783                 break;
784             }
785         }
786
787         for (Iterator JavaDoc it = object.getDeclaredAttributes().iterator(); it.hasNext();) {
788             ObjAttribute att = (ObjAttribute) it.next();
789             String JavaDoc name = att.getName();
790             writeProperty(name, decoder.decodeObject(name));
791         }
792     }
793
794     /**
795      * Returns this object's ObjectId.
796      *
797      * @since 1.2
798      */

799     public Object JavaDoc getOid() {
800         return getObjectId();
801     }
802
803     /**
804      * Sets this object's ObjectId.
805      *
806      * @since 1.2
807      */

808     public void setOid(Object JavaDoc oid) {
809         if (oid == null || oid instanceof ObjectId) {
810             setObjectId((ObjectId) oid);
811         }
812
813         throw new IllegalArgumentException JavaDoc(
814                 "CayenneDataObject only supports ObjectId ids, got: " + oid);
815     }
816     
817     /**
818      * Returns this object's DataContext.
819      *
820      * @since 1.2
821      */

822     public ObjectContext getObjectContext() {
823         return (dataContext == null || dataContext instanceof ObjectContext)
824                 ? (ObjectContext) dataContext
825                 : null;
826     }
827
828     /**
829      * @since 1.2
830      */

831     public void setObjectContext(ObjectContext objectContext) {
832         if (objectContext == null || objectContext instanceof DataContext) {
833             setDataContext((DataContext) objectContext);
834         }
835
836         throw new IllegalArgumentException JavaDoc(
837                 "CayenneDataObject only supports DataContext for ObjectContext, got: "
838                         + objectContext);
839     }
840 }
Popular Tags