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
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
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
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
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
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
103 Iterator objectsIter = objects.iterator();
104 while (objectsIter != null && objectsIter.hasNext()){
105 Object object = objectsIter.next();
106
107 writer.write(HibernateXmlDatabinder.CONST_OPEN_OBJECT_TAG);
108
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
130 writer.write("</hibernate-generic>");
131 }
132
133
134
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
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
243 Type indexType = persister.getIndexType();
244 appendAttribute(writer, HibernateXmlDatabinder.CONST_INDEX_TYPE, indexType.getName());
245 writer.write(HibernateXmlDatabinder.RIGHT_CHEVRON);
246
247 ClassPersister indexPersister = factory.getPersister(indexType.getReturnedClass());
248
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 }