Source code: org/progeeks/meta/MetaClass.java
1 /*
2 * $Id: MetaClass.java,v 1.11 2003/09/20 11:07:09 pspeed Exp $
3 *
4 * Copyright (c) 2001-2002, 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.util.*;
36
37 /**
38 * Describes the properties associated with a meta-object.
39 * MetaClass objects must be created through the factory methods
40 * provided by the MetaClassRegistry to which they will belong.
41 *
42 * @version $Revision: 1.11 $
43 * @author Paul Speed
44 */
45 public class MetaClass
46 {
47 private MetaClassRegistry classRegistry;
48 private String name;
49 private Map properties = new LinkedHashMap();
50 private List superclasses = new ArrayList();
51 private boolean resolved = false;
52
53 /**
54 * Creates the meta-class using the specified parameters.
55 */
56 protected MetaClass( MetaClassRegistry classRegistry, String name, Collection propertyInfo )
57 {
58 this( classRegistry, name, propertyInfo, Collections.EMPTY_LIST );
59 }
60
61 /**
62 * Creates the meta-class using the specified parameters.
63 */
64 protected MetaClass( MetaClassRegistry classRegistry, String name, Collection propertyInfo,
65 Collection superclasses )
66 {
67 if( name == null )
68 throw new RuntimeException( "Name cannot be null." );
69
70 this.classRegistry = classRegistry;
71 this.name = name;
72
73 // Possible class hierarchies:
74 //
75 // Simple:
76 // A <- B <- C
77 //
78 // Multiple:
79 // A <- C
80 // B <-
81 //
82 // C should have all of A's properties and any of B's that
83 // aren't defined in A.
84 //
85 // Complex:
86 // A <-\
87 // B <--C-\
88 // G
89 // B <--F-/
90 // D <-/
91 //
92 // G should have all of C and F's properties, only
93 // one set of B's properties (that aren't in C or F)
94 // and any of A and D's properties (that aren't in C or F).
95 // I think looking at C and F's full property set is probably
96 // enough... just check for dupes and conflicts at that level.
97
98 for( Iterator i = superclasses.iterator(); i.hasNext(); )
99 {
100 MetaClass mc = (MetaClass)i.next();
101 if( this.superclasses.contains( mc ) )
102 continue;
103 this.superclasses.add( mc );
104
105 // Go ahead and add the properties from the meta-class
106 for( Iterator j = mc.getPropertyInfos().iterator(); j.hasNext(); )
107 {
108 PropertyInfo info = (PropertyInfo)j.next();
109 addProperty( info );
110 }
111 }
112
113 for( Iterator i = propertyInfo.iterator(); i.hasNext(); )
114 {
115 PropertyInfo info = (PropertyInfo)i.next();
116 addProperty( info );
117 }
118 }
119
120 /**
121 * Creates the meta-class using the specified parameters.
122 */
123 protected MetaClass( MetaClassRegistry classRegistry, String name, Collection propertyInfo,
124 MetaClass superclass )
125 {
126 this( classRegistry, name, propertyInfo, Arrays.asList( new MetaClass[] { superclass } ) );
127 }
128
129 /**
130 * Called internally to resolve a property type if
131 * it is required.
132 */
133 protected PropertyType resolveType( PropertyType type )
134 {
135 if( type instanceof DeferredPropertyType )
136 {
137 type = ((DeferredPropertyType)type).resolveType( classRegistry );
138 return( type );
139 }
140 else if( type instanceof ContainerPropertyType )
141 {
142 ContainerPropertyType containerType = (ContainerPropertyType)type;
143 PropertyType valType = containerType.getValueType();
144 if( valType instanceof DeferredPropertyType )
145 {
146 valType = ((DeferredPropertyType)valType).resolveType( classRegistry );
147 containerType = containerType.replaceContainedType( valType );
148 return( containerType );
149 }
150 }
151 return( null );
152 }
153
154 /**
155 * Resolves any deferred types against the class registry
156 * associated with this meta-class. This should be called
157 * by meta-object implementations during instantiation until
158 * a more automatic way becomes available.
159 */
160 public void resolveTypes()
161 {
162 if( resolved )
163 return;
164
165 for( Iterator i = properties.entrySet().iterator(); i.hasNext(); )
166 {
167 Map.Entry entry = (Map.Entry)i.next();
168
169 PropertyInfo info = (PropertyInfo)entry.getValue();
170 PropertyType type = info.getPropertyType();
171 PropertyType resolvedType = resolveType( type );
172 if( resolvedType == null )
173 continue;
174
175 // This might be bad since we currently allow different
176 // implementations of PropertyInfo. I think that's wrong
177 // though, so may change in the future. It only gets
178 // toolkits in trouble to try and implement it themselves...
179 // and indeed this section only breaks those improper uses.
180 info = info.replacePropertyType( resolvedType );
181 entry.setValue( info );
182 }
183
184 resolved = true;
185 }
186
187 /**
188 * Returns the class registry to which this meta-class belongs.
189 */
190 public MetaClassRegistry getClassRegistry()
191 {
192 return( classRegistry );
193 }
194
195 /**
196 * Returns the name of this meta-class.
197 */
198 public String getName()
199 {
200 return( name );
201 }
202
203 /**
204 * Returns a List containing the immediate superclasses of this meta-class.
205 */
206 public List getSuperclasses()
207 {
208 return( Collections.unmodifiableList( superclasses ) );
209 }
210
211 /**
212 * Adds the specified property info to this meta-class.
213 */
214 protected void addProperty( PropertyInfo info )
215 {
216 String name = info.getPropertyName();
217 if( properties.containsKey( name ) )
218 {
219 // Check to make sure the types are compatable. Use
220 // the most specific type.
221 PropertyInfo check = getPropertyInfo( name );
222
223 // Right now we cheat. Types have to be identical
224 // and we never replace.
225 if( !check.getPropertyType().equals( info.getPropertyType() ) )
226 throw new RuntimeException( "Property already exists:" + name );
227 return;
228 }
229
230 properties.put( name, info );
231 }
232
233 /**
234 * Returns the type for the specified property.
235 */
236 public PropertyType getPropertyType( String name )
237 {
238 PropertyInfo info = getPropertyInfo( name );
239 if( info == null )
240 return( null );
241 return( info.getPropertyType() );
242 }
243
244 /**
245 * Returns the descriptive information for the specified
246 * property.
247 */
248 public PropertyInfo getPropertyInfo( String name )
249 {
250 // Temporary until class registry knows about dependencies
251 // May cause types to be resolved too early.
252 if( !resolved )
253 resolveTypes();
254 return( (PropertyInfo)properties.get( name ) );
255 }
256
257 /**
258 * Returns an iterator over all valid property names.
259 */
260 public Iterator propertyNames()
261 {
262 return( getPropertyNames().iterator() );
263 }
264
265 /**
266 * Returns the unmodifiable set of property names.
267 */
268 public Set getPropertyNames()
269 {
270 return( Collections.unmodifiableSet( properties.keySet() ) );
271 }
272
273 /**
274 * Returns an unmodifiable collection of property info objects.
275 */
276 public Collection getPropertyInfos()
277 {
278 return( Collections.unmodifiableCollection( properties.values() ) );
279 }
280
281 /**
282 * Returns true if the specified meta-object is an instance
283 * of this class.
284 */
285 public boolean isInstance( MetaObject obj )
286 {
287 if( equals( obj.getMetaClass() ) )
288 return( true );
289
290 // Check the superclasses
291 for( Iterator i = superclasses.iterator(); i.hasNext(); )
292 {
293 MetaClass mc = (MetaClass)i.next();
294 if( mc.isInstance(obj) )
295 return( true );
296 }
297
298 return( false );
299 }
300
301 public int hashCode()
302 {
303 return( name.hashCode() );
304 }
305
306 public boolean equals( MetaClass mClass )
307 {
308 if( mClass == this )
309 return( true );
310
311 if( mClass == null )
312 return( false );
313 if( !name.equals( mClass.name ) )
314 return( false );
315 if( !properties.equals( mClass.properties ) )
316 return( false );
317 return( true );
318 }
319
320 public boolean equals( Object obj )
321 {
322 if( obj == this )
323 return( true );
324
325 if( obj instanceof MetaClass )
326 {
327 return( equals( (MetaClass)obj ) );
328 }
329 return( false );
330 }
331
332 public String toString()
333 {
334 return( "MetaClass[" + name + "]" );
335 }
336 }