Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: nectar/record/Record.java


1   /*
2       Copyright (C) 2003  Kai Schutte
3    
4       This program is free software; you can redistribute it and/or modify
5       it under the terms of the GNU General Public License as published by
6       the Free Software Foundation; either version 2 of the License, or
7       (at your option) any later version.
8    
9       This program is distributed in the hope that it will be useful,
10      but WITHOUT ANY WARRANTY; without even the implied warranty of
11      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12      GNU General Public License for more details.
13   
14      You should have received a copy of the GNU General Public License
15      along with this program; if not, write to the Free Software
16      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17   
18   * Record.java
19   *
20   * Created on March 7, 2003, 6:54 PM
21   */
22  
23  package nectar.record;
24  
25  import nectar.record.datatypes.*;
26  import java.lang.reflect.Field;
27  import java.util.Map;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Vector;
31  import java.util.Collection;
32  import java.util.Iterator;
33  import java.util.Date;
34  import java.util.Calendar;
35  
36  import java.io.Serializable;
37  import java.beans.PropertyChangeSupport;
38  import java.beans.PropertyChangeListener;
39  import java.beans.PropertyChangeEvent;
40  
41  import nectar.view.RecordView;
42  
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  
46  
47  /** The abstract base model element for data records.<p>
48   *
49   * To retrieve records from the datasource, you must first know their ID numbers.
50   * To do this, see {@link nectar.data.DataAdapterService} on how to build and
51   * execute queries that will return relevant ID numbers.
52   * Once you have one or more ID number, you should retrieve records
53   * through the {@link nectar.services.RecordService} methods which return record instances.<p>
54   *
55   *
56   * You can then use all the <code>Record.get[*]()</code> methods on the record to retrieve raw data objects from the record,
57   * or retrieve a {@link nectar.view.RecordView} through the <code>Record.getView()</code> method.<p>
58   *
59   *
60   * Use the set*() methods to modify the data in a Record instance, followed by a {@link nectar.services.RecordService#commit} call to persist your changes to the datasource.<p>
61   *
62   * To create a new record, call the <code>Record.setupCreate()</code> method on a newly instantiated Record, use the <code>Record.set*()</code> to fill the data, and finally commit it to the database. A newly created record will only have an ID number once it has been committed.
63   *
64   *
65   * @author Kai Schutte skander@skander.com
66   * @see nectar.data.DataAdapterService
67   * @see nectar.services.RecordService
68   * @see nectar.view.RecordView
69   */
70  public abstract class Record implements Serializable {
71      private HashMap propertyChangeMap = new HashMap();
72      private static Log log = LogFactory.getLog(Record.class);
73      /** The Collection of tables that this record can build itself from.
74       */
75      protected Vector dataTables = new Vector();
76      
77      /** The name of the top-level record table in the datasource.
78       */
79      public static String TABLE = "record";
80      
81      /** Field name of the id field in the datasource.
82       */
83      public static final String FIELD_RECORD_ID = "r_id";
84      /** Field name of the project ID field in the datasource.
85       */
86      public static final String FIELD_PROJECT = "r_project";
87      /** Field name of the implementation id field in the datasource.
88       */
89      public static final String FIELD_CHILD_ID = "id";
90      /** Field name of the owner field in the datasource.
91       */
92      public static final String FIELD_OWNER = "r_owner";
93      /** Field name of the type field in the datasource.
94       */
95      public static final String FIELD_TYPE = "r_type";
96      /** Field name of the createdBy field in the datasource.
97       */
98      public static final String FIELD_CREATED_DATE = "r_created_date";
99      /** Field name of the modifiedDate field in the datasource.
100      */
101     public static final String FIELD_MODIFIED_DATE = "r_modified_date";
102     /** Field name of the createdDate field in the datasource.
103      */
104     public static final String FIELD_CREATED_BY = "r_created_by";
105     /** Field name of the status field in the datasource.
106      */
107     public static final String FIELD_STATUS = "r_status";
108     /** Field name of the order_by field in the datasource.
109      */
110     public static final String FIELD_ORDER_BY = "r_order_by";
111     
112     
113     /** Array of field names defined for all records.
114      */
115     public static final String[] TOP_LEVEL_FIELD_NAMES = {FIELD_RECORD_ID,FIELD_PROJECT,FIELD_OWNER,FIELD_TYPE,FIELD_CREATED_DATE,FIELD_MODIFIED_DATE,FIELD_CREATED_BY,FIELD_STATUS, FIELD_ORDER_BY};
116     
117     private RecordInteger id;
118     private RecordInteger project;
119     private RecordInteger owner;
120     private RecordTypeEnum type;
121     private RecordDate createdDate;
122     private RecordDate modifiedDate;
123     private RecordInteger createdBy;
124     private RecordStatusEnum status;
125     private RecordInteger orderBy;
126     protected Vector fields;
127     
128     /** Creates a new, empty instance of a Record model. To load records, you should use <CODE>RecordService.get[Record](id);</CODE> instead of this constructor. To create a new Record to be inserted into the datasource, you should use the constructors of the child class of the Record class, then call <code>setupCreate()</code>
129      * @see nectar.services.RecordService
130      */
131     protected Record() {
132         dataTables.add(Record.TABLE);
133         id = new RecordInteger(FIELD_RECORD_ID, false);
134         project = new RecordInteger(FIELD_PROJECT, true);
135         owner = new RecordInteger(FIELD_OWNER, true);
136         type = new RecordTypeEnum(FIELD_TYPE);
137         createdDate = new RecordDate(FIELD_CREATED_DATE, false);
138         modifiedDate = new RecordDate(FIELD_MODIFIED_DATE, false);
139         createdBy = new RecordInteger(FIELD_CREATED_BY, false);
140         status = new RecordStatusEnum(FIELD_STATUS);
141         orderBy = new RecordInteger(FIELD_ORDER_BY, false);
142         fields = new Vector(20);
143         fields.add(id);
144         fields.add(project);
145         fields.add(owner);
146         fields.add(type);
147         fields.add(createdDate);
148         fields.add(modifiedDate);
149         fields.add(createdBy);
150         fields.add(status);
151         fields.add(orderBy);
152     }
153     
154     public final Record copy() {
155         Record newRecord;
156         try {
157             newRecord = (Record)this.getClass().newInstance();
158         } catch (Exception e) { // shouldn't occur
159             e.printStackTrace();
160             return null;
161         }
162         newRecord.load(this.toObjectMap());
163         return newRecord;
164     }
165     
166     /** Returns the type String for this Record.
167      * @return The type String for this Record.
168      */
169     public abstract String getRecordType();
170     
171     /** Returns a Collection of Strings that lists the datasource names of the tables required to build this Record.
172      * @return The list of table names.
173      */
174     public final Collection getTables() {
175         return dataTables;
176     }
177     
178     protected Collection getFields() {
179         Class recordClass = this.getClass();
180         Vector fields = new Vector();
181         while (recordClass != null) {
182             Field[] classFields = recordClass.getDeclaredFields();
183             for (int i=0; i<classFields.length; i++) {
184                 Class fieldClass = classFields[i].getType();
185                 Class fieldDeclaringClass = fieldClass.getSuperclass();
186                 if (fieldDeclaringClass != null) {
187                     try {
188                         if (fieldDeclaringClass.getName() == "nectar.record.datatypes.RecordDataElement") {
189                             fields.add(classFields[i].get(this));
190                         }
191                     } catch (IllegalAccessException e) {
192                         if (log.isErrorEnabled()) {
193                             log.error("While gathering field values in getFields(), field: \""+classFields[i].getName()+"\" was inaccessible. Ensure the field is protected, not private.", e);
194                         }
195                     } 
196                 }
197             }
198             recordClass = recordClass.getSuperclass();
199         }
200         return fields;
201     }
202     
203     /** Returns the list of field names required to build this record.
204      * @return The list of field names required to build this record.
205      */
206     public final Collection getFieldNames() {
207         Collection fields = getFields();
208         Vector fieldNames = new Vector();
209         for (Iterator iter = fields.iterator(); iter.hasNext(); ) {
210             fieldNames.add(((RecordDataElement)iter.next()).getFieldName());
211         }
212         return fieldNames;
213     }
214     
215     
216     /** Loads the required data from the datasource into this record instance. To load a record you should use the <CODE>RecordService.getRecord(Long id)</CODE> method. Using this method directly will harm performance and is very insecure, since all the caching, distributed synchronization, etc. will be bypassed.
217      * @param values The [field_name] => [(Object) value] map to load into this record.
218      */
219     public final void load(Map values) {
220         Collection fields = this.getFields();
221         for (Iterator iter = fields.iterator(); iter.hasNext(); ) {
222             RecordDataElement field = (RecordDataElement)iter.next();
223             Object dataObj = values.get(field.getFieldName());
224             if (field == this.id && dataObj == null) continue;
225             try {
226                 field.assignDataValue(dataObj);
227             } catch (ClassCastException e) {
228                 throw new ClassCastException("NECTAR: Record Programming error: on loading record of type "+this.getClass().getName()+" - field: " +field.getFieldName()+" ClassCastException occured with message: "+e.getMessage());
229             }
230         }
231     }
232     
233     /** Returns a [field_name] => [(String) value] HashMap of this record.
234      * @return the generated map.
235      */
236     public final java.util.HashMap toMap() {
237         Collection fields = getFields();
238         java.util.HashMap map = new java.util.HashMap();
239         for (Iterator iter=fields.iterator(); iter.hasNext(); ) {
240             RecordDataElement field = (RecordDataElement)iter.next();
241             map.put(field.getFieldName(), field.getStringValue());
242         }
243         return map;
244     }
245     
246     public final java.util.HashMap toObjectMap() {
247         Collection fields = this.getFields();
248         java.util.HashMap map = new java.util.HashMap();
249         for (Iterator iter=fields.iterator(); iter.hasNext(); ) {
250             RecordDataElement field = (RecordDataElement)iter.next();
251             map.put(field.getFieldName(), field.getDataObject());
252         }
253         return map;
254     }
255     
256     /** loads all the default and system values into an newly instantiated, empty Record instance.
257      * @param creator The ID number of the PersonRecord creating this record. The System's creator ID is 0.
258      */
259     protected void setupCreate(Long project, Long creator) {
260         this.project.assignDataValue(project);
261         type.assignDataValue(this.getRecordType());
262         createdDate.assignDataValue(Calendar.getInstance().getTime());
263         modifiedDate.assignDataValue(Calendar.getInstance().getTime());
264         createdBy.assignDataValue(creator);
265         status.assignDataValue("active");
266         orderBy.assignDataValue(new Long(0));
267     }
268     
269     /** Returns the ID number of this record.
270      * @return The ID number of this record or null if the recorded hasn't been loaded.
271      */
272     public final Long getId() {
273         return id.getDataValue();
274     }
275     
276     /** Returns the project ID number of this record.
277      * @return The ID number of the project that this record is linked to.
278      */
279     public final Long getProject() {
280         return project.getDataValue();
281     }
282     
283     
284     /** Sets the project ID for this record. 
285      * @param value The project ID number of this record.
286      * @throws RecordInvalidInputException thrown if given id is null.
287      */
288     public final void setProject(Long value) throws RecordInvalidInputException {
289         if (value == null) throw new RecordInvalidInputException();
290         this.project.validate(value);
291     }
292         
293     
294     /** Sets the ID for this record. Note that you can't modify the id number of a record, only set it on a newly created instance.
295      * @param value The ID number of this record.
296      * @throws RecordInvalidInputException thrown when the ID number is already set for this record.
297      */
298     public final void setId(Long value) throws RecordInvalidInputException {
299         if (this.getId() == null)
300             this.id.assignDataValue(value);
301         else
302             throw new RecordInvalidInputException("ID already set!");
303     }
304     
305     /** Returns the ID number of the record that owns this record or null if this record isn't owned by any other record.
306      * @return The ID number of the record that owns this record, may be null.
307      */
308     public final Long getOwner() {
309         return owner.getDataValue();
310     }
311     
312     /** Sets the owner ID number of this record.
313      * @param value The ID number of the record that owns this record, may be null.
314      * @throws RecordInvalidInputException thrown if the owner record referred to with the given ID number does not exist, or isn't allowed to be the owner of this Record according to the record structure rules.
315      */
316     public final void setOwner(Long value) throws RecordInvalidInputException {
317         owner.validate(value);
318     }
319     
320     /** Returns the Date at which this record was originally created.
321      * @return The Date at which this record was originally created.
322      * @see nectar.record.Record#setCreatedDate
323      */
324     public final Date getCreatedDate() {
325         return createdDate.getDataValue();
326     }
327     
328     /** Sets the Date at which this record was originally created. This method always throws a {@link nectar.record.RecordInvalidInputException} since the createdDate is generated internally, and cannot be modified.
329      * @param value The Date at which this record was originally created.
330      * @throws RecordInvalidInputException always thrown since the createdDate is generated internally, and cannot be modified.
331      * @see nectar.record.Record#getCreatedDate
332      */
333     public final void setCreatedDate(Date value) throws RecordInvalidInputException {
334         createdDate.validate(value);
335     }
336     
337     /** Returns the Date at which this record was last modified.
338      * @return The Date at which this record was last modified.
339      * @see nectar.record.Record#setModifiedDate
340      */
341     public final Date getModifiedDate() {
342         return modifiedDate.getDataValue();
343     }
344     
345     /** Sets the Date at which this record was last modified. This method always throws a {@link nectar.record.RecordInvalidInputException} since the modifiedDate is generated internally, and cannot be modified.
346      * @param value The Date at which this record was last modified.
347      * @throws RecordInvalidInputException always thrown since the modifiedDate is generated internally, and cannot be modified.
348      * @see nectar.record.Record#setModifiedDate
349      */
350     public final void setModifiedDate(Date value) throws RecordInvalidInputException {
351         modifiedDate.validate(value);
352     }
353     
354     /** Returns the ID number of the PersonRecord, which describes the person that created this record.
355      * @return The ID number of the PersonRecord, which describes the person that created this record.
356      * @see nectar.record.Record#setCreatedBy
357      * @see nectar.record.PersonRecord
358      */
359     public final Long getCreatedBy() {
360         return createdBy.getDataValue();
361     }
362     
363     /** Sets the ID number of the PersonRecord, which describes the person that created this record.
364      * This method always throws a {@link nectar.record.RecordInvalidInputException} since the createdBy field is generated internally, and cannot be modified.
365      * @param value The ID number of the PersonRecord, which describes the person that created this record.
366      * @throws RecordInvalidInputException always thrown since the createdBy field is generated internally, and cannot be modified.
367      * @see nectar.record.Record#getCreatedBy
368      * @see nectar.record.PersonRecord
369      */
370     public final void setCreatedBy(Long value) throws RecordInvalidInputException {
371         createdBy.validate(value);
372     }
373     
374     /** Returns the status of this record.
375      * @return The status of this record.
376      */
377     public final String getStatus() {
378         return status.getDataValue();
379     }
380     
381     /** Sets the status of this record.
382      * @param value The status of this record.
383      * @throws RecordInvalidInputException if the given status String is not valid.
384      */
385     public final void setStatus(String value) throws RecordInvalidInputException {
386         status.validate(value);
387     }
388     
389     /** Returns the ordering value of this record.
390      * @return The ordering value of this record.
391      */
392     public final Long getOrderBy() {
393         return orderBy.getDataValue();
394     }
395     /** Sets the ordering value of this record.
396      * @param value The ordering value of this record.
397      * @throws RecordInvalidInputException never thrown.
398      */
399     public final void setOrderBy(Long value) throws RecordInvalidInputException {
400         orderBy.validate(value);
401     }
402     
403     /** Creates and returns appropriate RecordView instance for this record. You can safely typecast the return value to the RecordView subclass associated to this record.
404      * @return the RecordView associated to this Record.
405      */
406     public abstract nectar.view.RecordView getView();
407 }