KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > zeus > Marshaller


1 /*
2  * Enhydra Java Application Server Project
3  *
4  * The contents of this file are subject to the Enhydra Public License
5  * Version 1.1 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License on
7  * the Enhydra web site ( http://www.enhydra.org/ ).
8  *
9  * Software distributed under the License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11  * the License for the specific terms governing rights and limitations
12  * under the License.
13  *
14  * The Initial Developer of the Enhydra Application Server is Lutris
15  * Technologies, Inc. The Enhydra Application Server and portions created
16  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17  * All Rights Reserved.
18  */

19 package org.enhydra.zeus;
20
21 import org.enhydra.zeus.result.StreamResult;
22 import org.enhydra.zeus.util.CapitalizationUtils;
23 import org.enhydra.zeus.util.NamingUtils;
24
25 import java.io.IOException JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.lang.reflect.Field JavaDoc;
28 import java.lang.reflect.Method JavaDoc;
29 import java.lang.reflect.InvocationTargetException JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.LinkedList JavaDoc;
32 import java.util.List JavaDoc;
33
34 // JDOM imports
35
import org.jdom.Attribute;
36 import org.jdom.Document;
37 import org.jdom.DocType;
38 import org.jdom.Element;
39 import org.jdom.output.XMLOutputter;
40
41 /**
42  * <p>
43  * <code>Marshaller</code> takes a Java <code>Object</code> instance and writes
44  * out an XML representation of that object, with each property (variable)
45  * of the instance and its value.
46  * </p>
47  *
48  * @author Brett McLaughlin
49  */

50 public class Marshaller {
51     
52     /** List of methods to ignore in marshalling. */
53     protected List JavaDoc methodsToIgnore;
54
55     /**
56      * Prefix to be prepended to the name of every generated
57      * interface and class.
58      */

59     protected String JavaDoc namePrefix;
60     
61     /**
62      * <p>
63      * Simple constructor.
64      * </p>
65      */

66     public Marshaller() {
67         methodsToIgnore = new LinkedList JavaDoc();
68         methodsToIgnore.add("getClass");
69         methodsToIgnore.add(new StringBuffer JavaDoc("get")
70                                     .append(CapitalizationUtils.initialUpper(
71                                         ZeusDefaults.PCDATA_JAVA_NAME))
72                                     .toString());
73         namePrefix = "";
74     }
75     
76     /**
77      * <p>
78      * This will set an (additional) method to ignore in marshalling. The
79      * argument to this method should be the method name to ignore, without
80      * arguments or parenthesis. To ignore the method
81      * <code>getFactory()</code>, the value would simply be "getFactory"
82      * for this method.
83      * </p>
84      *
85      * @param methodToIgnore name of method to ignore.
86      */

87     public void ignoreMethod(String JavaDoc methodToIgnore) {
88         methodsToIgnore.add(methodToIgnore);
89     }
90    
91     /**
92      * <p>
93      * This method allows the developer to set a prefix that
94      * will be prepended to the name of every generated
95      * interface and class.
96      * </p>
97      *
98      * @param namePrefix prefix that will be prepended to the
99      * name of every generated interface and class
100      */

101     public void setNamePrefix(String JavaDoc namePrefix) {
102         if (namePrefix != null) {
103             this.namePrefix = namePrefix;
104         }
105     }
106
107     /**
108      * <p>
109      * This method allows the developer to get the prefix that
110      * will be prepended to the name of every generated
111      * interface and class. Returns an empty string in the
112      * case that a prefix is not prepended.
113      * </p>
114      *
115      * @return <code>String</code> prefix that will be prepended
116      * to the name of every generated interface and class
117      */

118     public String JavaDoc getNamePrefix() {
119         return namePrefix;
120     }
121     
122     /**
123      * <p>
124      * This method is the public entry point for marshalling an object into
125      * an XML instance document.
126      * </p>
127      *
128      * @param obj <code>Object</code> to convert to XML.
129      * @param result <code>Result</code> to write XML to.
130      * @throws <code>IOException</code> when errors in output occur.
131      */

132     public void marshal(Object JavaDoc obj, Result result)
133         throws IOException JavaDoc {
134
135         if (obj instanceof UnmarshalledObject) {
136             marshal((UnmarshalledObject)obj, result);
137         } else {
138             marshal(new UnmarshalledObject(obj), result);
139         }
140     }
141
142     /**
143      * <p>
144      * This method is the public entry point for marshalling an object into
145      * an XML instance document. It requires that an object previously
146      * unmarshalled by Zeus (an instance of <code>UnmarshalledObject</code>)
147      * be supplied.
148      * </p>
149      *
150      * @param obj <code>UnmarshalledObject</code> to convert to XML.
151      * @param result <code>Result</code> to write XML to.
152      * @throws <code>IOException</code> when errors in output occur.
153      */

154     public void marshal(UnmarshalledObject obj, Result result)
155         throws IOException JavaDoc {
156
157         // Root Element is the start of recursion
158
Element root = getXMLRepresentation(obj.getObject());
159         Document doc = new Document(root);
160
161         // Set a doctype if needed
162
String JavaDoc systemID = obj.getSystemID();
163         String JavaDoc publicID = obj.getPublicID();
164         if ((systemID != null) || (publicID != null)) {
165             DocType docType = new DocType(root.getName(), publicID, systemID);
166             doc.setDocType(docType);
167         }
168         
169         // XXX: For now, I don't know how we would write to anthing but
170
// a StreamResult. So this is a temp hack, for sure!
171
if (result instanceof StreamResult) {
172             StreamResult streamResult = (StreamResult)result;
173
174             // Use 2 space indentation and line feeds
175
XMLOutputter outputter = new XMLOutputter(" ", true);
176             outputter.output(doc, streamResult.getWriter());
177             streamResult.getWriter().flush();
178         } else {
179             throw new IllegalArgumentException JavaDoc(
180                 "Marshaller currently only works with StreamResults.");
181         }
182     }
183
184     /**
185      * <p>
186      * This is the granular portion of binding; a Java <code>Object</code> is
187      * converted into an XML element (in JDOM form), using recursion for any
188      * children.
189      * </p>
190      *
191      * @param obj <code>Object</code> to get the XML element representation for.
192      * @return <code>Element</code> - representation of
193      * <code>Object</code> in XML.
194      * @throws <code>IOException</code> when errors occur in binding.
195      */

196     private Element getXMLRepresentation(Object JavaDoc obj) throws IOException JavaDoc {
197         Class JavaDoc objectClass = obj.getClass();
198
199         // Get the name of the element for this object
200
String JavaDoc objectName = objectClass.getName();
201
202         // Strip the package name.
203
int index = -1;
204         if ((index = objectName.lastIndexOf('.')) != -1) {
205             objectName = objectName.substring((index + 1));
206         }
207
208         // Get the attributes order, if possible
209
boolean zeusGenerated = false;
210         String JavaDoc[] attributes = new String JavaDoc[0];
211         String JavaDoc[] childElements = new String JavaDoc[0];
212         try {
213             // Get the element's XML name
214
Field JavaDoc xmlName =
215                 objectClass.getField(ZeusDefaults.XML_NAME_FIELD);
216             objectName = (String JavaDoc) xmlName.get(obj);
217
218             // Get attributes
219
Field JavaDoc attributesField =
220                 objectClass.getField(ZeusDefaults.ATTRIBUTE_ARRAY_FIELD);
221             attributes = (String JavaDoc[])attributesField.get(obj);
222             
223             // Get elements
224
Field JavaDoc elementsField =
225                 objectClass.getField(ZeusDefaults.ELEMENT_ARRAY_FIELD);
226             childElements = (String JavaDoc[])elementsField.get(obj);
227
228             zeusGenerated = true;
229         } catch (NoSuchFieldException JavaDoc e) {
230             // Not a Zeus-generated object
231
} catch (IllegalAccessException JavaDoc e) {
232             throw new IOException JavaDoc(e.getMessage());
233         } catch (SecurityException JavaDoc e) {
234             throw new IOException JavaDoc(e.getMessage());
235         }
236
237         // Strip the name prefix
238
if (objectName.startsWith(namePrefix)) {
239             objectName = objectName.substring(namePrefix.length());
240         }
241
242         // If this is an "Impl" class, remove that from the name
243
if ((index = objectName.indexOf("Impl")) != -1) {
244             objectName = objectName.substring(0, index);
245         }
246
247         Element element = new Element(objectName);
248
249         // Deal with the object; either by direct access or introspection
250
if (zeusGenerated) {
251             // Deal with attributes
252
for (int i=0; i<attributes.length; i++) {
253                 String JavaDoc attribute = attributes[i];
254
255                 // Build the method name
256
String JavaDoc methodName = new StringBuffer JavaDoc("get")
257                     .append(CapitalizationUtils.initialUpper(attribute))
258                     .toString();
259
260                 // Lookup the method
261
try {
262                     Method JavaDoc method = objectClass.getMethod(methodName, null);
263                     String JavaDoc value = (String JavaDoc)method.invoke(obj, null);
264
265                     if (value == null) {
266                         continue;
267                     } else {
268                         element.setAttribute(attribute, value);
269                     }
270                 } catch (NoSuchMethodException JavaDoc e) {
271                     throw new IOException JavaDoc("An expected method, " +
272                         methodName + ", was not found, and is required.");
273                 } catch (IllegalAccessException JavaDoc e) {
274                     throw new IOException JavaDoc(e.getMessage());
275                 } catch (InvocationTargetException JavaDoc e) {
276                     throw new IOException JavaDoc(e.getMessage());
277                 } catch (SecurityException JavaDoc e) {
278                     throw new IOException JavaDoc(e.getMessage());
279                 }
280             }
281
282             // Deal with elements
283
for (int i=0; i<childElements.length; i++) {
284                 String JavaDoc childElementName = childElements[i];
285  
286                 // Build the method name
287
String JavaDoc methodName = new StringBuffer JavaDoc("get")
288                     .append(CapitalizationUtils.initialUpper(childElementName))
289                     .toString();
290
291                 // Lookup the "get" method
292
try {
293                     Method JavaDoc method = objectClass.getMethod(methodName, null);
294                     Object JavaDoc value = method.invoke(obj, null);
295
296                     if (value != null) {
297                         // See if this is a primitive or complex object
298
String JavaDoc valueClassName = value.getClass().getName();
299                         Element childElement = null;
300                         if (valueClassName.startsWith("java.lang")) {
301                             // Handle primitive objects
302
childElement = new Element(childElementName);
303                             childElement.setText(String.valueOf(value));
304                         } else {
305                             // Handle complex objects
306
childElement = getXMLRepresentation(value);
307                         }
308                         element.addContent(childElement);
309    
310                         // We're done; bail out
311
continue;
312                     }
313                 } catch (NoSuchMethodException JavaDoc e) {
314                     // Not in the form of a "get" method
315
} catch (IllegalAccessException JavaDoc e) {
316                     throw new IOException JavaDoc(e.getMessage());
317                 } catch (InvocationTargetException JavaDoc e) {
318                     throw new IOException JavaDoc(e.getMessage());
319                 } catch (SecurityException JavaDoc e) {
320                     throw new IOException JavaDoc(e.getMessage());
321                 }
322
323                 // If we got here, we're still looking for the method to call
324
// Build the "List" method name
325
methodName = new StringBuffer JavaDoc("get")
326                     .append(CapitalizationUtils.initialUpper(childElementName))
327                     .append("List")
328                     .toString();
329
330                 // Lookup the "List" method
331
try {
332                     Method JavaDoc method = objectClass.getMethod(methodName, null);
333                     List JavaDoc values = (List JavaDoc)method.invoke(obj, null);
334                     Object JavaDoc value = null;
335                     for (Iterator JavaDoc j = values.iterator(); j.hasNext(); ) {
336                         value = j.next();
337                         if (value == null) {
338                             continue;
339                         } else {
340                             // See if this is a primitive or complex object
341
String JavaDoc valueClassName = value.getClass().getName();
342                             Element childElement = null;
343                             if (valueClassName.startsWith("java.lang")) {
344                                 // Handle primitive objects
345
childElement = new Element(childElementName);
346                                 childElement.setText(String.valueOf(value));
347                             } else {
348                                 // Handle complex objects
349
childElement = getXMLRepresentation(value);
350                             }
351                             element.addContent(childElement);
352
353                             // We're done; bail out
354
continue;
355                         }
356                     }
357
358                     // We're done, so bail out
359
continue;
360                 } catch (NoSuchMethodException JavaDoc e) {
361                     // Not in the form of a "List" method
362
} catch (IllegalAccessException JavaDoc e) {
363                     throw new IOException JavaDoc(e.getMessage());
364                 } catch (InvocationTargetException JavaDoc e) {
365                     throw new IOException JavaDoc(e.getMessage());
366                 } catch (SecurityException JavaDoc e) {
367                     throw new IOException JavaDoc(e.getMessage());
368                 }
369             }
370
371
372             // Even if the element does not have child elements, it still can
373
// have content. Check for that text content here.
374
String JavaDoc methodName = new StringBuffer JavaDoc("get")
375                                     .append(CapitalizationUtils.initialUpper(
376                                         ZeusDefaults.PCDATA_JAVA_NAME))
377                                     .toString();
378
379                 // Lookup the "Value" method
380
try {
381                     Method JavaDoc method = objectClass.getMethod(methodName, null);
382                     String JavaDoc value = (String JavaDoc)method.invoke(obj, null);
383                     element.setText(value);
384                 } catch (NoSuchMethodException JavaDoc e) {
385                 // throw new IOException("The getValue() method " +
386
// "could not be found.");
387
} catch (IllegalAccessException JavaDoc e) {
388                     throw new IOException JavaDoc(e.getMessage());
389                 } catch (InvocationTargetException JavaDoc e) {
390                     throw new IOException JavaDoc(e.getMessage());
391                 } catch (SecurityException JavaDoc e) {
392                     throw new IOException JavaDoc(e.getMessage());
393                 }
394            
395         // Deal with object through reflection if not Zeus-generated
396
} else {
397             Method JavaDoc[] methods = objectClass.getMethods();
398             for (int i=0; i<methods.length; i++) {
399                 // Deal with attributes
400
Method JavaDoc method = methods[i];
401                 String JavaDoc methodName = method.getName();
402                 if ((methodName.startsWith("get")) &&
403                     (!methodsToIgnore.contains(methodName)) &&
404                     (!methodName.endsWith("List"))) {
405
406                     // Get the value for this method
407
try {
408                         Object JavaDoc o = method.invoke(obj, new Object JavaDoc[] { });
409                         // Ensure that the element exists
410
if (o == null) {
411                             continue;
412                         }
413
414                         // Remove the "get" and lower the initial letter
415
String JavaDoc propertyName =
416                             NamingUtils.getXMLElementNameFromAccessor(
417                                 method.getName());
418                     
419                         // Determine if it's primitive
420
if (o.getClass().getName().startsWith("java.lang.")) {
421                             // If it's a primitive, add as an attribute
422
element.setAttribute(propertyName, o.toString());
423                         } else {
424                             // Otherwise, recurse and add new element as child
425
element.addContent(getXMLRepresentation(o));
426                         }
427                     } catch (IllegalAccessException JavaDoc e) {
428                         throw new IOException JavaDoc(e.getMessage());
429                     } catch (InvocationTargetException JavaDoc e) {
430                         throw new IOException JavaDoc(e.getMessage());
431                     }
432                 } else if ((methodName.startsWith("get")) &&
433                            (methodName.endsWith("List"))) {
434                     // Deal with List data
435
try {
436                         List JavaDoc items = (List JavaDoc)method.invoke(obj, new Object JavaDoc[] { });
437                         for (Iterator JavaDoc j = items.iterator(); j.hasNext(); ) {
438                             element.addContent(getXMLRepresentation(j.next()));
439                         }
440                     } catch (IllegalAccessException JavaDoc e) {
441                         throw new IOException JavaDoc(e.getMessage());
442                     } catch (InvocationTargetException JavaDoc e) {
443                         throw new IOException JavaDoc(e.getMessage());
444                     }
445                 } else if (methodName.equals(new StringBuffer JavaDoc("get")
446                                     .append(CapitalizationUtils.initialUpper(
447                                         ZeusDefaults.PCDATA_JAVA_NAME))
448                                     .toString())) {
449                     // Deal with Data field
450
try {
451                         String JavaDoc content =
452                             (String JavaDoc)method.invoke(obj, new Object JavaDoc[] { });
453                         element.setText(content);
454                     } catch (IllegalAccessException JavaDoc e) {
455                         throw new IOException JavaDoc(e.getMessage());
456                     } catch (InvocationTargetException JavaDoc e) {
457                         throw new IOException JavaDoc(e.getMessage());
458                     }
459                 }
460             }
461         }
462
463         return element;
464     }
465 }
466
Popular Tags