KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jibx > extras > HashMapperStringToSchemaType


1 /*
2 Copyright (c) 2004-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.extras;
30
31 import java.math.BigDecimal JavaDoc;
32 import java.math.BigInteger JavaDoc;
33 import java.util.Date JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.Map JavaDoc;
37
38 import org.jibx.runtime.EnumSet;
39 import org.jibx.runtime.IAliasable;
40 import org.jibx.runtime.IMarshaller;
41 import org.jibx.runtime.IMarshallingContext;
42 import org.jibx.runtime.IUnmarshaller;
43 import org.jibx.runtime.IUnmarshallingContext;
44 import org.jibx.runtime.IXMLWriter;
45 import org.jibx.runtime.JiBXException;
46 import org.jibx.runtime.Utility;
47 import org.jibx.runtime.impl.MarshallingContext;
48 import org.jibx.runtime.impl.UnmarshallingContext;
49
50 /**
51  * <p>Custom marshaller/unmarshaller for <code>java.util.Map</code>
52  * instances. This handles mapping hash maps with string keys and values that
53  * match basic schema datatypes to and from XML. The key objects are marshalled
54  * as simple text values, using the <code>toString()</code> method to convert
55  * them to <code>String</code> if they are not already of that type. When
56  * unmarshalling the keys are always treated as <code>String</code> values. The
57  * corresponding values can be any of the object types corresponding to basic
58  * schema data types, and are marshalled with xsi:type attributes to specify the
59  * type of each value. The types currently supported are <code>Byte</code>,
60  * <code>Double</code>, <code>Float</code>, <code>Integer</code>,
61  * <code>Long</code>, <code>java.util.Date</code> (as xsd:dateTime),
62  * <code>java.sql.Date</code> (as xsd:date), <code>java.sql.Time</code> (as
63  * xsd:time), <code>java.math.BigDecimal</code> (with no exponent allowed, as
64  * xsd:decimal), and <code>java.math.BigInteger</code> (as xsd:integer). The
65  * xsd:type attribute is checked when unmarshalling values to select the proper
66  * deserialization and value type. The name of the top-level element in the XML
67  * structure can be configured in the binding definition, but the rest of the
68  * names are predefined and set in the code (though the namespace configured for
69  * the top-level element will be used with all the names).</p>
70  *
71  * <p>The net effect is that the XML structure will always be of the form:</p>
72  *
73  * <pre>&lt;map-name size="6" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
74  * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
75  * &lt;entry key="name" xsi:type="xsd:string">John Smith&lt;/entry>
76  * &lt;entry key="street" xsi:type="xsd:string">12345 Happy Lane&lt;/entry>
77  * &lt;entry key="city" xsi:type="xsd:string">Plunk&lt;/entry>
78  * &lt;entry key="state" xsi:type="xsd:string">WA&lt;/entry>
79  * &lt;entry key="rating" xsi:type="xsd:int">6&lt;/entry>
80  * &lt;entry key="joined" xsi:type="xsd:dateTime">2002-08-06T00:13:31Z&lt;/entry>
81  * &lt;/map-name></pre>
82  *
83  * <p>where "map-name" is the configured top-level element name, the "size"
84  * attribute is the number of pairs in the hash map, and the "entry" elements
85  * are the actual entries in the hash map.</p>
86  *
87  * <p>For unmarshalling this requires an active namespace declaration with a
88  * prefix for the schema namespace. All xsi:type attribute values must use this
89  * prefix. If more than one prefix is declared for the schema namespace, the
90  * innermost one declared must be used.</p>
91  *
92  * @author Dennis M. Sosnoski
93  * @version 1.0
94  */

95
96 public class HashMapperStringToSchemaType
97     implements IMarshaller, IUnmarshaller, IAliasable {
98     
99     //
100
// Basic constants used in code
101

102     private static final String JavaDoc SIZE_ATTRIBUTE_NAME = "size";
103     private static final String JavaDoc ENTRY_ELEMENT_NAME = "entry";
104     private static final String JavaDoc KEY_ATTRIBUTE_NAME = "key";
105     private static final String JavaDoc TYPE_ATTRIBUTE_NAME = "type";
106     private static final String JavaDoc XSI_NAMESPACE_URI =
107         "http://www.w3.org/2001/XMLSchema-instance";
108     private static final String JavaDoc XSD_NAMESPACE_URI =
109         "http://www.w3.org/2001/XMLSchema";
110     private static final String JavaDoc[] SCHEMA_NAMESPACE_URIS =
111     {
112         XSI_NAMESPACE_URI, XSD_NAMESPACE_URI
113     };
114     private static final String JavaDoc XSI_NAMESPACE_PREFIX = "xsi";
115     private static final String JavaDoc XSD_NAMESPACE_PREFIX = "xsd";
116     private static final String JavaDoc[] SCHEMA_NAMESPACE_PREFIXES =
117     {
118         XSI_NAMESPACE_PREFIX, XSD_NAMESPACE_PREFIX
119     };
120     private static final String JavaDoc XSD_PREFIX_LEAD = "xsd:";
121     private static final int DEFAULT_SIZE = 10;
122     
123     //
124
// Supported XML schema type correspondences enumeration
125

126     // numeric values for types
127
public static final int BOOLEAN_TYPE = 0;
128     public static final int BYTE_TYPE = 1;
129     public static final int DOUBLE_TYPE = 2;
130     public static final int FLOAT_TYPE = 3;
131     public static final int INT_TYPE = 4;
132     public static final int LONG_TYPE = 5;
133     public static final int SHORT_TYPE = 6;
134     public static final int DATETIME_TYPE = 7;
135     public static final int DATE_TYPE = 8;
136     public static final int TIME_TYPE = 9;
137     public static final int DECIMAL_TYPE = 10;
138     public static final int INTEGER_TYPE = 11;
139     public static final int BYTERRAY_TYPE = 12;
140     public static final int STRING_TYPE = 13;
141     
142     // enumeration definition (string order must match numeric list, above)
143
private static final EnumSet s_javaTypesEnum = new EnumSet(BOOLEAN_TYPE,
144         new String JavaDoc[] { "java.lang.Boolean", "java.lang.Byte",
145         "java.lang.Double", "java.lang.Float", "java.lang.Integer",
146         "java.lang.Long", "java.lang.Short", "java.util.Date", "java.sql.Date",
147         "java.sql.Time", "java.math.BigDecimal", "java.math.BigInteger",
148         "byte[]", "java.lang.String"} );
149     
150     // corresponding schema types (string order must match numeric list, above)
151
private static final EnumSet s_schemaTypesEnum = new EnumSet(BOOLEAN_TYPE,
152         new String JavaDoc[] { "boolean", "byte", "double", "float", "int", "long",
153         "short", "dateTime", "date", "time", "decimal", "integer",
154         "base64Binary", "string"} );
155     
156     //
157
// Member fields
158

159     private String JavaDoc m_uri;
160     private int m_index;
161     private String JavaDoc m_name;
162     
163     /**
164      * Default constructor. This uses a pre-defined name for the top-level
165      * element. It'll be used by JiBX when no name information is supplied by
166      * the mapping which references this custom marshaller/unmarshaller.
167      */

168     
169     public HashMapperStringToSchemaType() {
170         m_uri = null;
171         m_index = 0;
172         m_name = "hashmap";
173     }
174     
175     /**
176      * Aliased constructor. This takes a name definition for the top-level
177      * element. It'll be used by JiBX when a name is supplied by the mapping
178      * which references this custom marshaller/unmarshaller.
179      *
180      * @param uri namespace URI for the top-level element (also used for all
181      * other names within the binding)
182      * @param index namespace index corresponding to the defined URI within the
183      * marshalling context definitions
184      * @param name local name for the top-level element
185      */

186     
187     public HashMapperStringToSchemaType(String JavaDoc uri, int index, String JavaDoc name) {
188         m_uri = uri;
189         m_index = index;
190         m_name = name;
191     }
192     
193     /* (non-Javadoc)
194      * @see org.jibx.runtime.IMarshaller#isExtension(int)
195      */

196     
197     public boolean isExtension(int index) {
198         return false;
199     }
200
201     /* (non-Javadoc)
202      * @see org.jibx.runtime.IMarshaller#marshal(java.lang.Object,
203      * org.jibx.runtime.IMarshallingContext)
204      */

205     
206     public void marshal(Object JavaDoc obj, IMarshallingContext ictx)
207         throws JiBXException {
208         
209         // make sure the parameters are as expected
210
if (!(obj instanceof Map JavaDoc)) {
211             throw new JiBXException("Invalid object type for marshaller");
212         } else if (!(ictx instanceof MarshallingContext)) {
213             throw new JiBXException("Invalid object type for marshaller");
214         } else {
215             
216             // start by setting up added namespaces
217
MarshallingContext ctx = (MarshallingContext)ictx;
218             IXMLWriter xwrite = ctx.getXmlWriter();
219             int ixsi = xwrite.getNamespaces().length;
220             String JavaDoc[][] extens = xwrite.getExtensionNamespaces();
221             if (extens != null) {
222                 for (int i = 0; i < extens.length; i++) {
223                     ixsi += extens[i].length;
224                 }
225             }
226             xwrite.pushExtensionNamespaces(SCHEMA_NAMESPACE_URIS);
227             
228             // generate start tag for containing element
229
Map JavaDoc map = (Map JavaDoc)obj;
230             ctx.startTagNamespaces(m_index, m_name, new int[] { ixsi, ixsi+1 },
231                 SCHEMA_NAMESPACE_PREFIXES).
232                 attribute(m_index, SIZE_ATTRIBUTE_NAME, map.size()).
233                 closeStartContent();
234             
235             // loop through all entries in hashmap
236
Iterator JavaDoc iter = map.entrySet().iterator();
237             while (iter.hasNext()) {
238                 
239                 // first make sure we have a value
240
Map.Entry JavaDoc entry = (Map.Entry JavaDoc)iter.next();
241                 Object JavaDoc value = entry.getValue();
242                 if (value != null) {
243                     
244                     // write element with key attribute
245
ctx.startTagAttributes(m_index, ENTRY_ELEMENT_NAME);
246                     ctx.attribute(m_index, KEY_ATTRIBUTE_NAME,
247                         entry.getKey().toString());
248                     
249                     // translate value object class to schema type
250
String JavaDoc tname = value.getClass().getName();
251                     int type = s_javaTypesEnum.getValue(tname);
252                     if (type < 0) {
253                         throw new JiBXException("Value of type " + tname +
254                             " with key " + entry.getKey() +
255                             " is not a supported type");
256                     }
257                     
258                     // generate xsi:type attribute for value
259
ctx.attribute(ixsi, TYPE_ATTRIBUTE_NAME,
260                         XSD_PREFIX_LEAD + s_schemaTypesEnum.getName(type));
261                     ctx.closeStartContent();
262                     
263                     // handle the actual value conversion based on type
264
switch (type) {
265                         
266                         case BOOLEAN_TYPE:
267                             ctx.content(Utility.serializeBoolean
268                                 (((Boolean JavaDoc)value).booleanValue()));
269                             break;
270                             
271                         case BYTE_TYPE:
272                             ctx.content(Utility.
273                                 serializeByte(((Byte JavaDoc)value).byteValue()));
274                             break;
275                             
276                         case DOUBLE_TYPE:
277                             ctx.content(Utility.
278                                 serializeDouble(((Double JavaDoc)value).doubleValue()));
279                             break;
280                             
281                         case FLOAT_TYPE:
282                             ctx.content(Utility.
283                                 serializeFloat(((Float JavaDoc)value).floatValue()));
284                             break;
285                             
286                         case INT_TYPE:
287                             ctx.content(((Integer JavaDoc)value).intValue());
288                             break;
289                             
290                         case LONG_TYPE:
291                             ctx.content(Utility.
292                                 serializeLong(((Long JavaDoc)value).longValue()));
293                             break;
294                             
295                         case SHORT_TYPE:
296                             ctx.content(Utility.
297                                 serializeShort(((Short JavaDoc)value).shortValue()));
298                             break;
299                             
300                         case DATETIME_TYPE:
301                             ctx.content(Utility.serializeDateTime((Date JavaDoc)value));
302                             break;
303                             
304                         case DATE_TYPE:
305                             ctx.content(Utility.
306                                 serializeSqlDate((java.sql.Date JavaDoc)value));
307                             break;
308                             
309                         case TIME_TYPE:
310                             ctx.content(Utility.
311                                 serializeSqlTime((java.sql.Time JavaDoc)value));
312                             break;
313                             
314                         case BYTERRAY_TYPE:
315                             ctx.content(Utility.serializeBase64((byte[])value));
316                             break;
317                             
318                         case DECIMAL_TYPE:
319                         case INTEGER_TYPE:
320                         case STRING_TYPE:
321                             ctx.content(value.toString());
322                             break;
323                     }
324                     
325                     // finish with close tag for entry element
326
ctx.endTag(m_index, ENTRY_ELEMENT_NAME);
327                 }
328             }
329             
330             // finish with end tag for container element
331
ctx.endTag(m_index, m_name);
332             xwrite.popExtensionNamespaces();
333         }
334     }
335
336     /* (non-Javadoc)
337      * @see org.jibx.runtime.IUnmarshaller#isPresent(org.jibx.runtime.IUnmarshallingContext)
338      */

339      
340     public boolean isPresent(IUnmarshallingContext ctx) throws JiBXException {
341         return ctx.isAt(m_uri, m_name);
342     }
343
344     /* (non-Javadoc)
345      * @see org.jibx.runtime.IUnmarshaller#unmarshal(java.lang.Object,
346      * org.jibx.runtime.IUnmarshallingContext)
347      */

348      
349     public Object JavaDoc unmarshal(Object JavaDoc obj, IUnmarshallingContext ictx)
350         throws JiBXException {
351         
352         // make sure we're at the appropriate start tag
353
UnmarshallingContext ctx = (UnmarshallingContext)ictx;
354         if (!ctx.isAt(m_uri, m_name)) {
355             ctx.throwStartTagNameError(m_uri, m_name);
356         }
357         
358         // lookup the prefixes assigned to required namespaces
359
int nscnt = ctx.getActiveNamespaceCount();
360         String JavaDoc xsdlead = null;
361         for (int i = nscnt-1; i >= 0; i--) {
362             String JavaDoc uri = ctx.getActiveNamespaceUri(i);
363             if (XSD_NAMESPACE_URI.equals(uri)) {
364                 String JavaDoc prefix = ctx.getActiveNamespacePrefix(i);
365                 if (!"".equals(prefix)) {
366                     xsdlead = prefix + ':';
367                     break;
368                 }
369             }
370         }
371         if (xsdlead == null) {
372             throw new JiBXException
373                 ("Missing required schema namespace declaration");
374         }
375         
376         // create new hashmap if needed
377
int size = ctx.attributeInt(m_uri, SIZE_ATTRIBUTE_NAME, DEFAULT_SIZE);
378         Map JavaDoc map = (Map JavaDoc)obj;
379         if (map == null) {
380             map = new HashMap JavaDoc(size);
381         }
382         
383         // process all entries present in document
384
ctx.parsePastStartTag(m_uri, m_name);
385         String JavaDoc tdflt = xsdlead + "string";
386         while (ctx.isAt(m_uri, ENTRY_ELEMENT_NAME)) {
387             
388             // unmarshal key and type from start tag attributes
389
Object JavaDoc key = ctx.attributeText(m_uri, KEY_ATTRIBUTE_NAME, null);
390             String JavaDoc tname = ctx.attributeText(XSI_NAMESPACE_URI,
391                 TYPE_ATTRIBUTE_NAME, tdflt);
392             
393             // convert type name to type index number
394
int type = -1;
395             if (tname.startsWith(xsdlead)) {
396                 type = s_schemaTypesEnum.
397                     getValue(tname.substring(xsdlead.length()));
398             }
399             if (type < 0) {
400                 throw new JiBXException("Value of type " + tname +
401                     " with key " + key + " is not a supported type");
402             }
403             
404             // deserialize content as specified type
405
String JavaDoc text = ctx.parseElementText(m_uri, ENTRY_ELEMENT_NAME);
406             Object JavaDoc value = null;
407             switch (type) {
408                 
409                 case BOOLEAN_TYPE:
410                     value = Utility.parseBoolean(text) ?
411                         Boolean.TRUE : Boolean.FALSE;
412                     break;
413                     
414                 case BYTE_TYPE:
415                     value = new Byte JavaDoc(Utility.parseByte(text));
416                     break;
417                     
418                 case DOUBLE_TYPE:
419                     value = new Double JavaDoc(Utility.parseDouble(text));
420                     break;
421                     
422                 case FLOAT_TYPE:
423                     value = new Float JavaDoc(Utility.parseFloat(text));
424                     break;
425                     
426                 case INT_TYPE:
427                     value = new Integer JavaDoc(Utility.parseInt(text));
428                     break;
429                     
430                 case LONG_TYPE:
431                     value = new Long JavaDoc(Utility.parseLong(text));
432                     break;
433                     
434                 case SHORT_TYPE:
435                     value = new Short JavaDoc(Utility.parseShort(text));
436                     break;
437                     
438                 case DATETIME_TYPE:
439                     value = Utility.deserializeDateTime(text);
440                     break;
441                     
442                 case DATE_TYPE:
443                     value = Utility.deserializeSqlDate(text);
444                     break;
445                     
446                 case TIME_TYPE:
447                     value = Utility.deserializeSqlTime(text);
448                     break;
449                     
450                 case BYTERRAY_TYPE:
451                     value = Utility.deserializeBase64(text);
452                     break;
453                     
454                 case DECIMAL_TYPE:
455                     value = new BigDecimal JavaDoc(text);
456                     break;
457                     
458                 case INTEGER_TYPE:
459                     value = new BigInteger JavaDoc(text);
460                     break;
461                     
462                 case STRING_TYPE:
463                     value = text;
464                     break;
465             }
466             
467             // add key-value pair to map
468
map.put(key, value);
469         }
470         
471         // finish by skipping past wrapper end tag
472
ctx.parsePastEndTag(m_uri, m_name);
473         return map;
474     }
475 }
Popular Tags