KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jibx > binding > def > PropertyDefinition


1 /*
2 Copyright (c) 2003-2005, Dennis M. Sosnoski
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8  * Redistributions of source code must retain the above copyright notice, this
9    list of conditions and the following disclaimer.
10  * Redistributions in binary form must reproduce the above copyright notice,
11    this list of conditions and the following disclaimer in the documentation
12    and/or other materials provided with the distribution.
13  * Neither the name of JiBX nor the names of its contributors may be used
14    to endorse or promote products derived from this software without specific
15    prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */

28
29 package org.jibx.binding.def;
30
31 import java.util.ArrayList JavaDoc;
32
33 import org.jibx.binding.classes.BranchWrapper;
34 import org.jibx.binding.classes.ClassFile;
35 import org.jibx.binding.classes.ClassItem;
36 import org.jibx.binding.classes.ContextMethodBuilder;
37 import org.jibx.binding.classes.MethodBuilder;
38 import org.jibx.binding.model.ClassUtils;
39 import org.jibx.runtime.JiBXException;
40
41 /**
42  * Property definition from binding. This organizes shared information for
43  * bindings linked to fields or get/set methods of an object, and provides
44  * methods for related code generation.
45  *
46  * @author Dennis M. Sosnoski
47  * @version 1.0
48  */

49
50 public class PropertyDefinition
51 {
52     //
53
// Actual instance data
54

55     /** Reference to "this" property of object flag. */
56     private final boolean m_isThis;
57     
58     /** Reference to implicit value from collection. */
59     private final boolean m_isImplicit;
60
61     /** Optional item flag. */
62     private boolean m_isOptional;
63
64     /** Containing object context. */
65     private final IContextObj m_objContext;
66
67     /** Fully qualified name of actual type of value. */
68     private final String JavaDoc m_typeName;
69     
70     /** Fully qualified name of declared type of value loaded. */
71     private final String JavaDoc m_getValueType;
72     
73     /** Fully qualified name of declared type of value stored. */
74     private final String JavaDoc m_setValueType;
75
76     /** Information for field (if given, may be <code>null</code>). */
77     private final ClassItem m_fieldItem;
78
79     /** Information for test method (if given, may be <code>null</code>). */
80     private final ClassItem m_testMethod;
81
82     /** Information for get method (if given, may be <code>null</code>). */
83     private final ClassItem m_getMethod;
84
85     /** Information for set method (if given, may be <code>null</code>). */
86     private final ClassItem m_setMethod;
87
88     /**
89      * Constructor.
90      *
91      * @param parent containing binding definition structure
92      * @param obj containing object context
93      * @param type fully qualified name of type
94      * @param isthis "this" object reference flag
95      * @param opt optional property flag
96      * @param fname containing object field name for property (may be
97      * <code>null</code>)
98      * @param test containing object method to test for property present (may be
99      * <code>null</code>)
100      * @param get containing object method to get property value (may be
101      * <code>null</code>)
102      * @param set containing object method to set property value (may be
103      * <code>null</code>)
104      * @throws JiBXException if configuration error
105      */

106
107     public PropertyDefinition(IContainer parent, IContextObj obj, String JavaDoc type,
108         boolean isthis, boolean opt, String JavaDoc fname, String JavaDoc test, String JavaDoc get,
109         String JavaDoc set) throws JiBXException {
110         m_objContext = obj;
111         m_isThis = isthis;
112         m_isOptional = opt;
113         ClassFile cf = m_objContext.getBoundClass().getClassFile();
114         m_isImplicit = false;
115         String JavaDoc dtype = null;
116         String JavaDoc gtype = null;
117         String JavaDoc stype = null;
118         if (isthis) {
119             if (type == null) {
120                 dtype = gtype = stype = cf.getName();
121             } else {
122                 dtype = gtype = stype = type;
123             }
124         }
125         if (fname == null) {
126             m_fieldItem = null;
127         } else {
128             m_fieldItem = cf.getField(fname);
129             dtype = gtype = stype = m_fieldItem.getTypeName();
130         }
131         if (test == null) {
132             m_testMethod = null;
133         } else {
134             if (opt) {
135                 m_testMethod = cf.getMethod(test, "()Z");
136                 if (m_testMethod == null) {
137                     throw new JiBXException("test-method " + test +
138                         " not found in class " + cf.getName());
139                 }
140             } else {
141                 throw new JiBXException
142                     ("Test method only allowed for optional properties");
143             }
144         }
145         if (get == null) {
146             m_getMethod = null;
147         } else {
148             m_getMethod = cf.getMethod(get, "()");
149             if (m_getMethod == null) {
150                 throw new JiBXException("get-method " + get +
151                     " not found in class " + cf.getName());
152             } else {
153                 gtype = m_getMethod.getTypeName();
154                 if (dtype == null) {
155                     dtype = gtype;
156                 }
157             }
158         }
159         if (set == null) {
160             m_setMethod = null;
161         } else {
162             
163             // need to handle overloads, so generate possible signatures
164
ArrayList JavaDoc sigs = new ArrayList JavaDoc();
165             if (m_getMethod != null) {
166                 sigs.add("(" + ClassUtils.getSignature(gtype) + ")V");
167             }
168             if (type != null) {
169                 sigs.add("(" + ClassUtils.getSignature(type) + ")V");
170             }
171             if (m_fieldItem != null) {
172                 sigs.add("(" + m_fieldItem.getSignature() + ")V");
173             }
174             sigs.add("(Ljava/lang/Object;)V");
175             
176             // set method needs verification of argument and return type
177
m_setMethod = cf.getMethod(set,
178                 (String JavaDoc[])sigs.toArray(new String JavaDoc[0]));
179             if (m_setMethod == null) {
180                 throw new JiBXException("set-method " + set +
181                     " not found in class " + cf.getName());
182             } else {
183                 stype = m_setMethod.getArgumentType(0);
184                 if (dtype == null) {
185                     dtype = stype;
186                 }
187             }
188         }
189         m_getValueType = gtype;
190         m_setValueType = stype;
191
192         // check that enough information is supplied
193
BindingDefinition root = parent.getBindingRoot();
194         if (!isthis && m_fieldItem == null) {
195             if (root.isInput() && m_setMethod == null) {
196                 throw new JiBXException
197                     ("Missing way to set value for input binding");
198             }
199             if (root.isOutput() && m_getMethod == null) {
200                 throw new JiBXException
201                     ("Missing way to get value for output binding");
202             }
203         }
204         
205         // check that type information is consistent
206
if (type == null) {
207             m_typeName = dtype;
208         } else {
209             m_typeName = type;
210             boolean valid = true;
211             if (isthis) {
212                 valid = ClassItem.isAssignable(dtype, type);
213             } else {
214                 if (root.isInput()) {
215                     valid = ClassItem.isAssignable(type, m_setValueType);
216                 }
217                 if (valid && root.isOutput()) {
218                     valid = ClassItem.isAssignable(type, m_getValueType) ||
219                         ClassItem.isAssignable(m_getValueType, type);
220                 }
221             }
222             if (!valid) {
223                 throw new JiBXException
224                     ("Incompatible types for property definition");
225             }
226         }
227     }
228
229     /**
230      * Constructor for "this" object reference.
231      *
232      * @param obj containing object context
233      * @param opt optional property flag
234      */

235
236     public PropertyDefinition(IContextObj obj, boolean opt) {
237         m_objContext = obj;
238         m_isThis = true;
239         m_isImplicit = false;
240         m_isOptional = opt;
241         ClassFile cf = m_objContext.getBoundClass().getClassFile();
242         m_fieldItem = m_testMethod = m_getMethod = m_setMethod = null;
243         m_typeName = m_getValueType = m_setValueType = cf.getName();
244     }
245
246     /**
247      * Constructor for implicit object reference.
248      *
249      * @param type object type supplied
250      * @param obj containing object context
251      * @param opt optional property flag
252      */

253
254     public PropertyDefinition(String JavaDoc type, IContextObj obj, boolean opt) {
255         m_objContext = obj;
256         m_isImplicit = true;
257         m_isThis = false;
258         m_isOptional = opt;
259         m_fieldItem = m_testMethod = m_getMethod = m_setMethod = null;
260         m_typeName = m_getValueType = m_setValueType = type;
261     }
262
263     /**
264      * Check if property is "this" reference for object.
265      *
266      * @return <code>true</code> if reference to "this", <code>false</code> if
267      * not
268      */

269
270     public boolean isThis() {
271         return m_isThis;
272     }
273
274     /**
275      * Check if property is implicit value from collection.
276      *
277      * @return <code>true</code> if implicit, <code>false</code> if not
278      */

279
280     public boolean isImplicit() {
281         return m_isImplicit;
282     }
283
284     /**
285      * Check if property is optional.
286      *
287      * @return <code>true</code> if optional, <code>false</code> if required
288      */

289
290     public boolean isOptional() {
291         return m_isOptional;
292     }
293
294     /**
295      * Set flag for an optional property.
296      *
297      * @param opt <code>true</code> if optional property, <code>false</code> if
298      * not
299      */

300     
301     public void setOptional(boolean opt) {
302         m_isOptional = opt;
303     }
304
305     /**
306      * Get property name. If a field is defined this is the same as the field;
307      * otherwise it is either the get method name (with leading "get" stripped,
308      * if present) or the set method (with leading "set" stripped, if present),
309      * whichever is found.
310      *
311      * @return name for this property
312      */

313     
314     public String JavaDoc getName() {
315         if (m_isThis) {
316             return "this";
317         } else if (m_fieldItem != null) {
318             return m_fieldItem.getName();
319         } else if (m_getMethod != null) {
320             String JavaDoc name = m_getMethod.getName();
321             if (name.startsWith("get") && name.length() > 3) {
322                 name = name.substring(3);
323             }
324             return name;
325         } else if (m_setMethod != null) {
326             String JavaDoc name = m_setMethod.getName();
327             if (name.startsWith("set") && name.length() > 3) {
328                 name = name.substring(3);
329             }
330             return name;
331         } else {
332             return "item";
333         }
334     }
335
336     /**
337      * Get declared type fully qualified name.
338      *
339      * @return fully qualified class name of declared type
340      */

341
342     public String JavaDoc getTypeName() {
343         return m_typeName;
344     }
345
346     /**
347      * Get value type as fully qualified name for loaded property value.
348      *
349      * @return fully qualified class name of value type
350      */

351
352     public String JavaDoc getGetValueType() {
353         return m_getValueType;
354     }
355
356     /**
357      * Get value type as fully qualified name for stored property value.
358      *
359      * @return fully qualified class name of value type
360      */

361
362     public String JavaDoc getSetValueType() {
363         return m_setValueType;
364     }
365
366     /**
367      * Check if property has presence test. Code needs to be generated to check
368      * for the presence of the property if it is optional and either a test
369      * method is defined or the value is an object reference.
370      *
371      * @return <code>true</code> if presence test needed, <code>false</code> if
372      * not
373      */

374
375     public boolean hasTest() {
376         return isOptional() &&
377             (m_testMethod != null || !ClassItem.isPrimitive(m_typeName));
378     }
379
380     /**
381      * Generate code to test if property is present. The generated code
382      * assumes that the top of the stack is the reference for the containing
383      * object, and consumes this value for the test. The target for the
384      * returned branch instruction must be set by the caller.
385      *
386      * @param mb method builder
387      * @return wrapper for branch instruction taken when property is missing
388      * @throws JiBXException if configuration error
389      */

390
391     public BranchWrapper genTest(ContextMethodBuilder mb)
392         throws JiBXException {
393         
394         // error if called on "this" or implicit reference
395
if (m_isThis && m_testMethod == null) {
396             throw new IllegalStateException JavaDoc
397                 ("Internal error: No test for \"this\"");
398         } else if (m_isImplicit) {
399             throw new IllegalStateException JavaDoc
400                 ("Internal error: No test for implicit value from collection");
401         }
402         
403         // first check for supplied test method
404
if (m_testMethod != null) {
405             
406             // generate call to test method to check for property present
407
mb.addMethodExceptions(m_testMethod);
408             if (m_testMethod.isStatic()) {
409                 mb.appendPOP();
410             }
411             mb.appendCall(m_testMethod);
412             return mb.appendIFEQ(this);
413             
414         } else if (!ClassItem.isPrimitive(m_typeName)) {
415             
416             // generated instruction either loads a field value or calls a "get"
417
// method, as appropriate
418
if (m_getMethod == null) {
419                 if (m_fieldItem.isStatic()) {
420                     mb.appendPOP();
421                 }
422                 mb.appendGet(m_fieldItem);
423             } else {
424                 if (m_getMethod.isStatic()) {
425                     mb.appendPOP();
426                 }
427                 mb.addMethodExceptions(m_getMethod);
428                 mb.appendCall(m_getMethod);
429             }
430             return mb.appendIFNULL(this);
431             
432         } else {
433             return null;
434         }
435     }
436
437     /**
438      * Generate code to load property value to stack. The generated code
439      * assumes that the top of the stack is the reference for the containing
440      * object. It consumes this and leaves the actual value on the stack. If
441      * the property value is not directly accessible from the context of the
442      * method being generated this automatically constructs an access method
443      * and uses that method.
444      *
445      * @param mb method builder
446      * @throws JiBXException if configuration error
447      */

448
449     public void genLoad(MethodBuilder mb) throws JiBXException {
450         
451         // nothing to be done if called on "this" or implicit reference
452
if (!m_isThis && !m_isImplicit) {
453         
454             // first check direct access to property from method class
455
ClassFile from = mb.getClassFile();
456             ClassItem access = m_getMethod;
457             if (access == null) {
458                 access = m_fieldItem;
459             }
460             if (!from.isAccessible(access)) {
461                 access = m_objContext.getBoundClass().
462                     getLoadMethod(access, mb.getClassFile());
463             }
464         
465             // generated instruction either loads a field value or calls a "get"
466
// method, as appropriate
467
if (access.isStatic()) {
468                 mb.appendPOP();
469             }
470             if (access.isMethod()) {
471                 mb.addMethodExceptions(access);
472                 mb.appendCall(access);
473             } else {
474                 mb.appendGet(access);
475             }
476             
477             // cast value if necessary to assure correct type
478
mb.appendCreateCast(m_getValueType, m_typeName);
479             
480         }
481     }
482
483     /**
484      * Generate code to store property value from stack. The generated code
485      * assumes that the reference to the containing object and the value to be
486      * stored have already been pushed on the stack. It consumes these, leaving
487      * nothing. If the property value is not directly accessible from the
488      * context of the method being generated this automatically constructs an
489      * access method and uses that method.
490      *
491      * @param mb method builder
492      * @throws JiBXException if configuration error
493      */

494
495     public void genStore(MethodBuilder mb) throws JiBXException {
496         
497         // nothing to be done if called on "this" or implicit reference
498
if (!m_isThis && !m_isImplicit) {
499         
500             // first check direct access to property from method class
501
ClassFile from = mb.getClassFile();
502             ClassItem access = m_setMethod;
503             if (access == null) {
504                 access = m_fieldItem;
505             }
506             if (!from.isAccessible(access)) {
507                 access = m_objContext.getBoundClass().
508                     getStoreMethod(access, mb.getClassFile());
509             }
510         
511             // generated instruction either stores a field value or calls a
512
// "set" method, as appropriate
513
if (access.isStatic()) {
514                 mb.appendPOP();
515             }
516             if (access.isMethod()) {
517                 mb.addMethodExceptions(access);
518                 mb.appendCall(access);
519             } else {
520                 mb.appendPut(access);
521             }
522         }
523     }
524     
525     // DEBUG
526
public String JavaDoc toString() {
527         StringBuffer JavaDoc text = new StringBuffer JavaDoc();
528         if (m_isOptional) {
529             text.append("optional ");
530         }
531         text.append("property ");
532         if (m_isThis) {
533             text.append("\"this\" ");
534         } else if (m_isImplicit) {
535             text.append("from collection ");
536         } else if (m_fieldItem != null) {
537             text.append(m_fieldItem.getName() + " ");
538         } else {
539             if (m_getMethod != null) {
540                 text.append("from " + m_getMethod.getName() + " ");
541             }
542             if (m_setMethod != null) {
543                 text.append("to " + m_setMethod.getName() + " ");
544             }
545         }
546         text.append( "("+ m_typeName + ")");
547         return text.toString();
548     }
549 }
Popular Tags