KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > xml > XMLEncoder


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne.xml;
57
58 import java.util.Collection JavaDoc;
59 import java.util.Iterator JavaDoc;
60 import java.util.List JavaDoc;
61
62 import org.jdom.Document;
63 import org.jdom.Element;
64 import org.jdom.output.Format;
65 import org.jdom.output.XMLOutputter;
66 import org.objectstyle.cayenne.CayenneRuntimeException;
67 import org.objectstyle.cayenne.util.Util;
68
69 /**
70  * A helper class to encode objects to XML.
71  *
72  * @since 1.2
73  * @author Kevin J. Menard, Jr.
74  */

75 public class XMLEncoder {
76
77     /** The root of the XML document for the encoded object. */
78     protected Element root;
79
80     /**
81      * Encodes object using provided mapping file.
82      *
83      * @param object The object to encode.
84      * @param mappingFile The mapping file that defines the encoding.
85      * @return The encoded object in XML.
86      * @throws CayenneRuntimeException
87      */

88     public String JavaDoc encode(Object JavaDoc object, String JavaDoc mappingFile)
89             throws CayenneRuntimeException {
90
91         this.root = new XMLMappingUtil(mappingFile).encode(object);
92         return getXml();
93     }
94
95     /**
96      * Retrieves the XML representation of the encoded object.
97      *
98      * @return The encoded object in XML.
99      */

100     public String JavaDoc getXml() {
101         Document doc = new Document(root);
102
103         // Return the XML tree as a pretty, formatted string.
104
// TODO Make this output configurable.
105
XMLOutputter serializer = new XMLOutputter();
106         Format format = Format.getPrettyFormat();
107
108         format.setOmitDeclaration(true);
109         format.setLineSeparator("\n");
110
111         serializer.setFormat(format);
112
113         String JavaDoc ret = serializer.outputString(doc);
114         doc.detachRootElement();
115
116         ret = stripDoubleLineBreaks(ret);
117
118         return ret;
119     }
120     
121     // compensates for the lack of String.replace in JDK1.3
122
private String JavaDoc stripDoubleLineBreaks(String JavaDoc string) {
123         if (Util.isEmptyString(string)) {
124             return string;
125         }
126
127         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(string.length());
128         char previous = 0;
129         for (int i = 0; i < string.length(); i++) {
130             char c = string.charAt(i);
131             if (c == previous && previous == '\n') {
132                 continue;
133             }
134
135             buffer.append(c);
136             previous = c;
137         }
138
139         return buffer.toString();
140     }
141
142     /**
143      * Sets the root node for the encoded object. This must be called prior to any of the
144      * encodeXXX() methods. In order to use the encoder to encoder more than one object,
145      * this method should be called again.
146      *
147      * @param xmlTag The name of the XML root element.
148      * @param type The fully specified class name of the encoded object.
149      */

150     public void setRoot(String JavaDoc xmlTag, String JavaDoc type) {
151         root = new Element(xmlTag);
152         root.setAttribute("type", type);
153     }
154
155     /**
156      * Returns the root JDOM element of the encoded object.
157      *
158      * @return The root JDOM element.
159      */

160     public Element getRoot() {
161         return root;
162     }
163
164     /**
165      * Encodes an object's property value to XML.
166      *
167      * @param xmlTag The name of the XML element used to represent the property.
168      * @param property The object's property value to encode.
169      */

170     public void encodeProperty(String JavaDoc xmlTag, Object JavaDoc property) {
171         Element temp;
172
173         if (property instanceof XMLSerializable) {
174             XMLSerializable element = (XMLSerializable) property;
175
176             // Make a back-up copy of the root and then nullify it. This is done so we can
177
// recycle the encoder object.
178
Element rootCopy = root;
179             root = null;
180
181             // Use the current encoder object to encode the property.
182
element.encodeAsXML(this);
183
184             // Store a copy of the encoded property, which is currently stored in the
185
// root, since the encoder object was recycled.
186
// Also, change the element name to that which was provided as a parameter to
187
// the method.
188
temp = root;
189             temp.setName(xmlTag);
190
191             // Restore the old root.
192
root = rootCopy;
193         }
194         // TODO This block operates differently from the others. Here, root can never be
195
// null and we need to add a list of items. Having two points of exit is not good.
196
else if (property instanceof Collection JavaDoc) {
197             Collection JavaDoc c = (Collection JavaDoc) property;
198             root.addContent(encodeCollection(xmlTag, c));
199
200             return;
201         }
202         else {
203             temp = new Element(xmlTag);
204
205             temp.setAttribute("type", property.getClass().getName());
206             temp.setText(property.toString());
207         }
208
209         if (null != root) {
210             root.addContent(temp);
211         }
212         else {
213             root = temp;
214         }
215     }
216
217     /**
218      * Encodes a collection of objects.
219      *
220      * @param xmlTag The name of the root XML element for the encoded collection.
221      * @param c The collection to encode.
222      * @return A flat list of the encoded objects (i.e., there is no root node).
223      */

224     protected List JavaDoc encodeCollection(String JavaDoc xmlTag, Collection JavaDoc c) {
225         // We need a root node to add content to, but we'll be tossing the root and return
226
// only its children. Thus, it really doesn't matter what we pass to setRoot()
227
// here.
228
XMLEncoder encoder = new XMLEncoder();
229         encoder.setRoot("root", "root");
230
231         // Encode each of the elements in the collection.
232
for (Iterator JavaDoc it = c.iterator(); it.hasNext();) {
233             encoder.encodeProperty(xmlTag, it.next());
234         }
235
236         // Retrieve the list of encoded objects, removing their parent (the root) node.
237
List JavaDoc ret = encoder.getRoot().removeContent();
238
239         // If there was only a single element encoded, add the forceList attribute so that
240
// the decoder will know to treat this as a collection rather than as a single
241
// item.
242
if (1 == ret.size()) {
243             Element e = (Element) ret.get(0);
244
245             e.setAttribute("forceList", "YES");
246         }
247
248         return ret;
249     }
250
251     /**
252      * Encodes a collection of objects. This intended to be used to encode a list of
253      * DataObjects returned from a query or relationship retrieval.
254      *
255      * @param xmlTag The name of the root XML element for the encoded collection.
256      * @param dataObjects The collection to encode.
257      * @return An XML string representing the encoded collection.
258      */

259     public String JavaDoc encodeList(String JavaDoc xmlTag, List JavaDoc dataObjects) {
260         // It really doesn't matter what the root tag name is, but appending list
261
// to the name to be used for the elements seems like a sensible solution.
262
XMLEncoder encoder = new XMLEncoder();
263         encoder.setRoot(xmlTag + "List", dataObjects.getClass().getName());
264
265         encoder.getRoot().addContent(encoder.encodeCollection(xmlTag, dataObjects));
266
267         return encoder.getXml();
268     }
269
270     /**
271      * Encodes a collection of objects. This intended to be used to encode a list of
272      * DataObjects returned from a query or relationship retrieval.
273      *
274      * @param xmlTag The name of the root XML element for the encoded collection.
275      * @param dataObjects The collection to encode.
276      * @param mappingUrl The mapping file that defines how the list elements should be
277      * encoded.
278      * @return An XML string representing the encoded collection.
279      */

280     public String JavaDoc encodeList(String JavaDoc xmlTag, List JavaDoc dataObjects, String JavaDoc mappingUrl) {
281         // It really doesn't matter what the root tag name is, but appending list
282
// to the name to be used for the elements seems like a sensible solution.
283
XMLEncoder encoder = new XMLEncoder();
284         encoder.setRoot(xmlTag + "List", dataObjects.getClass().getName());
285
286         final Element tempRoot = encoder.root;
287
288         // This is a bit funky, but basically we're encoding each element individually
289
// using a new MappingUtils instance each time -- this may be a performance hit,
290
// we'll have to see, but it does allow straightforward code reuse.
291
for (Iterator JavaDoc it = dataObjects.iterator(); it.hasNext();) {
292             encoder.encode(it.next(), mappingUrl);
293             tempRoot.addContent(encoder.root);
294         }
295
296         encoder.root = tempRoot;
297
298         return encoder.getXml();
299     }
300 }
Popular Tags