KickJava   Java API By Example, From Geeks To Geeks.

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


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 org.jibx.binding.classes.*;
32 import org.jibx.runtime.JiBXException;
33
34 /**
35  * Object string conversion handling. Defines serialization handling for
36  * converting objects to and from a <code>String</code> value. The default is
37  * to just use the object <code>toString()</code> method for serialization and
38  * a constructor from a <code>String</code> value for deserialization.
39  * <code>java.lang.String</code> itself is a special case, with no added code
40  * used by default for either serializing or deserializing.
41  * <code>java.lang.Object</code> is also a special case, with no added code
42  * used by default for deserializing (the <code>String</code> value is used
43  * directly). Other classes must either implement <code>toString()</code> and
44  * a constructor from <code>String</code>, or use custom serializers and/or
45  * deserializers.
46  *
47  * @author Dennis M. Sosnoski
48  * @version 1.0
49  */

50
51 public class ObjectStringConversion extends StringConversion
52 {
53     //
54
// Constants for code generation.
55

56     private static final String JavaDoc TOSTRING_METHOD = "toString";
57     private static final String JavaDoc TOSTRING_SIGNATURE =
58         "()Ljava/lang/String;";
59     private static final String JavaDoc FROMSTRING_SIGNATURE =
60         "(Ljava/lang/String;)V";
61
62     //
63
// Actual instance data
64

65     /** Flag for conversion from <code>String</code> needed (type is anything
66      other than <code>String</code> or <code>Object</code>) */

67     private boolean m_needDeserialize;
68     
69     /** Initializer used for creating instance from <code>String</code>
70      (only used if no conversion needed and no deserializer supplied;
71      may be <code>null</code>) */

72     private ClassItem m_initFromString;
73     
74     /** Flag for conversion to <code>String</code> needed (type is anything
75      other than <code>String</code>) */

76     private boolean m_needSerialize;
77     
78     /** <code>toString()</code> method for converting instance to
79      <code>String</code> (only used if conversion needed and no serializer
80      supplied; may be <code>null</code>) */

81     private ClassItem m_instToString;
82     
83     /**
84      * Constructor. Initializes conversion handling based on the supplied
85      * inherited handling.
86      *
87      * @param type fully qualified name of class handled by conversion
88      * @param inherit conversion information inherited by this conversion
89      * @throws JiBXException if error in configuration
90      */

91
92     /*package*/ ObjectStringConversion(String JavaDoc type, ObjectStringConversion inherit)
93         throws JiBXException {
94         super(type, inherit);
95         if (type.equals(inherit.m_typeName)) {
96             m_needDeserialize = inherit.m_needDeserialize;
97             m_initFromString = inherit.m_initFromString;
98             m_needSerialize = inherit.m_needSerialize;
99             m_instToString = inherit.m_instToString;
100         } else {
101             initMethods();
102         }
103     }
104
105     /**
106      * Constructor. Initializes conversion handling based on argument values.
107      * This form is only used for constructing the default set of conversions.
108      * Because of this, it throws an unchecked exception on error.
109      *
110      * @param dflt default value object (wrapped value for primitive types,
111      * otherwise <code>String</code>)
112      * @param ser fully qualified name of serialization method
113      * @param deser fully qualified name of deserialization method
114      * @param type fully qualified name of class handled by conversion
115      */

116
117     /*package*/ ObjectStringConversion(Object JavaDoc dflt, String JavaDoc ser, String JavaDoc deser,
118         String JavaDoc type) {
119         super(dflt, ser, deser, type);
120         try {
121             initMethods();
122         } catch (JiBXException ex) {
123             throw new IllegalArgumentException JavaDoc(ex.getMessage());
124         }
125     }
126
127     /**
128      * Initialize methods used for conversion of types without serializer or
129      * deserializer. Sets flags for types needed, with errors thrown at time
130      * of attempted use rather than at definition time. That offers the
131      * advantages of simpler handling (we don't need to know which directions
132      * are supported in a binding) and more flexibility (can support nested
133      * partial definitions cleanly).
134      */

135
136     private void initMethods() throws JiBXException {
137         if (!"java.lang.String".equals(m_typeName)) {
138             m_needSerialize = true;
139             m_needDeserialize = !"java.lang.Object".equals(m_typeName);
140             ClassFile cf = ClassCache.getClassFile(m_typeName);
141             m_initFromString = cf.getInitializerMethod(FROMSTRING_SIGNATURE);
142             m_instToString = cf.getMethod(TOSTRING_METHOD, TOSTRING_SIGNATURE);
143         }
144     }
145
146     /**
147      * Generate code to convert <code>String</code> representation. The
148      * code generated by this method assumes that the <code>String</code>
149      * value has already been pushed on the stack. It consumes this and
150      * leaves the converted value on the stack.
151      *
152      * @param mb method builder
153      */

154
155     public void genFromText(MethodBuilder mb) throws JiBXException {
156         
157         // check if a deserializer is used for this type
158
if (m_deserializer != null) {
159             
160             // just generate call to the deserializer (adding any checked
161
// exceptions thrown by the deserializer to the list needing
162
// handling)
163
mb.addMethodExceptions(m_deserializer);
164             mb.appendCall(m_deserializer);
165             
166         } else if (m_initFromString != null) {
167             
168             // first generate code to duplicate value and check for null, with
169
// duplicate replaced by explicit null if already null (confusing
170
// in the bytecode, but will be optimized out by any native code
171
// generation)
172
mb.appendDUP();
173             BranchWrapper ifnnull = mb.appendIFNONNULL(this);
174             mb.appendPOP();
175             mb.appendACONST_NULL();
176             BranchWrapper toend = mb.appendUnconditionalBranch(this);
177             
178             // generate code to create an instance of object and pass text value
179
// to constructor
180
mb.targetNext(ifnnull);
181             mb.appendCreateNew(m_typeName);
182             mb.appendDUP_X1();
183             mb.appendSWAP();
184             mb.appendCallInit(m_typeName, FROMSTRING_SIGNATURE);
185             mb.targetNext(toend);
186             
187         } else if (m_needDeserialize) {
188             throw new JiBXException("No deserializer for " + m_typeName +
189                 "; define deserializer or constructor from java.lang.String");
190         }
191     }
192
193     /**
194      * Generate code to parse and convert optional attribute or element. The
195      * code generated by this method assumes that the unmarshalling context
196      * and name information for the attribute or element have already
197      * been pushed on the stack. It consumes these and leaves the converted
198      * value (or converted default value, if the item itself is missing) on
199      * the stack.
200      *
201      * @param attr item is an attribute (vs element) flag
202      * @param mb method builder
203      * @throws JiBXException if error in configuration
204      */

205     
206     public void genParseOptional(boolean attr, MethodBuilder mb)
207         throws JiBXException {
208         
209         // first part of generated instruction sequence is to push the default
210
// value, then call the appropriate unmarshalling context method to get
211
// the value as a String
212
mb.appendLoadConstant((String JavaDoc)m_default);
213         String JavaDoc name = attr ? UNMARSHAL_OPT_ATTRIBUTE : UNMARSHAL_OPT_ELEMENT;
214         mb.appendCallVirtual(name, UNMARSHAL_OPT_SIGNATURE);
215         
216         // second part is to actually convert to an instance of the type
217
genFromText(mb);
218     }
219
220     /**
221      * Generate code to parse and convert required attribute or element. The
222      * code generated by this method assumes that the unmarshalling context and
223      * name information for the attribute or element have already been pushed
224      * on the stack. It consumes these and leaves the converted value on the
225      * stack.
226      *
227      * @param attr item is an attribute (vs element) flag
228      * @param mb method builder
229      * @throws JiBXException if error in configuration
230      */

231
232     public void genParseRequired(boolean attr, MethodBuilder mb)
233         throws JiBXException {
234         
235         // first part of generated instruction sequence is a call to the
236
// appropriate unmarshalling context method to get the value as a
237
// String
238
String JavaDoc name = attr ? UNMARSHAL_REQ_ATTRIBUTE : UNMARSHAL_REQ_ELEMENT;
239         mb.appendCallVirtual(name, UNMARSHAL_REQ_SIGNATURE);
240         
241         // second part is to actually convert to an instance of the type
242
genFromText(mb);
243     }
244     
245     /**
246      * Shared code generation for converting instance of type to
247      * <code>String</code>. This override of the base class method checks for
248      * serialization using the <code>toString</code> method and implements that
249      * case directly, while calling the base class method for normal handling.
250      * The code generated by this method assumes that the reference to the
251      * instance to be converted is on the stack. It consumes the reference,
252      * replacing it with the corresponding <code>String</code> value.
253      *
254      * @param type fully qualified class name for value on stack
255      * @param mb marshal method builder
256      * @throws JiBXException if error in configuration
257      */

258
259     public void genToText(String JavaDoc type, MethodBuilder mb)
260         throws JiBXException {
261         if (m_serializer == null && m_needSerialize) {
262             
263             // report error if no handling available
264
if (m_instToString == null) {
265                 throw new JiBXException("No serializer for " + m_typeName +
266                     "; define serializer or toString() method");
267             } else {
268                 
269                 // generate code to call toString() method of instance (adding
270
// any checked exceptions thrown by the method to the list
271
// needing handling)
272
mb.addMethodExceptions(m_instToString);
273                 mb.appendCall(m_instToString);
274                 
275             }
276         } else {
277             super.genToText(type, mb);
278         }
279     }
280
281     /**
282      * Generate code to check if an optional value is not equal to the default.
283      * The code generated by this method assumes that the actual value to be
284      * converted has already been pushed on the stack. It consumes this,
285      * leaving the converted text reference on the stack if it's not equal to
286      * the default value.
287      *
288      * @param type fully qualified class name for value on stack
289      * @param mb method builder
290      * @param extra count of extra values to be popped from stack if missing
291      * @return handle for branch taken when value is equal to the default
292      * (target must be set by caller)
293      * @throws JiBXException if error in configuration
294      */

295
296     protected BranchWrapper genToOptionalText(String JavaDoc type, MethodBuilder mb,
297         int extra) throws JiBXException {
298         
299         // check if the default value is just null
300
if (m_default == null) {
301             
302             // generate code to call the serializer and get String value on
303
// stack
304
genToText(type, mb);
305             return null;
306             
307         } else {
308             
309             // start with code to call the serializer and get the String value
310
// on stack
311
genToText(type, mb);
312         
313             // add code to check if the serialized text is different from the
314
// default, by duplicating the returned reference, pushing the
315
// default, and calling the object comparison method. This is
316
// followed by a branch if the comparison says they're not equal
317
// (nonzero result, since it's a boolean value).
318
mb.appendDUP();
319             mb.appendLoadConstant((String JavaDoc)m_default);
320             mb.appendCallStatic(COMPARE_OBJECTS_METHOD,
321                 COMPARE_OBJECTS_SIGNATURE);
322             BranchWrapper iffalse = mb.appendIFEQ(this);
323             
324             // finish by discarding copy of object reference on stack when
325
// equal to the default
326
genPopValues(extra+1, mb);
327             BranchWrapper toend = mb.appendUnconditionalBranch(this);
328             mb.targetNext(iffalse);
329             return toend;
330         
331         }
332     }
333
334     /**
335      * Check if the type handled by this conversion is of a primitive type.
336      *
337      * @return <code>false</code> to indicate object type
338      */

339
340     public boolean isPrimitive() {
341         return false;
342     }
343     
344     /**
345      * Convert text representation into default value object. For object types
346      * this just returns the text value.
347      *
348      * @param text value representation to be converted
349      * @return converted default value object
350      * @throws JiBXException on conversion error
351      */

352
353     protected Object JavaDoc convertDefault(String JavaDoc text) throws JiBXException {
354         return text;
355     }
356
357     /**
358      * Derive from existing formatting information. This allows constructing
359      * a new instance from an existing format of the same or an ancestor
360      * type, with the properties of the existing format copied to the new
361      * instance except where overridden by the supplied values.
362      *
363      * @param type fully qualified name of class handled by conversion
364      * @param ser fully qualified name of serialization method
365      * (<code>null</code> if inherited)
366      * @param dser fully qualified name of deserialization method
367      * (<code>null</code> if inherited)
368      * @param dflt default value text (<code>null</code> if inherited)
369      * @return new instance initialized from existing one
370      * @throws JiBXException if error in configuration information
371      */

372
373     public StringConversion derive(String JavaDoc type, String JavaDoc ser, String JavaDoc dser,
374         String JavaDoc dflt) throws JiBXException {
375         if (type == null) {
376             type = m_typeName;
377         }
378         StringConversion inst = new ObjectStringConversion(type, this);
379         if (ser != null) {
380             inst.setSerializer(ser);
381         }
382         if (dser != null) {
383             inst.setDeserializer(dser);
384         }
385         if (dflt != null) {
386             inst.m_default = inst.convertDefault(dflt);
387         }
388         return inst;
389     }
390 }
391
Popular Tags