1 /*
2 * Scope: a generic MVC framework.
3 * Copyright (c) 2000-2002, The Scope team
4 * All rights reserved.
5 *
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * Neither the name "Scope" nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 *
36 * $Id: ModelToXML.java,v 1.7 2002/09/05 15:41:45 ludovicc Exp $
37 */
38 package org.scopemvc.view.servlet.xml;
39
40
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45 import org.scopemvc.core.IntIndexSelector;
46 import org.scopemvc.core.PropertyManager;
47 import org.scopemvc.core.Selector;
48 import org.scopemvc.model.collection.ArrayModel;
49 import org.scopemvc.model.collection.ListModel;
50 import org.scopemvc.util.Debug;
51 import org.scopemvc.util.convertor.StringConvertor;
52 import org.scopemvc.util.convertor.StringConvertors;
53 import org.xml.sax.Attributes;
54 import org.xml.sax.ContentHandler;
55 import org.xml.sax.helpers.AttributesImpl;
56
57 /***
58 * <P>
59 *
60 * Converts a model into an XML document (as SAX events driving a
61 * ContentHandler). Uses a {@link PropertyIDGenerator} to create "path"
62 * attributes for all elements, and "id" attributes for model elements. </P> <P>
63 *
64 * Handles circular references using the "ID" and "IDREF" pattern. </P> <P>
65 *
66 * <PRE>
67 * (data id='_root')
68 * (name path='name')Steve(/name)
69 * (pets path='pets')
70 * (element index='0' path='pets.0')
71 * (data id='pets.0')
72 * (name path='pets.0.name')Trevor(/name)
73 * (/data)
74 * (/element)
75 * (/pets)
76 * (/data)
77 * </PRE> </P>
78 *
79 * @author <A HREF="mailto:smeyfroi@users.sourceforge.net">Steve Meyfroidt</A>
80 * @created 05 September 2002
81 * @version $Revision: 1.7 $ $Date: 2002/09/05 15:41:45 $
82 */
83 public class ModelToXML {
84
85 // Some constants for creating the XML
86 /***
87 * TODO: describe of the Field
88 */
89 protected final static String ID_ATTRIBUTE = "id";
90 /***
91 * TODO: describe of the Field
92 */
93 protected final static String IDREF_ATTRIBUTE = "idref";
94 /***
95 * TODO: describe of the Field
96 */
97 protected final static String CDATA_TYPE = "CDATA";
98 /***
99 * TODO: describe of the Field
100 */
101 protected final static String PATH_ATTRIBUTE = "id";
102 /***
103 * TODO: describe of the Field
104 */
105 protected final static Attributes EMPTY_ATTRIBUTES = new AttributesImpl();
106
107 /***
108 * Element to contain contents of a collection model.
109 */
110 protected static String COLLECTION_ELEMENT = "element";
111
112 /***
113 * Attribute to index the contents of a collection model.
114 */
115 protected static String COLLECTION_INDEX_ATTRIBUTE = "index";
116
117 /***
118 * Element to contain a model object.
119 */
120 protected static String MODEL_ELEMENT = "data";
121
122 private final static Log LOG = LogFactory.getLog(ModelToXML.class);
123
124 // Reuse these Attributes to avoid object creation (initialised in init()).
125 /***
126 * TODO: describe of the Field
127 */
128 protected AttributesImpl idAttributes;
129 /***
130 * TODO: describe of the Field
131 */
132 protected AttributesImpl idrefAttributes;
133 /***
134 * TODO: describe of the Field
135 */
136 protected AttributesImpl pathIndexAttributes;
137 /***
138 * TODO: describe of the Field
139 */
140 protected AttributesImpl indexAttributes;
141 /***
142 * TODO: describe of the Field
143 */
144 protected AttributesImpl pathAttributes;
145
146
147 /***
148 * Constructor for the ModelToXML object
149 */
150 public ModelToXML() {
151 init();
152 }
153
154
155 /***
156 * @param inContentHandler Drive this ContentHandler with the Model's SAX
157 * events.
158 * @param inModel Model object to write. null generates no SAX.
159 * @param inIDGenerator TODO: Describe the Parameter
160 * @throws Exception TODO: Describe the Exception
161 */
162 public void modelToXML(Object inModel, ContentHandler inContentHandler, PropertyIDGenerator inIDGenerator)
163 throws Exception {
164
165 inContentHandler.startDocument();
166
167 IdRefMap idRefMap = new IdRefMap();
168 String id = idRefMap.getNextId();
169 idAttributes.setValue(0, id);
170 idRefMap.storeModel(id, inModel);
171
172 inContentHandler.startElement("", MODEL_ELEMENT, MODEL_ELEMENT, idAttributes);
173
174 propertiesToXML(inModel, inContentHandler, inIDGenerator, idRefMap);
175
176 inContentHandler.endElement("", MODEL_ELEMENT, MODEL_ELEMENT);
177
178 inContentHandler.endDocument();
179 }
180
181
182 /***
183 * Initialise the Attributes that are reused during SAX generation.
184 */
185 protected void init() {
186 idAttributes = new AttributesImpl();
187 idAttributes.addAttribute("", ID_ATTRIBUTE, ID_ATTRIBUTE, CDATA_TYPE, "");
188
189 idrefAttributes = new AttributesImpl();
190 idrefAttributes.addAttribute("", IDREF_ATTRIBUTE, IDREF_ATTRIBUTE, CDATA_TYPE, "");
191
192 pathIndexAttributes = new AttributesImpl();
193 pathIndexAttributes.addAttribute("", PATH_ATTRIBUTE, PATH_ATTRIBUTE, CDATA_TYPE, "");
194 pathIndexAttributes.addAttribute("", COLLECTION_INDEX_ATTRIBUTE, COLLECTION_INDEX_ATTRIBUTE, CDATA_TYPE, "");
195
196 indexAttributes = new AttributesImpl();
197 indexAttributes.addAttribute("", COLLECTION_INDEX_ATTRIBUTE, COLLECTION_INDEX_ATTRIBUTE, CDATA_TYPE, "");
198
199 pathAttributes = new AttributesImpl();
200 pathAttributes.addAttribute("", PATH_ATTRIBUTE, PATH_ATTRIBUTE, CDATA_TYPE, "");
201 }
202
203
204 /***
205 * TODO: document the method
206 *
207 * @param inModel TODO: Describe the Parameter
208 * @param inContentHandler TODO: Describe the Parameter
209 * @param inIDGenerator TODO: Describe the Parameter
210 * @param inIdRefMap TODO: Describe the Parameter
211 * @throws Exception TODO: Describe the Exception
212 */
213 protected void propertiesToXML(Object inModel, ContentHandler inContentHandler, PropertyIDGenerator inIDGenerator, IdRefMap inIdRefMap)
214 throws Exception {
215 if (Debug.ON) {
216 Debug.assertTrue(inContentHandler != null, "null ContentHandler");
217 }
218 if (LOG.isDebugEnabled()) {
219 LOG.debug("propertiesToXML: idGenerator=" + inIDGenerator);
220 }
221
222 // Don't serialise null models
223 if (inModel == null) {
224 return;
225 }
226
227 // Get a PropertyManager for the model
228 PropertyManager manager = PropertyManager.getInstance(inModel);
229 if (Debug.ON) {
230 Debug.assertTrue(manager != null, "null manager");
231 }
232
233 // Serialise the properties using a SelectorIterator
234 Iterator i = manager.getSelectorIterator(inModel);
235 if (Debug.ON) {
236 Debug.assertTrue(i != null, "null Iterator");
237 }
238 while (i.hasNext()) {
239
240 try {
241 // selector
242 Object o = i.next();
243 if (Debug.ON) {
244 Debug.assertTrue(o instanceof Selector, "not a Selector: " + o);
245 }
246 Selector selector = (Selector) o;
247 Object property = manager.get(inModel, selector);
248
249 // Don't serialise null properties
250 if (property == null) {
251 continue;
252 }
253
254 // Don't serialise the array property of an ArrayModel ***** feels like a hack
255 if (inModel instanceof ArrayModel && ArrayModel.ARRAY.equals(selector)) {
256 continue;
257 }
258
259 // Don't serialise the list property of a ListModel ***** feels like a hack
260 if (inModel instanceof ListModel && ListModel.LIST.equals(selector)) {
261 continue;
262 }
263
264 StringConvertor convertor = StringConvertors.forClass(property.getClass());
265 inIDGenerator.startProperty(selector);
266 String path = inIDGenerator.getPropertyID();
267
268 // If selector is an IntIndexSelector then write the
269 // ... property like: <element index='0' path='xxx'>
270 // ... else write it like: <{selectorName} path='xxx'>
271 if (selector instanceof IntIndexSelector) {
272 if (path != null) {
273 pathIndexAttributes.setValue(0, path);
274 pathIndexAttributes.setValue(1, selector.getName());
275 inContentHandler.startElement("", COLLECTION_ELEMENT, COLLECTION_ELEMENT, pathIndexAttributes);
276 } else {
277 indexAttributes.setValue(0, selector.getName());
278 inContentHandler.startElement("", COLLECTION_ELEMENT, COLLECTION_ELEMENT, indexAttributes);
279 }
280 } else {
281 if (path != null) {
282 pathAttributes.setValue(0, path);
283 inContentHandler.startElement("", selector.getName(), selector.getName(), pathAttributes);
284 } else {
285 inContentHandler.startElement("", selector.getName(), selector.getName(), EMPTY_ATTRIBUTES);
286 }
287 }
288
289 // value, using StringConvertor
290 if (convertor != null) {
291 String value = convertor.valueAsString(property);
292 if (Debug.ON) {
293 Debug.assertTrue(value != null);
294 }
295 char[] valueElement = value.toCharArray();
296 inContentHandler.characters(valueElement, 0, valueElement.length);
297 } else {
298 // A Model so check if already serialised (look in the IdRefMap)
299 String previousId = inIdRefMap.findIdFor(property);
300 if (previousId != null) {
301 idrefAttributes.setValue(0, previousId);
302 inContentHandler.startElement("", MODEL_ELEMENT, MODEL_ELEMENT, idrefAttributes);
303 } else {
304 // ... if not previously serialised then do it here
305 String id = inIdRefMap.getNextId();
306 idAttributes.setValue(0, id);
307 inContentHandler.startElement("", MODEL_ELEMENT, MODEL_ELEMENT, idAttributes);
308 inIdRefMap.storeModel(id, property);
309 propertiesToXML(property, inContentHandler, inIDGenerator, inIdRefMap);
310 }
311 inContentHandler.endElement("", MODEL_ELEMENT, MODEL_ELEMENT);
312 }
313
314 // end of property
315 if (selector instanceof IntIndexSelector) {
316 inContentHandler.endElement("", COLLECTION_ELEMENT, COLLECTION_ELEMENT);
317 } else {
318 inContentHandler.endElement("", selector.getName(), selector.getName());
319 }
320
321 inIDGenerator.endProperty();
322
323 } catch (Exception e) {
324 // ignore: may not be able to get write-only properties
325 }
326 }
327 }
328 }
329
330 class IdRefMap {
331
332 private long nextId = 0;
333 private HashMap models = new HashMap();
334
335 /***
336 * Gets the next id
337 *
338 * @return The nextId value
339 */
340 String getNextId() {
341 String result = "_" + Long.toString(nextId);
342 nextId++;
343 return result;
344 }
345
346 /***
347 * TODO: document the method
348 *
349 * @param inModel TODO: Describe the Parameter
350 * @return TODO: Describe the Return Value
351 */
352 String findIdFor(Object inModel) {
353 return (String) models.get(inModel);
354 }
355
356 /***
357 * TODO: document the method
358 *
359 * @param inId TODO: Describe the Parameter
360 * @param inModel TODO: Describe the Parameter
361 */
362 void storeModel(String inId, Object inModel) {
363 models.put(inModel, inId);
364 }
365 }
This page was automatically generated by Maven