001: /*
002: * $Id: XmlSerializer.java,v 1.3 2004/01/20 15:55:18 ajzeneski Exp $
003: *
004: * Copyright (c) 2001, 2002 The Open For Business Project - www.ofbiz.org
005: *
006: * Permission is hereby granted, free of charge, to any person obtaining a
007: * copy of this software and associated documentation files (the "Software"),
008: * to deal in the Software without restriction, including without limitation
009: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
010: * and/or sell copies of the Software, and to permit persons to whom the
011: * Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included
014: * in all copies or substantial portions of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
017: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
021: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
022: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023: */
024: package org.ofbiz.entity.serialize;
025:
026: import java.io.FileNotFoundException;
027: import java.io.IOException;
028: import java.lang.ref.WeakReference;
029: import java.text.DateFormat;
030: import java.text.ParseException;
031: import java.text.SimpleDateFormat;
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.HashMap;
035: import java.util.HashSet;
036: import java.util.Hashtable;
037: import java.util.Iterator;
038: import java.util.LinkedList;
039: import java.util.Locale;
040: import java.util.Map;
041: import java.util.Properties;
042: import java.util.Stack;
043: import java.util.TreeMap;
044: import java.util.TreeSet;
045: import java.util.Vector;
046: import java.util.WeakHashMap;
047:
048: import javax.xml.parsers.ParserConfigurationException;
049:
050: import org.ofbiz.base.util.UtilMisc;
051: import org.ofbiz.base.util.UtilXml;
052: import org.ofbiz.base.util.Debug;
053: import org.ofbiz.entity.GenericDelegator;
054: import org.ofbiz.entity.GenericPK;
055: import org.ofbiz.entity.GenericValue;
056: import org.w3c.dom.Document;
057: import org.w3c.dom.Element;
058: import org.w3c.dom.Node;
059: import org.xml.sax.SAXException;
060:
061: /**
062: * <p><b>Title:</b> XmlSerializer
063: * <p><b>Description:</b> Simple XML serialization/deserialization routines with embedded type information
064: *
065: * @author <a href="mailto:jonesde@ofbiz.org">David E. Jones</a>
066: * @version $Revision: 1.3 $
067: * @since 2.0
068: */
069: public class XmlSerializer {
070: public static final String module = XmlSerializer.class.getName();
071:
072: private static WeakReference simpleDateFormatter;
073:
074: public static String serialize(Object object)
075: throws SerializeException, FileNotFoundException,
076: IOException {
077: Document document = UtilXml.makeEmptyXmlDocument("ofbiz-ser");
078: Element rootElement = document.getDocumentElement();
079:
080: rootElement.appendChild(serializeSingle(object, document));
081: return UtilXml.writeXmlDocument(document);
082: }
083:
084: public static Object deserialize(String content,
085: GenericDelegator delegator) throws SerializeException,
086: SAXException, ParserConfigurationException, IOException {
087: // readXmlDocument with false second parameter to disable validation
088: Document document = UtilXml.readXmlDocument(content, false);
089: if (document != null) {
090: Element rootElement = document.getDocumentElement();
091: // find the first element below the root element, that should be the object
092: Node curChild = rootElement.getFirstChild();
093:
094: while (curChild != null
095: && curChild.getNodeType() != Node.ELEMENT_NODE) {
096: curChild = curChild.getNextSibling();
097: }
098: if (curChild == null)
099: return null;
100: Element element = (Element) curChild;
101:
102: return deserializeSingle(element, delegator);
103: } else {
104: Debug.logWarning("Serialized document came back null",
105: module);
106: return null;
107: }
108: }
109:
110: public static Element serializeSingle(Object object,
111: Document document) throws SerializeException {
112: if (document == null)
113: return null;
114:
115: if (object == null)
116: return document.createElement("null");
117:
118: // - Standard Objects -
119: if (object instanceof String) {
120: return makeElement("std-String", object, document);
121: } else if (object instanceof Integer) {
122: return makeElement("std-Integer", object, document);
123: } else if (object instanceof Long) {
124: return makeElement("std-Long", object, document);
125: } else if (object instanceof Float) {
126: return makeElement("std-Float", object, document);
127: } else if (object instanceof Double) {
128: return makeElement("std-Double", object, document);
129: } else if (object instanceof Boolean) {
130: return makeElement("std-Boolean", object, document);
131: } else if (object instanceof Locale) {
132: return makeElement("std-Locale", object, document);
133: // - SQL Objects -
134: } else if (object instanceof java.sql.Timestamp) {
135: return makeElement("sql-Timestamp", object, document);
136: } else if (object instanceof java.sql.Date) {
137: return makeElement("sql-Date", object, document);
138: } else if (object instanceof java.sql.Time) {
139: return makeElement("sql-Time", object, document);
140: } else if (object instanceof java.util.Date) {
141: // NOTE: make sure this is AFTER the java.sql date/time objects since they inherit from java.util.Date
142: DateFormat formatter = getDateFormat();
143: String stringValue = null;
144:
145: synchronized (formatter) {
146: stringValue = formatter.format((java.util.Date) object);
147: }
148: return makeElement("std-Date", stringValue, document);
149: // return makeElement("std-Date", object, document);
150: } else if (object instanceof Collection) {
151: // - Collections -
152: String elementName = null;
153:
154: // these ARE order sensitive; for instance Stack extends Vector, so if Vector were first we would lose the stack part
155: if (object instanceof ArrayList) {
156: elementName = "col-ArrayList";
157: } else if (object instanceof LinkedList) {
158: elementName = "col-LinkedList";
159: } else if (object instanceof Stack) {
160: elementName = "col-Stack";
161: } else if (object instanceof Vector) {
162: elementName = "col-Vector";
163: } else if (object instanceof TreeSet) {
164: elementName = "col-TreeSet";
165: } else if (object instanceof HashSet) {
166: elementName = "col-HashSet";
167: } else {
168: // no specific type found, do general Collection, will deserialize as LinkedList
169: elementName = "col-Collection";
170: }
171:
172: // if (elementName == null) return serializeCustom(object, document);
173:
174: Collection value = (Collection) object;
175: Element element = document.createElement(elementName);
176: Iterator iter = value.iterator();
177:
178: while (iter.hasNext()) {
179: element.appendChild(serializeSingle(iter.next(),
180: document));
181: }
182: return element;
183: } else if (object instanceof GenericPK) {
184: // Do GenericEntity objects as a special case, use std XML import/export routines
185: GenericPK value = (GenericPK) object;
186:
187: return value.makeXmlElement(document, "eepk-");
188: } else if (object instanceof GenericValue) {
189: GenericValue value = (GenericValue) object;
190:
191: return value.makeXmlElement(document, "eeval-");
192: } else if (object instanceof Map) {
193: // - Maps -
194: String elementName = null;
195:
196: // these ARE order sensitive; for instance Properties extends Hashtable, so if Hashtable were first we would lose the Properties part
197: if (object instanceof HashMap) {
198: elementName = "map-HashMap";
199: } else if (object instanceof Properties) {
200: elementName = "map-Properties";
201: } else if (object instanceof Hashtable) {
202: elementName = "map-Hashtable";
203: } else if (object instanceof WeakHashMap) {
204: elementName = "map-WeakHashMap";
205: } else if (object instanceof TreeMap) {
206: elementName = "map-TreeMap";
207: } else {
208: // serialize as a simple Map implementation if nothing else applies, these will deserialize as a HashMap
209: elementName = "map-Map";
210: }
211:
212: Element element = document.createElement(elementName);
213: Map value = (Map) object;
214: Iterator iter = value.entrySet().iterator();
215:
216: while (iter.hasNext()) {
217: Map.Entry entry = (Map.Entry) iter.next();
218:
219: Element entryElement = document
220: .createElement("map-Entry");
221:
222: element.appendChild(entryElement);
223:
224: Element key = document.createElement("map-Key");
225:
226: entryElement.appendChild(key);
227: key.appendChild(serializeSingle(entry.getKey(),
228: document));
229: Element mapValue = document.createElement("map-Value");
230:
231: entryElement.appendChild(mapValue);
232: mapValue.appendChild(serializeSingle(entry.getValue(),
233: document));
234: }
235: return element;
236: }
237:
238: return serializeCustom(object, document);
239: }
240:
241: public static Element serializeCustom(Object object,
242: Document document) throws SerializeException {
243: // TODO: if nothing else, try looking up a class for the type in the properties file, the class should implement an interface or have a certain static method on it
244: throw new SerializeException(
245: "Cannot serialize object of class "
246: + object.getClass().getName());
247: }
248:
249: public static Element makeElement(String elementName, Object value,
250: Document document) {
251: if (value == null)
252: return document.createElement("null");
253: Element element = document.createElement(elementName);
254:
255: element.setAttribute("value", value.toString());
256: return element;
257: }
258:
259: public static Object deserializeSingle(Element element,
260: GenericDelegator delegator) throws SerializeException {
261: String tagName = element.getTagName();
262:
263: if (tagName.equals("null"))
264: return null;
265:
266: if (tagName.startsWith("std-")) {
267: // - Standard Objects -
268: if ("std-String".equals(tagName)) {
269: return element.getAttribute("value");
270: } else if ("std-Integer".equals(tagName)) {
271: String valStr = element.getAttribute("value");
272: return Integer.valueOf(valStr);
273: } else if ("std-Long".equals(tagName)) {
274: String valStr = element.getAttribute("value");
275: return Long.valueOf(valStr);
276: } else if ("std-Float".equals(tagName)) {
277: String valStr = element.getAttribute("value");
278: return Float.valueOf(valStr);
279: } else if ("std-Double".equals(tagName)) {
280: String valStr = element.getAttribute("value");
281: return Double.valueOf(valStr);
282: } else if ("std-Boolean".equals(tagName)) {
283: String valStr = element.getAttribute("value");
284: return Boolean.valueOf(valStr);
285: } else if ("std-Locale".equals(tagName)) {
286: String valStr = element.getAttribute("value");
287: return UtilMisc.parseLocale(valStr);
288: } else if ("std-Date".equals(tagName)) {
289: String valStr = element.getAttribute("value");
290: DateFormat formatter = getDateFormat();
291: java.util.Date value = null;
292:
293: try {
294: synchronized (formatter) {
295: value = formatter.parse(valStr);
296: }
297: } catch (ParseException e) {
298: throw new SerializeException(
299: "Could not parse date String: " + valStr, e);
300: }
301: return value;
302: }
303: } else if (tagName.startsWith("sql-")) {
304: // - SQL Objects -
305: if ("sql-Timestamp".equals(tagName)) {
306: String valStr = element.getAttribute("value");
307: return java.sql.Timestamp.valueOf(valStr);
308: } else if ("sql-Date".equals(tagName)) {
309: String valStr = element.getAttribute("value");
310: return java.sql.Date.valueOf(valStr);
311: } else if ("sql-Time".equals(tagName)) {
312: String valStr = element.getAttribute("value");
313: return java.sql.Time.valueOf(valStr);
314: }
315: } else if (tagName.startsWith("col-")) {
316: // - Collections -
317: Collection value = null;
318:
319: if ("col-ArrayList".equals(tagName)) {
320: value = new ArrayList();
321: } else if ("col-LinkedList".equals(tagName)) {
322: value = new LinkedList();
323: } else if ("col-Stack".equals(tagName)) {
324: value = new Stack();
325: } else if ("col-Vector".equals(tagName)) {
326: value = new Vector();
327: } else if ("col-TreeSet".equals(tagName)) {
328: value = new TreeSet();
329: } else if ("col-HashSet".equals(tagName)) {
330: value = new HashSet();
331: } else if ("col-Collection".equals(tagName)) {
332: value = new LinkedList();
333: }
334:
335: if (value == null) {
336: return deserializeCustom(element);
337: } else {
338: Node curChild = element.getFirstChild();
339:
340: while (curChild != null) {
341: if (curChild.getNodeType() == Node.ELEMENT_NODE) {
342: value.add(deserializeSingle((Element) curChild,
343: delegator));
344: }
345: curChild = curChild.getNextSibling();
346: }
347: return value;
348: }
349: } else if (tagName.startsWith("map-")) {
350: // - Maps -
351: Map value = null;
352:
353: if ("map-HashMap".equals(tagName)) {
354: value = new HashMap();
355: } else if ("map-Properties".equals(tagName)) {
356: value = new Properties();
357: } else if ("map-Hashtable".equals(tagName)) {
358: value = new Hashtable();
359: } else if ("map-WeakHashMap".equals(tagName)) {
360: value = new WeakHashMap();
361: } else if ("map-TreeMap".equals(tagName)) {
362: value = new TreeMap();
363: } else if ("map-Map".equals(tagName)) {
364: value = new HashMap();
365: }
366:
367: if (value == null) {
368: return deserializeCustom(element);
369: } else {
370: Node curChild = element.getFirstChild();
371:
372: while (curChild != null) {
373: if (curChild.getNodeType() == Node.ELEMENT_NODE) {
374: Element curElement = (Element) curChild;
375:
376: if ("map-Entry".equals(curElement.getTagName())) {
377: Element mapKeyElement = UtilXml
378: .firstChildElement(curElement,
379: "map-Key");
380: Element keyElement = null;
381: Node tempNode = mapKeyElement
382: .getFirstChild();
383:
384: while (tempNode != null) {
385: if (tempNode.getNodeType() == Node.ELEMENT_NODE) {
386: keyElement = (Element) tempNode;
387: break;
388: }
389: tempNode = tempNode.getNextSibling();
390: }
391: if (keyElement == null)
392: throw new SerializeException(
393: "Could not find an element under the map-Key");
394:
395: Element mapValueElement = UtilXml
396: .firstChildElement(curElement,
397: "map-Value");
398: Element valueElement = null;
399:
400: tempNode = mapValueElement.getFirstChild();
401: while (tempNode != null) {
402: if (tempNode.getNodeType() == Node.ELEMENT_NODE) {
403: valueElement = (Element) tempNode;
404: break;
405: }
406: tempNode = tempNode.getNextSibling();
407: }
408: if (valueElement == null)
409: throw new SerializeException(
410: "Could not find an element under the map-Value");
411:
412: value.put(deserializeSingle(keyElement,
413: delegator), deserializeSingle(
414: valueElement, delegator));
415: }
416: }
417: curChild = curChild.getNextSibling();
418: }
419: return value;
420: }
421: } else if (tagName.startsWith("eepk-")) {
422: return delegator.makePK(element);
423: } else if (tagName.startsWith("eeval-")) {
424: return delegator.makeValue(element);
425: }
426:
427: return deserializeCustom(element);
428: }
429:
430: public static Object deserializeCustom(Element element)
431: throws SerializeException {
432: throw new SerializeException(
433: "Cannot deserialize element named "
434: + element.getTagName());
435: }
436:
437: /**
438: * Returns the DateFormat used to serialize and deserialize <code>java.util.Date</code> objects.
439: * This format is NOT used to format any of the java.sql subtypes of java.util.Date.
440: * A <code>WeakReference</code> is used to maintain a reference to the DateFormat object
441: * so that it can be created and garbage collected as needed.
442: *
443: * @return the DateFormat used to serialize and deserialize <code>java.util.Date</code> objects.
444: */
445: private static DateFormat getDateFormat() {
446: DateFormat formatter = null;
447:
448: if (simpleDateFormatter != null) {
449: formatter = (DateFormat) simpleDateFormatter.get();
450: }
451: if (formatter == null) {
452: formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
453: simpleDateFormatter = new WeakReference(formatter);
454: }
455: return formatter;
456: }
457: }
|