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

Quick Search    Search Deep

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


1   /*
2    * $Id: AbstractMetaObject.java,v 1.5 2003/09/20 10:24:29 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.meta.*;
39  import org.progeeks.meta.util.*;
40  import org.progeeks.util.beans.*;
41  
42  /**
43   *  An abstract meta-object implementation making it easier to create
44   *  kit-specific meta-object adapters.  Hooks are provided to allow
45   *  subclasses to override specific functionality that may be important
46   *  to a meta-kit implementation.
47   *
48   *  Subclasses must provide, at the minimum, getProperty() and setProperty()
49   *  methods.
50   *
51   *  @version   $Revision: 1.5 $
52   *  @author    Paul Speed
53   */
54  public abstract class AbstractMetaObject extends BeanChangeSupport
55                                           implements MetaObject
56  {
57      private MetaKit         metaKit;
58      private MetaClass       metaClass;
59      private Map             wrappedValues = new HashMap();
60  
61      /**
62       *  Creates a meta-object that will use the specified
63       *  meta-kit and meta-class.
64       */
65      protected AbstractMetaObject( MetaClass metaClass, MetaKit metaKit )
66      {
67          setMetaClass( metaClass );
68          this.metaKit = metaKit;
69      }
70  
71      /**
72       *  Creates a meta-object that will use the specified
73       *  meta-kit.  The metaClass must be set by subclasses by calling
74       *  the setMetaClass() method before any other local methods
75       *  are accessed.
76       */
77      protected AbstractMetaObject( MetaKit metaKit )
78      {
79          this.metaKit = metaKit;
80      }
81  
82      /**
83       *  Called by subclasses to set the meta-class when setting
84       *  it on the constructor is not possible.  The meta-class
85       *  can only be set once or a RuntimeException will be thrown.
86       */
87      protected void setMetaClass( MetaClass metaClass )
88      {
89          if( this.metaClass != null )
90              throw new RuntimeException( "A MetaClass can only by set once." );
91          if( metaClass == null )
92              throw new IllegalArgumentException( "MetaClass cannot be null." );
93          this.metaClass = metaClass;
94  
95          // Resolve any deferred types
96          metaClass.resolveTypes();
97      }
98  
99      /**
100      *  Overridden by subclasses to provide implementation-specific
101      *  field-level access.
102      */
103     protected abstract Object setPropertyValue( String name, Object value );
104 
105     /**
106      *  Overridden by subclasses to provide implementation-specific
107      *  field-level access.
108      */
109     protected abstract Object getPropertyValue( String name );
110 
111     /**
112      *  Sticks a wrapped value into the wrapper cache.
113      */
114     protected void cacheWrapper( String name, Object value )
115     {
116         wrappedValues.put( name, value );
117     }
118 
119     /**
120      *  Retrieves a wrapped value from the wrapper cache.
121      */
122     protected Object getCachedWrapper( String name )
123     {
124         return( wrappedValues.get( name ) );
125     }
126 
127     /**
128      *  Wraps the object as appropriate to return to a caller or
129      *  returns the original value if no wrapping is needed.  This
130      *  implementation checks the property type and wraps objects
131      *  in meta-objects or MetaObjectListAdapters if needed.  Wrapped
132      *  values are cached in an internal HashMap to speed later retrievals
133      *  and reuse meta-objects as much as possible.  Objects are wrapped
134      *  using the local MetaKit.
135      */
136     protected Object wrapPropertyValue( String name, PropertyType type, Object value )
137     {
138         if( value == null )
139             return( null ); // don't wrap nulls for now.  FIXME?
140 
141         if( type instanceof MetaClassPropertyType )
142             {
143             // See if the object is pre-wrapped
144             if( value instanceof MetaObject )
145                 return( value );
146 
147             // See if the object is already wrapped by us
148             MetaObject wrapped = (MetaObject)getCachedWrapper( name );
149             if( wrapped != null && metaKit.getInternalObject( wrapped ) == value )
150                 {
151                 value = wrapped;
152                 }
153             else
154                 {
155                 MetaClass mClass = ((MetaClassPropertyType)type).getMetaClass();
156 
157                 // Try for a specific class
158                 MetaClass c = metaKit.getMetaClassForObject( value, mClass.getClassRegistry() );
159                 if( c != null )
160                     value = metaKit.wrapObject( value, c );
161                 else
162                     value = metaKit.wrapObject( value, mClass );
163 
164                 // Store it away to use later
165                 cacheWrapper( name, value );
166                 }
167             }
168         else if( type instanceof ListPropertyType )
169             {
170             PropertyType elementType = ((ListPropertyType)type).getValueType();
171             if( elementType instanceof MetaClassPropertyType )
172                 {
173                 // See if the object is pre-wrapped
174                 if( value instanceof MetaObjectListAdapter )
175                     return( value );
176 
177                 // See if the object is already wrapped by us
178                 MetaObjectListAdapter wrapped = (MetaObjectListAdapter)getCachedWrapper( name );
179                 if( wrapped != null && wrapped.getList() == value )
180                     {
181                     value = wrapped;
182                     }
183                 else
184                     {
185                     MetaClass mClass = ((MetaClassPropertyType)elementType).getMetaClass();
186 
187                     // Wrap the value in a meta-object wrapper list, we assume it's
188                     // a list of the appropriate objects
189                     value = new MetaObjectListAdapter( metaKit, mClass, (List)value );
190 
191                     // Store it away to use later
192                     cacheWrapper( name, value );
193                     }
194                 }
195             }
196 
197         return( value );
198     }
199 
200     /**
201      *  Unwraps the object as appropriate to pass to the internal
202      *  setPropertyValue() method or returns the original value if no
203      *  unwrapping is needed.  This implementation currently only
204      *  checks for MetaObjectListAdapter values and returns the internal
205      *  list.  MetaObjects that can from a different meta-kit are not
206      *  properly unwrapped yet.  This is because technically a local
207      *  meta-kit version of the meta-object created and the properties
208      *  copied.  This will be easier at a future time and is unnecessary
209      *  for most users.
210      */
211     protected Object unwrapPropertyValue( Object value )
212     {
213         if( value instanceof MetaObject )
214             {
215             MetaObject metaObject = (MetaObject)value;
216 
217             // Is it the same meta-kit?
218             if( metaObject.getMetaKit().equals( metaKit ) )
219                 {
220                 // Safe to unwrap
221                 value = metaKit.getInternalObject( metaObject );
222                 }
223             else
224                 {
225                 // Need to translate it back into a regular object
226                 // by creating a new bean and copying the properties
227                 // over
228                 throw new UnsupportedOperationException( "Incompatible meta-kits in applied property value." );
229                 }
230             }
231         else if( value instanceof MetaObjectListAdapter )
232             {
233             // Unwrap the list value
234             value = ((MetaObjectListAdapter)value).getList();
235             }
236         return( value );
237     }
238 
239     /**
240      *  Sets the value of the specified property.  The default
241      *  implementation does some initial type checking, unwraps
242      *  the value as needed by calling unwrapPropertyValue() and
243      *  then calls setPropertyValue() before firing a change event.
244      */
245     public void setProperty( String name, Object value )
246     {
247         PropertyType type = metaClass.getPropertyType( name );
248         if( type == null )
249             throw new RuntimeException( "No property defined:" + name );
250         if( value == null || !type.isInstance( value ) )
251             throw new RuntimeException( "Invalid value type for property:" + name + "  type:" + type );
252 
253         value = unwrapPropertyValue( value );
254 
255         Object oldValue = setPropertyValue( name, value );
256 
257         // Note: if the contained map is an observable map, we
258         // probably don't want to do this.  FIXME
259         firePropertyChange( name, oldValue, value );
260     }
261 
262     /**
263      *  Returns the value of the specified property.  The
264      *  default version calls getPropertyValue() to retrieve
265      *  the property value and then wraps the value as needed
266      *  by calling wrapPropertyValue() before returning it.
267      */
268     public Object getProperty( String name )
269     {
270         PropertyType type = metaClass.getPropertyType( name );
271         if( type == null )
272             throw new RuntimeException( "No property defined:" + name );
273 
274         Object val = getPropertyValue( name );
275 
276         return( wrapPropertyValue( name, type, val ) );
277     }
278 
279     /**
280      *  Returns a mutator for the specified property.  This implementation
281      *  calls createListMutator() or createPropertyMutator() as is appropriate
282      *  based on the PropertyInfo class.
283      */
284     public PropertyMutator getPropertyMutator( String name )
285     {
286         PropertyInfo info = metaClass.getPropertyInfo( name );
287         if( info == null )
288             throw new RuntimeException( "Property:" + name + " does not exist in:" + metaClass );
289 
290         if( info instanceof ContainerPropertyInfo && info.getPropertyType() instanceof ListPropertyType )
291             {
292             return( createListMutator( name ) );
293             }
294         return( createPropertyMutator( name ) );
295     }
296 
297     /**
298      *  Returns the meta-class associated with this object.
299      */
300     public MetaClass getMetaClass()
301     {
302         return( metaClass );
303     }
304 
305     /**
306      *  Returns the meta-kit for this meta-object's implementation
307      *  layer.
308      */
309     public MetaKit getMetaKit()
310     {
311         return( metaKit );
312     }
313 
314     /**
315      *  Creates a PropertyMutator for the specified property.   The
316      *  default implementation creates a DefaultPropertyMutator.
317      */
318     protected PropertyMutator createPropertyMutator( String name )
319     {
320         return( new DefaultPropertyMutator( name, this ) );
321     }
322 
323     /**
324      *  Creates a ListMutator for the specified property.  The
325      *  default implementation creates an instance of the inner
326      *  class BaseListMutator which is derived from
327      *  DefaultListMutator.
328      */
329     protected ListMutator createListMutator( String name )
330     {
331         return( new BaseListMutator( name ) );
332     }
333 
334     /**
335      *  Overridden to provide access to a firePropertyChange() method.
336      */
337     protected class BaseListMutator extends DefaultListMutator
338     {
339         public BaseListMutator( String propertyName )
340         {
341             super( propertyName, AbstractMetaObject.this );
342         }
343 
344         protected void firePropertyChangeEvent( PropertyChangeEvent event )
345         {
346             AbstractMetaObject.this.firePropertyChange( event );
347         }
348     }
349 }
350