KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > forms > util > XMLAdapter


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

16 package org.apache.cocoon.forms.util;
17
18 import java.util.Iterator JavaDoc;
19 import java.util.Locale JavaDoc;
20
21 import org.apache.cocoon.xml.AbstractXMLConsumer;
22 import org.apache.cocoon.forms.datatype.Datatype;
23 import org.apache.cocoon.forms.datatype.convertor.ConversionResult;
24 import org.apache.cocoon.forms.formmodel.Action;
25 import org.apache.cocoon.forms.formmodel.AggregateField;
26 import org.apache.cocoon.forms.formmodel.BooleanField;
27 import org.apache.cocoon.forms.formmodel.ContainerWidget;
28 import org.apache.cocoon.forms.formmodel.DataWidget;
29 import org.apache.cocoon.forms.formmodel.Form;
30 import org.apache.cocoon.forms.formmodel.MultiValueField;
31 import org.apache.cocoon.forms.formmodel.Repeater;
32 import org.apache.cocoon.forms.formmodel.Widget;
33 import org.apache.excalibur.xml.sax.XMLizable;
34 import org.xml.sax.Attributes JavaDoc;
35 import org.xml.sax.ContentHandler JavaDoc;
36 import org.xml.sax.SAXException JavaDoc;
37 import org.xml.sax.helpers.AttributesImpl JavaDoc;
38
39 /**
40  * Adapter class that wraps a <code>Form</code> object and makes it
41  * possible to populate a widget hierarchy from XML in form of SAX
42  * events and serialize the content of the widget hierarchy as XML.
43  *
44  * The XML format is such that there is one XML element for each
45  * widget and the element get the widgets id as name. Exceptions from
46  * this is that the elements in a repeater gets the name
47  * <code>item</code> and a attribute <code>position</code> with the
48  * position of the repeater child, instead of just a number (which is
49  * not allowed as element name). Childs of a
50  * <code>MultiValueField</code> are also embeded within a
51  * <code>item</code> element. If the <code>Form</code> widget does
52  * not have an id it get the name <code>uknown</code>.
53  *
54  * An <code>AggregateField</code> can both be interpreted as one value
55  * and as several widgets. This ambiguity is resolved by chosing to emit
56  * the single value rather than the fields as XML. For population of the
57  * form both forms are however allowed.
58  *
59  * @version $Id: XMLAdapter.java 161264 2005-04-14 12:32:26Z sylvain $
60  */

61 public class XMLAdapter extends AbstractXMLConsumer implements XMLizable {
62
63     /** Name of element in list. */
64     private final static String JavaDoc ITEM = "item";
65     /** Name of unkown element. */
66     private final static String JavaDoc UNKNOWN = "unknown";
67     /** Name of position attribute in list. */
68     private final static String JavaDoc POSITION = "position";
69     /** The namespace prefix of this component. */
70     private final static String JavaDoc PREFIX = "";
71     /** The namespace URI of this component. */
72     private final static String JavaDoc URI = "";
73     /** The <code>ContentHandler</code> receiving SAX events. */
74     private ContentHandler JavaDoc contentHandler;
75     /** The <code>Widget</code> to read and write XML to. */
76     private Widget widget;
77     /** The <code>Widget</code> that we are currently writing to. */
78     private Widget currentWidget = null;
79     /** The <code>Locale</code> that decides how to convert widget values to strings */
80     private Locale JavaDoc locale;
81     /** Is a <code>MultiValueField</code> handled? */
82     private boolean isMultiValueItem = false;
83     /** The buffer used to receive character events */
84     private StringBuffer JavaDoc textBuffer;
85
86
87     /**
88      * Wrap a <code>Form</code> with an <code>XMLAdapter</code>
89      *
90      */

91     public XMLAdapter(Widget widget) {
92         this.widget = widget;
93         this.locale = Locale.US;
94     }
95
96     /**
97      * Set the locale used for conversion between XML data and Java objects
98      */

99     public void setLocale(Locale JavaDoc locale) {
100         this.locale = locale;
101     }
102
103     /**
104      * Get the locale used for conversion between XML data and Java objects
105      */

106     public Locale JavaDoc getLocale() {
107         return this.locale;
108     }
109
110     /* ================ SAX -> Widget ================ */
111
112     /*
113      * The current state during handling of input events is described
114      * by <code>currentWidget</code> that points to the widget that is
115      * beeing populated. The state that the population has not began
116      * yet or that it is finished is encoded by setting
117      * <code>currentWidget</code> to <code>null</code> and the state of
118      * being within a <code>item</code> within a
119      * <code>MultiValueField</code> is encoded by setting the variable
120      * <code>isMultiValueItem</code> to true.
121     */

122
123     /**
124      * Receive notification of the beginning of an element.
125      *
126      * @param uri The Namespace URI, or the empty string if the element has no
127      * Namespace URI or if Namespace
128      * processing is not being performed.
129      * @param loc The local name (without prefix), or the empty string if
130      * Namespace processing is not being performed.
131      * @param raw The raw XML 1.0 name (with prefix), or the empty string if
132      * raw names are not available.
133      * @param a The attributes attached to the element. If there are no
134      * attributes, it shall be an empty Attributes object.
135      */

136     public void startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc a)
137     throws SAXException JavaDoc {
138         handleText();
139         if (this.currentWidget == null) {
140             // The name of the root element is ignored
141
this.currentWidget = this.widget;
142             return;
143         } else if (this.currentWidget instanceof ContainerWidget) {
144             Widget child = ((ContainerWidget)this.currentWidget).getChild(loc);
145             if (child != null)
146                 this.currentWidget = child;
147             else
148                 throw new SAXException JavaDoc("There is no widget with id: " + loc +
149                                        " as child to: " + this.currentWidget.getId());
150         } else if (this.currentWidget instanceof Repeater) {
151             // In a repeater the XML elements are added in the order
152
// they are recieved, the position attribute is not used
153
if (ITEM.equals(loc)) {
154                 Repeater repeater = (Repeater)currentWidget;
155                 currentWidget = repeater.addRow();
156             } else
157                 throw new SAXException JavaDoc("The element: " + loc +
158                                        " is not allowed as a direct child of a Repeater");
159         } else if (this.currentWidget instanceof MultiValueField) {
160             this.isMultiValueItem = true;
161             if (!ITEM.equals(loc))
162                 throw new SAXException JavaDoc("The element: " + loc +
163                                        " is not allowed as a direct child of a MultiValueField");
164         }
165     }
166
167
168     /**
169      * Receive notification of the end of an element.
170      *
171      * @param uri The Namespace URI, or the empty string if the element has no
172      * Namespace URI or if Namespace
173      * processing is not being performed.
174      * @param loc The local name (without prefix), or the empty string if
175      * Namespace processing is not being performed.
176      * @param raw The raw XML 1.0 name (with prefix), or the empty string if
177      * raw names are not available.
178      */

179     public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
180     throws SAXException JavaDoc {
181         handleText();
182         if (this.currentWidget == null)
183             throw new SAXException JavaDoc("Wrong state");
184
185         String JavaDoc id = this.currentWidget.getId();
186
187         if (this.currentWidget instanceof Form) {
188             this.currentWidget = null;
189             return;
190         } else if (this.currentWidget instanceof AggregateField) {
191             ((AggregateField)this.currentWidget).combineFields();
192         } else if (this.currentWidget instanceof Repeater.RepeaterRow) {
193             id = ITEM;
194         } else if (this.currentWidget instanceof MultiValueField && loc.equals(ITEM)) {
195             this.isMultiValueItem = false;
196             return;
197         }
198
199         if (loc.equals(id))
200             this.currentWidget = this.currentWidget.getParent();
201         else
202             throw new SAXException JavaDoc("Unexpected element, was: " + loc +
203                                    " expected: " + id);
204     }
205
206     /**
207      * Receive notification of character data.
208      *
209      * @param ch The characters from the XML document.
210      * @param start The start position in the array.
211      * @param len The number of characters to read from the array.
212      */

213     public void characters(char ch[], int start, int len)
214     throws SAXException JavaDoc {
215         // Buffer text, as a single text node can be sent in several chunks.
216
if (this.textBuffer == null) {
217             this.textBuffer = new StringBuffer JavaDoc();
218         }
219         this.textBuffer.append(ch, start, len);
220     }
221     
222     /**
223      * Handle text nodes, if any. Called on every potential text node boundary,
224      * i.e. start and end element events.
225      *
226      * @throws SAXException
227      */

228     private void handleText() throws SAXException JavaDoc {
229         if (this.textBuffer == null)
230             return;
231         
232         String JavaDoc input = this.textBuffer.toString().trim();
233         this.textBuffer = null; // clear buffer
234
if (input.length() == 0)
235             return;
236
237         if (this.currentWidget instanceof MultiValueField && isMultiValueItem) {
238             MultiValueField field = (MultiValueField)this.currentWidget;
239             Datatype type = field.getDatatype();
240             ConversionResult conv =
241                 type.convertFromString(input, this.locale);
242             if (conv.isSuccessful()) {
243                 Object JavaDoc[] values = (Object JavaDoc[])field.getValue();
244                 int valLen = values == null ? 0 : values.length;
245                 Object JavaDoc[] newValues = new Object JavaDoc[valLen + 1];
246                 for (int i = 0; i < valLen; i++)
247                     newValues[i] = values[i];
248                 newValues[valLen] = conv.getResult();
249                 field.setValues(newValues);
250             } else
251                 throw new SAXException JavaDoc("Could not convert: " + input +
252                                        " to " + type.getTypeClass());
253         } else if (this.currentWidget instanceof DataWidget) {
254             DataWidget data = (DataWidget)this.currentWidget;
255             Datatype type = data.getDatatype();
256             ConversionResult conv =
257                 type.convertFromString(input, this.locale);
258             if (conv.isSuccessful()) {
259                 data.setValue(conv.getResult());
260             } else
261                 throw new SAXException JavaDoc("Could not convert: " + input +
262                                        " to " + type.getTypeClass());
263         } else if (this.currentWidget instanceof BooleanField) {
264             // FIXME: BooleanField should implement DataWidget, which
265
// would make this case unnesecary
266
if ("true".equals(input))
267                 this.currentWidget.setValue(Boolean.TRUE);
268             else if ("false".equals(input))
269                 this.currentWidget.setValue(Boolean.FALSE);
270             else
271                 throw new SAXException JavaDoc("Unkown boolean: " + input);
272         } else {
273             throw new SAXException JavaDoc("Unknown widget type: " + this.currentWidget);
274         }
275     }
276
277
278     /* ================ Widget -> SAX ================ */
279
280     /*
281      * Just recurses in deep first order over the widget hierarchy and
282      * emits XML
283      */

284
285     /**
286      * Generates SAX events representing the object's state.
287      */

288     public void toSAX( ContentHandler JavaDoc handler ) throws SAXException JavaDoc {
289         this.contentHandler = handler;
290
291         this.contentHandler.startDocument();
292         this.contentHandler.startPrefixMapping(PREFIX, URI);
293
294         generateSAX(this.widget);
295
296         this.contentHandler.endPrefixMapping(PREFIX);
297         this.contentHandler.endDocument();
298     }
299
300     /**
301      * Generate XML data.
302      */

303     private void generateSAX(Widget widget)
304         throws SAXException JavaDoc {
305         generateSAX(widget, null);
306     }
307
308     private void generateSAX(Widget widget, String JavaDoc id)
309         throws SAXException JavaDoc {
310
311         // no XML output for actions
312
if (widget instanceof Action)
313             return;
314
315         if (id == null)
316             id = widget.getId() == "" ? UNKNOWN : widget.getId();
317
318         final AttributesImpl JavaDoc attr = new AttributesImpl JavaDoc();
319         if (widget instanceof Repeater.RepeaterRow)
320             attribute(attr, POSITION, widget.getId());
321
322         start(id, attr);
323         // Placing the handling DataWidget before ContainerWidget
324
// means that an AggregateField is handled like a DataWidget
325
if (widget instanceof MultiValueField) {
326             Datatype datatype = ((MultiValueField)widget).getDatatype();
327             Object JavaDoc[] values = (Object JavaDoc[])widget.getValue();
328             if (values != null)
329                 for (int i = 0; i < values.length; i++) {
330                     start(ITEM, attr);
331                     data(datatype.convertToString(values[i], this.locale));
332                     end(ITEM);
333                 }
334         } else if (widget instanceof DataWidget) {
335             Datatype datatype = ((DataWidget)widget).getDatatype();
336             if (widget.getValue() != null)
337                 data(datatype.convertToString(widget.getValue(), this.locale));
338         } else if (widget instanceof BooleanField) {
339             // FIXME: BooleanField should implement DataWidget, which
340
// would make this case unnecessary
341
if (widget.getValue() != null) {
342                 data(widget.getValue().toString());
343             }
344         } else if (widget instanceof ContainerWidget) {
345             Iterator JavaDoc children = ((ContainerWidget)widget).getChildren();
346             while (children.hasNext())
347                 generateSAX((Widget)children.next());
348         } else if (widget instanceof Repeater) {
349             Repeater repeater = (Repeater)widget;
350             for (int i = 0; i < repeater.getSize(); i++)
351                 generateSAX(repeater.getRow(i), ITEM);
352         }
353         end(id);
354     }
355
356     private void attribute(AttributesImpl JavaDoc attr, String JavaDoc name, String JavaDoc value) {
357         attr.addAttribute("", name, name, "CDATA", value);
358     }
359
360     private void start(String JavaDoc name, AttributesImpl JavaDoc attr)
361     throws SAXException JavaDoc {
362         String JavaDoc qName = PREFIX == "" ? name : PREFIX + ":" + name;
363         this.contentHandler.startElement(URI, name, qName, attr);
364         attr.clear();
365     }
366
367     private void end(String JavaDoc name)
368     throws SAXException JavaDoc {
369         String JavaDoc qName = PREFIX == "" ? name : PREFIX + ":" + name;
370         this.contentHandler.endElement(URI, name, qName);
371     }
372
373     private void data(String JavaDoc data)
374     throws SAXException JavaDoc {
375         this.contentHandler.characters(data.toCharArray(), 0, data.length());
376     }
377 }
378
Popular Tags