KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.xml;
21
22 import java.io.StringWriter JavaDoc;
23 import java.text.SimpleDateFormat JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Date JavaDoc;
26 import java.util.Iterator JavaDoc;
27
28 import javax.xml.transform.OutputKeys JavaDoc;
29 import javax.xml.transform.Result JavaDoc;
30 import javax.xml.transform.Source JavaDoc;
31 import javax.xml.transform.Transformer JavaDoc;
32 import javax.xml.transform.TransformerFactory JavaDoc;
33 import javax.xml.transform.dom.DOMSource JavaDoc;
34 import javax.xml.transform.stream.StreamResult JavaDoc;
35
36 import org.apache.cayenne.CayenneRuntimeException;
37 import org.w3c.dom.Document JavaDoc;
38 import org.w3c.dom.Element JavaDoc;
39 import org.w3c.dom.Node JavaDoc;
40
41 /**
42  * A helper class to encode objects to XML.
43  *
44  * @since 1.2
45  * @author Kevin J. Menard, Jr., Andrus Adamchik
46  */

47 public class XMLEncoder {
48
49     private XMLMappingDescriptor mappingDescriptor;
50
51     // temp ivars that define the structure of the encoding stack.
52
private Document JavaDoc document;
53     private Node JavaDoc parent;
54     private String JavaDoc tagOverride;
55     private boolean inProgress;
56
57     /**
58      * Creates new XMLEncoder.
59      */

60     public XMLEncoder() {
61
62     }
63
64     /**
65      * Creates new XMLEncoder that will use a mapping descriptor loaded via provided URL.
66      */

67     public XMLEncoder(String JavaDoc mappingUrl) {
68         this.mappingDescriptor = new XMLMappingDescriptor(mappingUrl);
69     }
70
71     /**
72      * A callback for XMLSerializable objects to add a node to an encoding tree.
73      */

74     public void setRoot(String JavaDoc xmlTag, String JavaDoc type) {
75         setRoot(xmlTag, type, true);
76     }
77
78     /**
79      * A callback method for XMLSerializable objects to encode an object property. Note
80      * that the object must call "setRoot" prior to encoding its properties.
81      *
82      * @param xmlTag The name of the XML element used to represent the property.
83      * @param value The object's property value to encode.
84      */

85     public void encodeProperty(String JavaDoc xmlTag, Object JavaDoc value) {
86         encodeProperty(xmlTag, value, true);
87     }
88
89     /**
90      * Encodes an object using "root" as a root tag.
91      */

92     public String JavaDoc encode(Object JavaDoc object) throws CayenneRuntimeException {
93         return encode("root", object);
94     }
95
96     /**
97      * Encodes using provided root XML tag.
98      */

99     public String JavaDoc encode(String JavaDoc rootTag, Object JavaDoc object) throws CayenneRuntimeException {
100
101         try {
102             initDocument(rootTag, object != null ? object.getClass().getName() : null);
103
104             if (mappingDescriptor != null) {
105                 mappingDescriptor.getRootEntity().setObject(object);
106                 mappingDescriptor.getRootEntity().encodeAsXML(this);
107             }
108             else {
109                 encodeProperty(rootTag, object);
110             }
111
112             if (object instanceof Collection JavaDoc) {
113                 return nodeToString(getRootNode(true));
114             } else {
115                 return nodeToString(getRootNode(false));
116             }
117         }
118         finally {
119             // make sure we can restart the encoder...
120
inProgress = false;
121         }
122     }
123
124     /**
125      * Resets the encoder to process a new object tree.
126      */

127     void initDocument(String JavaDoc rootTag, String JavaDoc type) {
128         this.document = XMLUtil.newBuilder().newDocument();
129         this.parent = document;
130
131         // create a "synthetic" root
132
Element JavaDoc root = push(rootTag);
133         if (type != null) {
134             root.setAttribute("type", type);
135         }
136
137         inProgress = true;
138     }
139
140     /**
141      * Returns a root DOM node of the encoder.
142      */

143     Node JavaDoc getRootNode(boolean forceSyntheticRoot) {
144         if (document == null) {
145             return null;
146         }
147
148         // if synthetic root has a single child, use child as a root
149
Node JavaDoc root = document.getDocumentElement();
150         
151         if (!forceSyntheticRoot && root.getChildNodes().getLength() == 1) {
152             root = root.getFirstChild();
153         }
154
155         return root;
156     }
157
158     String JavaDoc nodeToString(Node JavaDoc rootNode) {
159
160         StringWriter JavaDoc out = new StringWriter JavaDoc();
161         Result JavaDoc result = new StreamResult JavaDoc(out);
162
163         Source JavaDoc source = new DOMSource JavaDoc(rootNode);
164
165         try {
166             Transformer JavaDoc xformer = TransformerFactory.newInstance().newTransformer();
167             xformer.setOutputProperty(OutputKeys.METHOD, "xml");
168             xformer.setOutputProperty(OutputKeys.INDENT, "yes");
169             xformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
170             xformer.transform(source, result);
171         }
172         catch (Exception JavaDoc e) {
173             throw new CayenneRuntimeException("XML transformation error", e);
174         }
175
176         return out.toString().trim() + System.getProperty("line.separator");
177     }
178
179     void setRoot(String JavaDoc xmlTag, String JavaDoc type, boolean push) {
180
181         // all public methods must implicitly init the document
182
if (!inProgress) {
183             initDocument(xmlTag, type);
184         }
185
186         if (tagOverride != null) {
187             xmlTag = tagOverride;
188             tagOverride = null;
189         }
190
191         // new node will be a peer rather than a child of current parent.
192
if (!push) {
193             pop();
194         }
195
196         Element JavaDoc element = push(xmlTag);
197         if (type != null) {
198             element.setAttribute("type", type);
199         }
200     }
201
202     void encodeProperty(String JavaDoc xmlTag, Object JavaDoc value, boolean useType) {
203         // all public methods must be able to implicitly init the document
204
if (!inProgress) {
205             String JavaDoc type = (useType && value != null) ? value.getClass().getName() : null;
206             initDocument(xmlTag, type);
207         }
208
209         if (value == null) {
210             return;
211         }
212         else if (value instanceof XMLSerializable) {
213             encodeSerializable(xmlTag, (XMLSerializable) value);
214         }
215         else if (value instanceof Collection JavaDoc) {
216             encodeCollection(xmlTag, (Collection JavaDoc) value, useType);
217         }
218         else {
219             encodeSimple(xmlTag, value, useType);
220         }
221     }
222
223     void encodeSimple(String JavaDoc xmlTag, Object JavaDoc object, boolean useType) {
224
225         // simple properties will not call setRoot, so push manually
226
Element JavaDoc node = push(xmlTag);
227
228         if (useType) {
229             node.setAttribute("type", object.getClass().getName());
230         }
231
232         // Dates need special handling
233
if (object instanceof Date JavaDoc) {
234             SimpleDateFormat JavaDoc sdf = new SimpleDateFormat JavaDoc(XMLUtil.DEFAULT_DATE_FORMAT);
235             node.appendChild(document.createTextNode(sdf.format(object)));
236         }
237         else {
238             node.appendChild(document.createTextNode(object.toString()));
239         }
240
241         pop();
242     }
243
244     void encodeSerializable(String JavaDoc xmlTag, XMLSerializable object) {
245         // don't allow children to reset XML tag name ... unless they are at the root
246
// level
247
if (document.getDocumentElement() != parent) {
248             tagOverride = xmlTag;
249         }
250
251         object.encodeAsXML(this);
252
253         tagOverride = null;
254         pop();
255     }
256
257     /**
258      * Encodes a collection of objects, attaching them to the current root.
259      *
260      * @param xmlTag The name of the root XML element for the encoded collection.
261      * @param c The collection to encode.
262      */

263     void encodeCollection(String JavaDoc xmlTag, Collection JavaDoc c, boolean useType) {
264
265         Iterator JavaDoc it = c.iterator();
266         while (it.hasNext()) {
267             // encode collection without doing push/pop so that its elements are encoded
268
// without an intermediate grouping node.
269
encodeProperty(xmlTag, it.next(), useType);
270         }
271
272         if (c.size() == 1) {
273             ((Element JavaDoc) parent.getLastChild()).setAttribute("forceList", "YES");
274         }
275     }
276
277     /**
278      * Creates a new element, pushing it onto encoding stack.
279      */

280     private Element JavaDoc push(String JavaDoc xmlTag) {
281
282         Element JavaDoc child = document.createElement(xmlTag);
283         this.parent.appendChild(child);
284         this.parent = child;
285         return child;
286     }
287
288     /**
289      * Pops the top element from the encoding stack.
290      */

291     Node JavaDoc pop() {
292         Node JavaDoc old = parent;
293         parent = parent.getParentNode();
294         return old;
295     }
296 }
297
Popular Tags