KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2 Copyright (c) 2003-2004, 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.lang.reflect.InvocationTargetException JavaDoc;
32 import java.lang.reflect.Method JavaDoc;
33
34 import org.apache.bcel.generic.*;
35
36 import org.jibx.binding.classes.*;
37 import org.jibx.runtime.JiBXException;
38
39 /**
40  * Primitive string conversion handling. Class for handling serialization
41  * converting a primitive type to and from <code>String</code> values.
42  *
43  * @author Dennis M. Sosnoski
44  * @version 1.0
45  */

46
47 public class PrimitiveStringConversion extends StringConversion
48 {
49     //
50
// Static class references
51

52     private static ClassFile s_unmarshalClass;
53     {
54         try {
55             s_unmarshalClass = ClassCache.getClassFile
56                 ("org.jibx.runtime.impl.UnmarshallingContext");
57         } catch (JiBXException ex) { /* no handling required */ }
58     }
59     
60     //
61
// enum for comparison types of primitive values
62
private static final int INT_TYPE = 0;
63     private static final int LONG_TYPE = 1;
64     private static final int FLOAT_TYPE = 2;
65     private static final int DOUBLE_TYPE = 3;
66     
67     //
68
// Constants for code generation.
69

70     /** Class providing basic conversion methods. */
71     private static final String JavaDoc UTILITY_CLASS_NAME =
72         "org.jibx.runtime.Utility";
73     
74     /** Class providing basic conversion methods. */
75     private static final String JavaDoc UNMARSHAL_CLASS_NAME =
76         "org.jibx.runtime.impl.UnmarshallingContext";
77     
78     /** Unmarshal method signature leading portion. */
79     private static final String JavaDoc UNMARSHAL_SIG_LEAD =
80         "(Ljava/lang/String;Ljava/lang/String;";
81     
82     /** Constant argument type array for finding conversion methods. */
83     private static final Class JavaDoc[] SINGLE_STRING_ARGS =
84         new Class JavaDoc[] { String JavaDoc.class };
85
86     //
87
// Actual instance data
88

89     /** Marshalling requires conversion to text flag. */
90     private boolean m_isMarshalText;
91     
92     /** Unmarshalling requires conversion to text flag. */
93     private boolean m_isUnmarshalText;
94         
95     /** Unmarshalling context method for optional attribute. */
96     private ClassItem m_unmarshalOptAttribute;
97     
98     /** Unmarshalling context method for optional element. */
99     private ClassItem m_unmarshalOptElement;
100     
101     /** Unmarshalling context method for required attribute. */
102     private ClassItem m_unmarshalReqAttribute;
103     
104     /** Unmarshalling context method for required element. */
105     private ClassItem m_unmarshalReqElement;
106     
107     /** Comparison and marshal type of value (INT_TYPE, LONG_TYPE, FLOAT_TYPE,
108      or DOUBLE_TYPE) */

109     private int m_valueType;
110     
111     /** Name of value type on stack. */
112     private String JavaDoc m_stackType;
113     
114     /**
115      * Constructor. Initializes conversion handling based on the supplied
116      * inherited handling.
117      *
118      * @param type name of primitive type handled by conversion
119      * @param inherit conversion information inherited by this conversion
120      */

121
122     protected PrimitiveStringConversion(String JavaDoc type,
123         PrimitiveStringConversion inherit) {
124         super(type, inherit);
125         m_isMarshalText = inherit.m_isMarshalText;
126         m_isUnmarshalText = inherit.m_isUnmarshalText;
127         m_unmarshalOptAttribute = inherit.m_unmarshalOptAttribute;
128         m_unmarshalOptElement = inherit.m_unmarshalOptElement;
129         m_unmarshalReqAttribute = inherit.m_unmarshalReqAttribute;
130         m_unmarshalReqElement = inherit.m_unmarshalReqElement;
131         m_valueType = inherit.m_valueType;
132         m_stackType = inherit.m_stackType;
133     }
134
135     /**
136      * Constructor. Initializes conversion handling based on argument values.
137      * This form is only used for constructing the default set of conversions.
138      *
139      * @param cls class of primitive type handled by conversion
140      * @param dflt default value object (wrapped value, or <code>String</code>
141      * or <code>null</code> with special deserializer)
142      * @param ts name of utility class static method for converting value to
143      * <code>String</code>
144      * @param fs name of utility class static method for converting
145      * <code>String</code> to value
146      * @param uattr unmarshalling context method name for attribute value
147      * @param uelem unmarshalling context method name for element value
148      */

149
150     public PrimitiveStringConversion(Class JavaDoc cls, Object JavaDoc dflt, String JavaDoc code,
151         String JavaDoc ts, String JavaDoc fs, String JavaDoc uattr, String JavaDoc uelem) {
152         super(dflt, UTILITY_CLASS_NAME+'.'+ts, UTILITY_CLASS_NAME+'.'+fs,
153             cls.getName());
154         m_isMarshalText = m_isUnmarshalText = false;
155         String JavaDoc sig = UNMARSHAL_SIG_LEAD + code + ')' + code;
156         m_unmarshalOptAttribute = s_unmarshalClass.getMethod(uattr, sig);
157         m_unmarshalOptElement = s_unmarshalClass.getMethod(uelem, sig);
158         sig = UNMARSHAL_SIG_LEAD + ')' + code;
159         m_unmarshalReqAttribute = s_unmarshalClass.getMethod(uattr, sig);
160         m_unmarshalReqElement = s_unmarshalClass.getMethod(uelem, sig);
161         if (cls == Long.TYPE) {
162             m_valueType = LONG_TYPE;
163             m_stackType = "long";
164         } else if (cls == Float.TYPE) {
165             m_valueType = FLOAT_TYPE;
166             m_stackType = "float";
167         } else if (cls == Double.TYPE) {
168             m_valueType = DOUBLE_TYPE;
169             m_stackType = "double";
170         } else {
171             m_valueType = INT_TYPE;
172             m_stackType = "int";
173         }
174     }
175
176     /**
177      * Generate code to convert <code>String</code> representation. The
178      * code generated by this method assumes that the <code>String</code>
179      * value has already been pushed on the stack. It consumes this and
180      * leaves the converted value on the stack.
181      *
182      * @param mb method builder
183      */

184
185     public void genFromText(MethodBuilder mb) {
186         
187         // check if a deserializer is used for this type
188
if (m_deserializer != null) {
189             
190             // just generate call to the deserializer (adding any checked
191
// exceptions thrown by the deserializer to the list needing
192
// handling)
193
mb.addMethodExceptions(m_deserializer);
194             mb.appendCall(m_deserializer);
195         }
196     }
197     
198     /**
199      * Push default value on stack. Just adds the appropriate instruction to
200      * the list for the method.
201      *
202      * @param mb method builder
203      */

204
205     protected void pushDefault(MethodBuilder mb) {
206         mb.appendLoadConstant(m_default);
207     }
208
209     /**
210      * Generate code to parse and convert optional attribute or element. The
211      * code generated by this method assumes that the unmarshalling context
212      * and name information for the attribute or element have already
213      * been pushed on the stack. It consumes these and leaves the converted
214      * value (or default value, if the item itself is missing) on the stack.
215      *
216      * @param attr item is an attribute (vs element) flag
217      * @param mb method builder
218      * @throws JiBXException if error in configuration
219      */

220     
221     public void genParseOptional(boolean attr, MethodBuilder mb)
222         throws JiBXException {
223         
224         // choose between custom deserializer or standard built-in method
225
if (m_isUnmarshalText) {
226             
227             // first part of generated instruction sequence is to push the
228
// default value text, then call the appropriate unmarshalling
229
// context method to get the value as a String
230
String JavaDoc dflt;
231             if (m_default instanceof String JavaDoc || m_default == null) {
232                 dflt = (String JavaDoc)m_default;
233             } else {
234                 dflt = m_default.toString();
235             }
236             mb.appendLoadConstant(dflt);
237             String JavaDoc name = attr ? UNMARSHAL_OPT_ATTRIBUTE :
238                 UNMARSHAL_OPT_ELEMENT;
239             mb.appendCallVirtual(name, UNMARSHAL_OPT_SIGNATURE);
240             
241             // second part is to generate call to deserializer
242
genFromText(mb);
243             
244         } else {
245             
246             // generated instruction sequence just pushes the unwrapped default
247
// value, then calls the appropriate unmarshalling context method
248
// to get the value as a primitive
249
pushDefault(mb);
250             mb.appendCall(attr ?
251                 m_unmarshalOptAttribute : m_unmarshalOptElement);
252             
253         }
254     }
255
256     /**
257      * Generate code to parse and convert required attribute or element. The
258      * code generated by this method assumes that the unmarshalling context and
259      * name information for the attribute or element have already been pushed
260      * on the stack. It consumes these and leaves the converted value on the
261      * stack.
262      *
263      * @param attr item is an attribute (vs element) flag
264      * @param mb method builder
265      * @throws JiBXException if error in configuration
266      */

267
268     public void genParseRequired(boolean attr, MethodBuilder mb)
269         throws JiBXException {
270         
271         // choose between custom deserializer or standard built-in method
272
if (m_isUnmarshalText) {
273             
274             // first part of generated instruction sequence is a call to
275
// the appropriate unmarshalling context method to get the value
276
// as a String
277
String JavaDoc name = attr ? UNMARSHAL_REQ_ATTRIBUTE :
278                 UNMARSHAL_REQ_ELEMENT;
279             mb.appendCallVirtual(name, UNMARSHAL_REQ_SIGNATURE);
280             
281             // second part is to generate call to deserializer
282
genFromText(mb);
283             
284         } else {
285             
286             // generated instruction sequence just calls the appropriate
287
// unmarshalling context method to get the value as a primitive
288
mb.appendCall(attr ?
289                 m_unmarshalReqAttribute : m_unmarshalReqElement);
290             
291         }
292     }
293
294     /**
295      * Generate code to check if an optional value is not equal to the default.
296      * The code generated by this method assumes that the actual value to be
297      * converted has already been pushed on the stack. It consumes this,
298      * leaving the converted text reference on the stack if it's not equal to
299      * the default value.
300      *
301      * @param type fully qualified class name for value on stack
302      * @param mb method builder
303      * @param extra count of extra values to be popped from stack if missing
304      * @return handle for branch taken when value is equal to the default
305      * (target must be set by caller)
306      * @throws JiBXException if error in configuration
307      */

308
309     protected BranchWrapper genToOptionalText(String JavaDoc type, MethodBuilder mb,
310         int extra) throws JiBXException {
311     
312         // set instructions based on value size
313
StackInstruction dup;
314         StackInstruction pop;
315         if (m_valueType == LONG_TYPE || m_valueType == DOUBLE_TYPE) {
316             mb.appendDUP2();
317         } else {
318             mb.appendDUP();
319         }
320         extra++;
321     
322         // first add code to check if the value is different from the default,
323
// by duplicating the value, pushing the default, and executing the
324
// appropriate branch comparison
325

326         // TODO: this should not be done inline, but necessary for now
327
Object JavaDoc value = m_default;
328         if (m_isUnmarshalText) {
329             try {
330                 String JavaDoc mname = m_deserializer.getName();
331                 String JavaDoc cname = m_deserializer.getClassFile().getName();
332                 Class JavaDoc clas = ClassFile.loadClass(cname);
333                 if (clas == null) {
334                     throw new JiBXException("Deserializer class " + cname +
335                         " not found for converting default value");
336                 } else {
337                     
338                     // try first to find a declared method, then a public one
339
Method JavaDoc meth;
340                     try {
341                         meth = clas.getDeclaredMethod(mname,
342                             SINGLE_STRING_ARGS);
343                         meth.setAccessible(true);
344                     } catch (NoSuchMethodException JavaDoc ex) {
345                         meth = clas.getMethod(mname, SINGLE_STRING_ARGS);
346                     }
347                     String JavaDoc text;
348                     if (value instanceof String JavaDoc || value == null) {
349                         text = (String JavaDoc)value;
350                     } else {
351                         text = value.toString();
352                     }
353                     value = meth.invoke(null, new Object JavaDoc[] { text });
354                     
355                 }
356             } catch (IllegalAccessException JavaDoc ex) {
357                 throw new JiBXException("Conversion method not accessible", ex);
358             } catch (InvocationTargetException JavaDoc ex) {
359                 throw new JiBXException("Internal error", ex);
360             } catch (NoSuchMethodException JavaDoc ex) {
361                 throw new JiBXException("Internal error", ex);
362             }
363         }
364         mb.appendLoadConstant(value);
365         
366         BranchWrapper ifne = null;
367         switch (m_valueType) {
368         
369             case LONG_TYPE:
370                 mb.appendLCMP();
371                 break;
372         
373             case FLOAT_TYPE:
374                 mb.appendFCMPG();
375                 break;
376         
377             case DOUBLE_TYPE:
378                 mb.appendDCMPG();
379                 break;
380         
381             default:
382                 ifne = mb.appendIF_ICMPNE(this);
383                 break;
384             
385         }
386         if (ifne == null) {
387             ifne = mb.appendIFNE(this);
388         }
389         
390         // generate code for branch not taken case, popping the value from
391
// stack along with extra parameters, and branching past using code
392
genPopValues(extra, mb);
393         BranchWrapper toend = mb.appendUnconditionalBranch(this);
394         mb.targetNext(ifne);
395         genToText(m_stackType, mb);
396         return toend;
397     }
398     
399     /**
400      * Convert text representation into default value object. This override of
401      * the base class method uses reflection to call the actual deserialization
402      * method, returning the wrapped result value. If a custom deserializer is
403      * defined this just returns the <code>String</code> value directly.
404      *
405      * @param text value representation to be converted
406      * @return converted default value object
407      * @throws JiBXException on conversion error
408      */

409
410     protected Object JavaDoc convertDefault(String JavaDoc text) throws JiBXException {
411         if (!m_isUnmarshalText) {
412             try {
413                 String JavaDoc mname = m_deserializer.getName();
414                 String JavaDoc cname = m_deserializer.getClassFile().getName();
415                 Class JavaDoc clas = ClassFile.loadClass(cname);
416                 if (clas == null) {
417                     throw new JiBXException("Deserializer class " + cname +
418                         " not found for converting default value");
419                 } else {
420                     
421                     // try first to find a declared method, then a public one
422
Method JavaDoc meth;
423                     try {
424                         meth = clas.getDeclaredMethod(mname,
425                             SINGLE_STRING_ARGS);
426                         meth.setAccessible(true);
427                     } catch (NoSuchMethodException JavaDoc ex) {
428                         meth = clas.getMethod(mname, SINGLE_STRING_ARGS);
429                     }
430                     return meth.invoke(null, new Object JavaDoc[] { text });
431                     
432                 }
433             } catch (IllegalAccessException JavaDoc ex) {
434                 throw new JiBXException("Conversion method not accessible", ex);
435             } catch (InvocationTargetException JavaDoc ex) {
436                 throw new JiBXException("Internal error", ex);
437             } catch (NoSuchMethodException JavaDoc ex) {
438                 throw new JiBXException("Internal error", ex);
439             }
440         } else {
441             return text;
442         }
443     }
444
445     /**
446      * Check if the type handled by this conversion is of a primitive type.
447      *
448      * @return <code>true</code> to indicate primitive type
449      */

450
451     public boolean isPrimitive() {
452         return true;
453     }
454
455     /**
456      * Set serializer for conversion. This override of the base class method
457      * sets a flag to indicate that values must be converted to text before
458      * they are written to a document after executing the base class processing.
459      *
460      * @param ser fully qualified class and method name of serializer
461      * @throws JiBXException if serializer not found or not usable
462      */

463
464     protected void setSerializer(String JavaDoc ser) throws JiBXException {
465         super.setSerializer(ser);
466         m_isMarshalText = true;
467     }
468
469     /**
470      * Set deserializer for conversion. This override of the base class method
471      * sets a flag to indicate that values must be read from a document as text
472      * and converted as a separate step after executing the base class
473      * processing.
474      *
475      * @param deser fully qualified class and method name of deserializer
476      * @throws JiBXException if deserializer not found or not usable
477      */

478
479     protected void setDeserializer(String JavaDoc deser) throws JiBXException {
480         super.setDeserializer(deser);
481         m_isUnmarshalText = true;
482     }
483
484     /**
485      * Derive from existing formatting information. This allows constructing
486      * a new instance from an existing format of the same or an ancestor
487      * type, with the properties of the existing format copied to the new
488      * instance except where overridden by the supplied values.
489      *
490      * @param type fully qualified name of class handled by conversion
491      * @param ser fully qualified name of serialization method
492      * (<code>null</code> if inherited)
493      * @param dser fully qualified name of deserialization method
494      * (<code>null</code> if inherited)
495      * @param dflt default value text (<code>null</code> if inherited)
496      * @return new instance initialized from existing one
497      * @throws JiBXException if error in configuration information
498      */

499
500     public StringConversion derive(String JavaDoc type, String JavaDoc ser, String JavaDoc dser,
501         String JavaDoc dflt) throws JiBXException {
502         if (type == null) {
503             type = m_typeName;
504         }
505         StringConversion inst = new PrimitiveStringConversion(type, this);
506         if (ser != null) {
507             inst.setSerializer(ser);
508         }
509         if (dser != null) {
510             inst.setDeserializer(dser);
511         }
512         if (dflt != null) {
513             inst.m_default = inst.convertDefault(dflt);
514         }
515         return inst;
516     }
517 }
518
Popular Tags