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

Quick Search    Search Deep

Source code: org/progeeks/meta/DefaultMetaTable.java


1   /*
2    * $Id: DefaultMetaTable.java,v 1.6 2003/09/12 08:06:21 pspeed Exp $
3    *
4    * Copyright (c) 2001-2003, Paul Speed
5    * All rights reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without
8    * modification, are permitted provided that the following conditions
9    * are met:
10   *
11   * 1) Redistributions of source code must retain the above copyright notice,
12   *    this list of conditions and the following disclaimer.
13   * 2) Redistributions in binary form must reproduce the above copyright
14   *    notice, this list of conditions and the following disclaimer in the
15   *    documentation and/or other materials provided with the distribution.
16   * 3) Neither the names "Progeeks", "Meta-JB", nor the names of its contributors
17   *    may be used to endorse or promote products derived from this software
18   *    without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   */
32  
33  package org.progeeks.meta;
34  
35  import java.beans.*;
36  import java.util.*;
37  
38  import org.progeeks.util.*;
39  import org.progeeks.util.beans.*;
40  
41  /**
42   *  A default implementation of the MetaTable interface.  This
43   *  meta-table implementation contains an internal list of objects.
44   *  If these objects are meta-objects then they are access directly.
45   *  Otherwise, they are wrapped in meta-objects using the meta-kit
46   *  provided.
47   *  Optionally a list of property names can be provided or the table
48   *  will pull the property names from the specified meta-class.
49   *
50   *  @version   $Revision: 1.6 $
51   *  @author    Paul Speed
52   */
53  public class DefaultMetaTable extends BeanChangeSupport
54                                implements MetaTable
55  {
56      /**
57       *  Keeps cached version of the meta-objects as they
58       *  are created.  If the original object list contains
59       *  meta-objects, then this field will be null.
60       */
61      private List wrapperObjects;
62  
63      /**
64       *  The original object list.  This will be the same size
65       *  as the metaList, or null if the objects are all meta-objects.
66       */
67      private ObservableList objects;
68  
69      /**
70       *  An array of the supported properties.  Easy to map column to
71       *  property name this way.
72       */
73      private String[] properties;
74  
75      /**
76       *  The meta-class that this table is constrained by.
77       */
78      private MetaClass metaClass;
79  
80      /**
81       *  A reference to the meta-kit we'll use to create new meta-objects
82       *  when we need to.
83       */
84      private MetaKit kit;
85  
86      /**
87       *  The listener that is added to all meta-objects created internally.
88       *  This passes on the change events to table listeners.
89       */
90      private MetaObjectListener objectListener = new MetaObjectListener();
91  
92      /**
93       *  Creates a default meta-table that contains the specified list
94       *  of objects.
95       */
96      public DefaultMetaTable( List objects, List properties, MetaClass metaClass, MetaKit kit )
97      {
98          this.metaClass = metaClass;
99          this.kit = kit;
100 
101         Object obj = objects.size() > 0 ? objects.get(0) : null;
102 
103         setObjects( objects, !(obj instanceof MetaObject) );
104         setProperties( properties );
105     }
106 
107     /**
108      *  Creates a default meta-table that contains the specified list
109      *  of objects.
110      */
111     public DefaultMetaTable( List objects, List properties, MetaClass metaClass, MetaKit kit,
112                              boolean autoWrap )
113     {
114         this.metaClass = metaClass;
115         this.kit = kit;
116 
117         Object obj = objects.size() > 0 ? objects.get(0) : null;
118 
119         setObjects( objects, autoWrap );
120         setProperties( properties );
121     }
122 
123     protected void setObjects( List objects, boolean autoWrap )
124     {
125         // You know.  Potentially, we should always keep a separate
126         // list containing the wrapper objects.  This is so that we
127         // can better manage listener add/remove when changes to the
128         // original list happen.  Right now, if the caller passes in
129         // an ObservableList containing MetaObjects then we create a
130         // resource leak every time an object is removed from the list.
131 
132         this.wrapperObjects = null;
133         this.objects = null;
134 
135         if( objects instanceof ObservableList )
136             {
137             this.objects = (ObservableList)objects;
138             }
139         else
140             {
141             // We make our own copy since we can't detect changes
142             // otherwise.
143             this.objects = new ObservableList( new ArrayList( objects ) );
144             }
145 
146         if( !autoWrap )
147             {
148             // Don't need to keep a separate set of wrappers.
149 
150             // Add our listener to all of the objects
151             for( Iterator i = objects.iterator(); i.hasNext(); )
152                 {
153                 MetaObject mObj = (MetaObject)i.next();
154                 if( mObj != null )
155                     mObj.addPropertyChangeListener( objectListener );
156                 }
157             }
158         else
159             {
160             // Otherwise, make sure the wrapper list is the same
161             // size as the objects list
162             wrapperObjects = new ArrayList( objects.size() );
163             for( int i = 0; i < objects.size(); i++ )
164                 wrapperObjects.add( null );
165             }
166 
167         // Register ourselves a listener to the list... if it's
168         // external, this is vital.  If it's internal, it just
169         // makes things simpler.
170         this.objects.addPropertyChangeListener( objectListener );
171     }
172 
173     protected void setProperties( List names )
174     {
175         properties = new String[ names.size() ];
176         properties = (String[])names.toArray( properties );
177     }
178 
179     /**
180      *  Adds the specified object to the list.  If an object is a
181      *  meta-object then it will be directly added to the meta-object
182      *  list.  Otherwise, a meta-object will be created.
183      */
184     public void addObject( Object obj )
185     {
186         MetaObject mObj = null;
187         if( obj instanceof MetaObject )
188             {
189             mObj = (MetaObject)mObj;
190             if( wrapperObjects != null )
191                 {
192                 // Handle the case even though callers really shouldn't
193                 // be doing this.
194                 wrapperObjects.add( obj );
195                 objects.add( kit.getInternalObject( (MetaObject)obj ) );
196                 }
197             else
198                 {
199                 objects.add( mObj );
200                 }
201             }
202         else if( wrapperObjects == null )
203             {
204             throw new RuntimeException( "Non-meta-object cannot be added to homogenous meta-object list." );
205             }
206         else
207             {
208             // Go ahead and add the meta-object now instead of waiting
209             // for a cache miss to keep the event propagation simpler
210             mObj = kit.wrapObject( obj, metaClass );
211 
212             objects.add( obj );
213             wrapperObjects.add( mObj );
214             }
215 
216         // Add our listener
217         mObj.addPropertyChangeListener( objectListener );
218     }
219 
220     /**
221      *  Returns the specified row as a meta-object.
222      */
223     public MetaObject getMetaObject( int row )
224     {
225         if( wrapperObjects == null )
226             return( (MetaObject)objects.get(row) );
227 
228         MetaObject mObj = (MetaObject)wrapperObjects.get( row );
229         if( mObj != null )
230             return( mObj );
231 
232         Object obj = objects.get( row );
233         mObj = kit.wrapObject( obj, metaClass );
234         wrapperObjects.set( row, mObj );
235 
236         // Add our listener
237         mObj.addPropertyChangeListener( objectListener );
238 
239         return( mObj );
240     }
241 
242     /**
243      *  Returns the row index for the specified meta-object.  This is
244      *  an optional operation and may throw an UnsupportedOpreationException.
245      */
246     public int getRowIndex( MetaObject obj )
247     {
248         // Note: there is no way they would have a valid meta-object
249         //       without it already being in one of the lists.
250         if( wrapperObjects != null )
251             return( wrapperObjects.indexOf( obj ) );
252         return( objects.indexOf( obj ) );
253     }
254 
255     /**
256      *  Returns the property info for a given column.
257      */
258     public PropertyInfo getPropertyInfo( int column )
259     {
260         return( metaClass.getPropertyInfo( properties[column] ) );
261     }
262 
263     /**
264      *  Returns the column index of the specified property.
265      *  @return The index of the property or -1 if the name does
266      *          not represent a supported property.
267      */
268     public int getColumnIndex( String propertyName )
269     {
270         for( int i = 0; i < properties.length; i++ )
271             {
272             if( propertyName.equals( properties[i] ) )
273                 return( i );
274             }
275         return( -1 );
276     }
277 
278     /**
279      *  Returns the number of rows in the table.
280      */
281     public int getRowCount()
282     {
283         return( objects.size() );
284     }
285 
286     /**
287      *  Returns the number of columns in the table.
288      */
289     public int getColumnCount()
290     {
291         return( properties.length );
292     }
293 
294     /**
295      *  Sets the value of the specified property.
296      */
297     public void setProperty( int row, int column, Object value )
298     {
299         getMetaObject( row ).setProperty( properties[column], value );
300     }
301 
302     /**
303      *  Returns the value of the specified property.
304      */
305     public Object getProperty( int row, int column )
306     {
307         return( getMetaObject( row ).getProperty( properties[column] ) );
308     }
309 
310     /**
311      *  Returns a mutator for the specified property.
312      */
313     public PropertyMutator getPropertyMutator( int row, int column )
314     {
315         return( getMetaObject( row ).getPropertyMutator( properties[column] ) );
316     }
317 
318     /**
319      *  Returns the meta-class associated with this object.
320      */
321     public MetaClass getMetaClass()
322     {
323         return( metaClass );
324     }
325 
326     /**
327      *  Returns the meta-kit for this meta-object's implementation
328      *  layer.
329      */
330     public MetaKit getMetaKit()
331     {
332         return( kit );
333     }
334 
335     /**
336      *  Adds a property change listener that will be notified
337      *  whenever the specified property changes in any row.
338      */
339     public void addPropertyChangeListener( int column,
340                                            PropertyChangeListener l )
341     {
342         addPropertyChangeListener( properties[column], l );
343     }
344 
345     /**
346      *  Returns true if the specified property name has listeners
347      *  registered.
348      */
349     public boolean hasListeners( int column )
350     {
351         return( hasListeners( properties[column] ) );
352     }
353 
354     /**
355      *  Removes the property change listener from a specific
356      *  property.
357      */
358     public void removePropertyChangeListener( int column,
359                                               PropertyChangeListener l )
360     {
361         removePropertyChangeListener( properties[column], l );
362     }
363 
364     /**
365      *  This is the listener that is added to all meta-objects
366      *  so that any changes are forwarded along to table listeners.
367      */
368     private class MetaObjectListener implements PropertyChangeListener
369     {
370         public void propertyChange( PropertyChangeEvent event )
371         {
372             // Check to see if the event is from our internal list
373             if( event.getSource() == objects )
374                 {
375                 ListPropertyChangeEvent le = new ListPropertyChangeEvent( DefaultMetaTable.this,
376                                                                           (ListPropertyChangeEvent)event );
377 
378                 // Make sure the appropriate update is made
379                 // to our own structures
380                 if( wrapperObjects != null )
381                     {
382                     int count = le.getLastIndex() - le.getFirstIndex() + 1;
383 
384                     switch( le.getType() )
385                         {
386                         case ListPropertyChangeEvent.INSERT:
387                             for( int i = 0; i < count; i++ )
388                                 wrapperObjects.add( le.getFirstIndex(), null );
389                             break;
390                         case ListPropertyChangeEvent.UPDATE:
391                             for( int i = le.getFirstIndex(); i <= le.getLastIndex(); i++ )
392                                 {
393                                 MetaObject mObj = (MetaObject)wrapperObjects.set( i, null );
394                                 if( mObj != null )
395                                     mObj.removePropertyChangeListener( objectListener );
396                                 }
397                             break;
398                         case ListPropertyChangeEvent.DELETE:
399                             for( int i = 0; i < count; i++ )
400                                 {
401                                 MetaObject mObj = (MetaObject)wrapperObjects.remove( le.getFirstIndex() );
402                                 if( mObj != null )
403                                     mObj.removePropertyChangeListener( objectListener );
404                                 }
405                             break;
406                         }
407                     }
408 
409                 // Need to resource the event
410                 firePropertyChange( le );
411                 return;
412                 }
413 
414             // Only send the event along if it's in our
415             // property list.
416             String name = event.getPropertyName();
417             if( getColumnIndex(name) < 0 )
418                 return;
419 
420             firePropertyChange( event );
421         }
422     }
423 }
424