View Javadoc

1   /**
2    * Copyright 2005-2006 the original author or authors.
3    *
4    * Licensed under the Gnu General Pubic License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with
6    * the License. You may obtain a copy of the License at
7    *
8    *      http://www.opensource.org/licenses/gpl-license.php
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13   * See the Gnu General Public License for more details.
14   */
15  package org.figure8.join.core.persistence;
16  
17  import net.sf.hibernate.Hibernate;
18  import net.sf.hibernate.SessionFactory;
19  import net.sf.hibernate.HibernateException;
20  import net.sf.hibernate.proxy.HibernateProxy;
21   import net.sf.hibernate.proxy.LazyInitializer;
22  import net.sf.hibernate.proxy.HibernateProxyHelper;
23  import net.sf.hibernate.engine.SessionFactoryImplementor;
24  import net.sf.hibernate.collection.CollectionPersister;
25  import net.sf.hibernate.collection.PersistentCollection;
26  import net.sf.hibernate.type.Type;
27  import net.sf.hibernate.type.SetType;
28  import net.sf.hibernate.type.BagType;
29  import net.sf.hibernate.type.MapType;
30  import net.sf.hibernate.type.DateType;
31  import net.sf.hibernate.type.ListType;
32  import net.sf.hibernate.type.StringType;
33  import net.sf.hibernate.type.TimestampType;
34  import net.sf.hibernate.type.AbstractComponentType;
35  import net.sf.hibernate.type.PersistentCollectionType;
36  import net.sf.hibernate.persister.ClassPersister;
37  import net.sf.hibernate.util.StringHelper;
38  
39  import java.io.Writer;
40  import java.io.IOException;
41  import java.util.Map;
42  import java.util.Set;
43  import java.util.Date;
44  import java.util.List;
45  import java.util.HashMap;
46  import java.util.HashSet;
47  import java.util.Iterator;
48  import java.util.Collection;
49  import java.lang.reflect.Array;
50  /**
51   * This utility class is an Xml export handler for Hibernate entity objects.
52   * It is adapted from the one written by Ara Abrahamian and available on
53   * http://opensource.atlassian.com/projects/hibernate/browse/HB-493.
54   * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
55   * @version $Revision: 1.1 $
56   */
57  public class HibernateXmlExportHandler{
58  
59     // Attributes ---------------------------------------------------------------
60  
61     /** The collection of entity objects to export as Xml */
62     private Collection objects = null;
63     /** The encoding to use for writing Xml stream */
64     private String encoding = HibernateXmlDatabinder.DEFAULT_ENCODING;
65  
66     /* Collections for handling processed objects */
67     private Set processedObjects = new HashSet();
68     private Set associatedObjects = new HashSet();
69     private Map excludedObjects = new HashMap();
70  
71     /** The Hiberante factory implementor */
72     private SessionFactoryImplementor factory = null;
73  
74  
75     // Constructors -------------------------------------------------------------
76  
77     /** Creates a new instance of XmlExportHandler */
78     public HibernateXmlExportHandler(SessionFactory factory, Collection entityObjects){
79        this.factory = (SessionFactoryImplementor)factory;
80        this.objects = entityObjects;
81     }
82  
83  
84     // Public -------------------------------------------------------------------
85  
86     /** @param encoding The encoding for Xml output */
87     public void setEncoding(String encoding){
88        this.encoding = encoding;
89     }
90  
91     /**
92      * Write the entity objects specified during construction as their Xml form
93      * @param writer Use this writer to output the Xml representation
94      * @throws HibernateException if metadata on entity objects cannot be retrieved
95      * @throws IOException if writing problems occurs
96      */
97     public void writeEntityObjects(Writer writer) throws HibernateException, IOException{
98        // Write objects stream in Xml.
99        String date = Hibernate.TIMESTAMP.toString(new Date(), factory);
100       writer.write("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
101       writer.write("<hibernate-generic datetime=\"" + date + "\">\n");
102       // Process each entity object.
103       Iterator objectsIter = objects.iterator();
104       while (objectsIter != null && objectsIter.hasNext()){
105          Object object = objectsIter.next();
106          // Start writing object.
107          writer.write(HibernateXmlDatabinder.CONST_OPEN_OBJECT_TAG);
108          // Write class description attributes.
109          object = initializeIfProxy(object);
110          writeClass(writer, object.getClass());
111          writer.write(HibernateXmlDatabinder.END_TAG_CARRIAGE_RETURN);
112          //
113          ClassPersister persister = factory.getPersister(object.getClass());
114          if (persister.hasIdentifierPropertyOrEmbeddedCompositeIdentifier()){
115             Object id = persister.getIdentifier(object);
116             writeProperty(writer, persister.getIdentifierPropertyName(), persister.getIdentifierType(), id,
117                     HibernateXmlDatabinder.CONST_COMPOSITE_ID, HibernateXmlDatabinder.CONST_ID, null, false);
118          }
119          Type types[] = persister.getPropertyTypes();
120          Object values[] = persister.getPropertyValues(object);
121          String names[] = persister.getPropertyNames();
122          for (int i=0; i<types.length; i++)
123             if (!excludedObjects.containsKey(values[i]))
124                writeProperty(writer, names[i], types[i], values[i], HibernateXmlDatabinder.CONST_COMPONENT,
125                        HibernateXmlDatabinder.CONST_PROPERTY, HibernateXmlDatabinder.CONST_COLLECTION, false);
126          writer.write(HibernateXmlDatabinder.CONST_CLOSE_OBJECT_TAG);
127       }
128 
129       // Close Xml stream.
130       writer.write("</hibernate-generic>");
131    }
132 
133 
134    // Private -----------------------------------------------------------------
135 
136    /** @return Object lazy initializer if object if a proxy */
137    private Object initializeIfProxy(Object object){
138       if (!(object instanceof HibernateProxy))
139          return object;
140       LazyInitializer li = HibernateProxyHelper.getLazyInitializer((HibernateProxy)object);
141       return li.getImplementation();
142    }
143 
144    /** Write the Xml class qualifier onto writer */
145    private void writeClass(Writer writer, Class clazz) throws IOException{
146       String className = clazz.getName();
147       String unqualifiedClassName = StringHelper.unqualify(className);
148       String packageName = StringHelper.qualifier(className);
149       writer.write(" " + HibernateXmlDatabinder.CONST_CLASS + "=\"" + unqualifiedClassName + "\" package=\"" + packageName + "\"");
150    }
151 
152    /** Write the Xml property export onto writer */
153    private void writeProperty(Writer writer, String name, Type type, Object value, String componentName, String propertyName, String collectionName,
154             boolean doType) throws HibernateException, IOException{
155       if (type.isComponentType())
156          writeComponentType(writer, name, type, value, componentName, doType);
157       else if (type.isPersistentCollectionType())
158          writeCollectionType(writer, name, type, value, collectionName, doType);
159       else if (type.isEntityType())
160          writeEntityType(writer, name, type, value, propertyName, doType);
161       else
162          writeOtherType(writer, name, type, value, propertyName, doType);
163    }
164 
165    /** Write the Xml component export onto writer */
166    private void writeComponentType(Writer writer, String name, Type type, Object value, String componentName, boolean doType)
167         throws HibernateException, IOException{
168       if (value != null){
169          AbstractComponentType componenttype = (AbstractComponentType)type;
170          writer.write(HibernateXmlDatabinder.LEFT_CHEVRON + componentName);
171          if (name != null) appendAttribute(writer, HibernateXmlDatabinder.CONST_NAME, name);
172          if (doType) appendAttribute(writer, HibernateXmlDatabinder.CONST_CLASS, type.getName());
173          writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
174          String properties[] = componenttype.getPropertyNames();
175          Object subvalues[] = componenttype.getPropertyValues(value, null);
176          Type subtypes[] = componenttype.getSubtypes();
177          for (int j=0; j<properties.length; j++)
178             writeProperty(writer, properties[j], subtypes[j], subvalues[j], HibernateXmlDatabinder.CONST_COMPONENT, HibernateXmlDatabinder.CONST_PROPERTY, HibernateXmlDatabinder.CONST_COLLECTION, false);
179          writer.write(HibernateXmlDatabinder.START_CLOSE_TAG + componentName + HibernateXmlDatabinder.END_TAG_CARRIAGE_RETURN);
180        }
181    }
182 
183    /** Write the Xml collection export onto writer */
184    private void writeCollectionType(Writer writer, String name, Type type, Object value, String collectionName, boolean doType)
185          throws HibernateException, IOException{
186       if (value == null)
187          return;
188       PersistentCollectionType collectiontype = (PersistentCollectionType)type;
189       String role = collectiontype.getRole();
190       CollectionPersister persister = factory.getCollectionPersister(role);
191       if (persister.isArray() && (Array.getLength(value) == 0))
192          return;
193       else if ((value instanceof Collection) && ((Collection)value).isEmpty())
194          return;
195       if (persister.isArray())
196          collectionName = "array";
197       writer.write(HibernateXmlDatabinder.LEFT_CHEVRON + collectionName);
198       if (name != null) appendAttribute(writer, HibernateXmlDatabinder.CONST_NAME, name);
199       if (!persister.isArray() && doType) appendAttribute(writer, HibernateXmlDatabinder.CONST_CLASS, type.getName());
200       Type elemType = persister.getElementType();
201 
202       if (persister.isArray()){
203          writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
204          int length = Array.getLength(value);
205          for (int i=0; i<length; i++)
206             writeProperty(writer, null, elemType, Array.get(value, i), HibernateXmlDatabinder.CONST_COMPOSITE_ELEMENT,
207                     HibernateXmlDatabinder.CONST_ELEMENT, HibernateXmlDatabinder.CONST_SUBCOLLECTION, false);
208 
209       }
210       else{
211          boolean wasInitialized = false;
212          if (value instanceof PersistentCollection){
213             PersistentCollection persistentCollection = (PersistentCollection)value;
214             wasInitialized = persistentCollection.wasInitialized();
215          }
216          if (!persister.isLazy() || wasInitialized)
217             if (type instanceof ListType){
218                // Add information on index-type.
219                appendAttribute(writer, HibernateXmlDatabinder.CONST_INDEX_TYPE, "integer");
220                writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
221                Iterator iter = ((List)value).iterator();
222                while (iter.hasNext()){
223                   Object collectionItem = iter.next();
224                   if (!excludedObjects.containsKey(collectionItem))
225                      writeProperty(writer, null, elemType, collectionItem,
226                              HibernateXmlDatabinder.CONST_COMPOSITE_ELEMENT, HibernateXmlDatabinder.CONST_ELEMENT,
227                              HibernateXmlDatabinder.CONST_SUBCOLLECTION, false);
228                }
229              }
230              else if ((type instanceof SetType) || (type instanceof BagType)){
231                 writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
232                 Iterator iter = ((Collection)value).iterator();
233                 while (iter.hasNext()){
234                    Object collectionItem = iter.next();
235                    if (!excludedObjects.containsKey(collectionItem))
236                       writeProperty(writer, null, elemType, collectionItem,
237                              HibernateXmlDatabinder.CONST_COMPOSITE_ELEMENT, HibernateXmlDatabinder.CONST_ELEMENT,
238                              HibernateXmlDatabinder.CONST_SUBCOLLECTION, false);
239                }
240              }
241              else if (type instanceof MapType){
242                 // Add information on index type.
243                 Type indexType = persister.getIndexType();
244                 appendAttribute(writer, HibernateXmlDatabinder.CONST_INDEX_TYPE, indexType.getName());
245                 writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
246                 // Retrieve persister for index entity.
247                 ClassPersister indexPersister = factory.getPersister(indexType.getReturnedClass());
248                 // Browse map elements.
249                 Iterator iter = ((Map)value).entrySet().iterator();
250                 while (iter.hasNext()){
251                    java.util.Map.Entry e = (java.util.Map.Entry)iter.next();
252                    Object collectionItem = e.getValue();
253                    if (!excludedObjects.containsKey(collectionItem))
254                       writeIndexedElementType(writer, null, elemType, collectionItem,
255                              HibernateXmlDatabinder.CONST_ELEMENT, indexPersister.getIdentifier(e.getKey()).toString(), false);
256                }
257              }
258       }
259       writer.write(HibernateXmlDatabinder.START_CLOSE_TAG + collectionName + HibernateXmlDatabinder.END_TAG_CARRIAGE_RETURN);
260    }
261 
262    /** Write the Xml entity export onto writer */
263    private void writeEntityType(Writer writer, String name, Type type, Object value, String propertyName, boolean doType)
264          throws HibernateException, IOException{
265       if ((value = initializeIfProxy(value)) != null){
266          writer.write(HibernateXmlDatabinder.LEFT_CHEVRON + propertyName);
267          if (name != null) appendAttribute(writer, HibernateXmlDatabinder.CONST_NAME, name);
268          if (doType) appendAttribute(writer, HibernateXmlDatabinder.CONST_TYPE, type.getName());
269          writeClass(writer, value.getClass());
270          writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
271 
272          ClassPersister persister = factory.getPersister(value.getClass());
273          if (persister.hasIdentifierPropertyOrEmbeddedCompositeIdentifier()){
274             Type idType = persister.getIdentifierType();
275             Object id = persister.getIdentifier(value);
276             writeProperty(writer, persister.getIdentifierPropertyName(), idType, id,
277                     HibernateXmlDatabinder.CONST_COMPOSITE_ID, HibernateXmlDatabinder.CONST_ID, null, false);
278          }
279 
280          if (!processedObjects.contains(value) && !objects.contains(value))
281             associatedObjects.add(value);
282 
283          writer.write(HibernateXmlDatabinder.START_CLOSE_TAG + propertyName + HibernateXmlDatabinder.END_TAG_CARRIAGE_RETURN);
284       }
285    }
286 
287    /** Write the Xml other type export onto writer */
288    private void writeOtherType(Writer writer, String name, Type type, Object value, String propertyName, boolean doType)
289          throws HibernateException, IOException{
290       writer.write(HibernateXmlDatabinder.LEFT_CHEVRON + propertyName);
291       if (name != null) appendAttribute(writer, HibernateXmlDatabinder.CONST_NAME, name);
292       if (doType) appendAttribute(writer, HibernateXmlDatabinder.CONST_TYPE, type.getName());
293       if (value != null){
294          writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
295          String xmlValue = type.toString(value, factory);
296          if ((type instanceof StringType))
297             writer.write(HibernateXmlDatabinder.CONST_OPEN_CDATA + xmlValue + HibernateXmlDatabinder.CONST_CLOSE_CDATA);
298          else if (type instanceof TimestampType)
299             writer.write(HibernateXmlDatabinder.formatTimestamp((Date)value));
300          else if (type instanceof DateType)
301             writer.write(HibernateXmlDatabinder.formatDate((Date)value));
302          else
303             writer.write(xmlValue);
304          writer.write(HibernateXmlDatabinder.START_CLOSE_TAG + propertyName + HibernateXmlDatabinder.END_TAG_CARRIAGE_RETURN);
305       }
306       else writer.write("/" + HibernateXmlDatabinder.END_TAG_CARRIAGE_RETURN);
307    }
308 
309    /** Write the Xml indexed element export onto writer */
310    private void writeIndexedElementType(Writer writer, String name, Type type, Object value, String propertyName, String indexId, boolean doType)
311          throws HibernateException, IOException{
312       if ((value = initializeIfProxy(value)) != null){
313          writer.write(HibernateXmlDatabinder.LEFT_CHEVRON + propertyName);
314          if (name != null) appendAttribute(writer, HibernateXmlDatabinder.CONST_NAME, name);
315          if (doType) appendAttribute(writer, HibernateXmlDatabinder.CONST_TYPE, type.getName());
316          writeClass(writer, value.getClass());
317          if (indexId != null) appendAttribute(writer, HibernateXmlDatabinder.CONST_INDEX_ID, indexId);
318          writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
319 
320          ClassPersister persister = factory.getPersister(value.getClass());
321          if (persister.hasIdentifierPropertyOrEmbeddedCompositeIdentifier()){
322             Type idType = persister.getIdentifierType();
323             Object id = persister.getIdentifier(value);
324             writeProperty(writer, persister.getIdentifierPropertyName(), idType, id,
325                     HibernateXmlDatabinder.CONST_COMPOSITE_ID, HibernateXmlDatabinder.CONST_ID, null, false);
326          }
327 
328          if (!processedObjects.contains(value) && !objects.contains(value))
329             associatedObjects.add(value);
330 
331          writer.write(HibernateXmlDatabinder.START_CLOSE_TAG + propertyName + HibernateXmlDatabinder.END_TAG_CARRIAGE_RETURN);
332       }
333    }
334 
335    /** Write the Xml attribute export onto writer */
336    private void appendAttribute(Writer writer, String name, String value) throws IOException{
337       writer.write(" " + name + "=\"" + value + "\"");
338    }
339 }