KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > forms > generation > JXMacrosHelper


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
17 package org.apache.cocoon.forms.generation;
18
19 import java.util.Collections JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Locale JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.Set JavaDoc;
25
26 import org.apache.cocoon.ajax.BrowserUpdateTransformer;
27 import org.apache.cocoon.environment.Request;
28 import org.apache.cocoon.forms.FormsConstants;
29 import org.apache.cocoon.forms.FormsRuntimeException;
30 import org.apache.cocoon.forms.formmodel.Form;
31 import org.apache.cocoon.forms.formmodel.Repeater;
32 import org.apache.cocoon.forms.formmodel.Widget;
33 import org.apache.cocoon.forms.formmodel.WidgetState;
34 import org.apache.cocoon.forms.formmodel.tree.Tree;
35 import org.apache.cocoon.forms.formmodel.tree.TreeWalker;
36 import org.apache.cocoon.forms.validation.ValidationError;
37 import org.apache.cocoon.i18n.I18nUtils;
38 import org.apache.cocoon.xml.AbstractXMLPipe;
39 import org.apache.cocoon.xml.AttributesImpl;
40 import org.apache.cocoon.xml.XMLConsumer;
41 import org.apache.cocoon.xml.XMLUtils;
42 import org.apache.commons.collections.ArrayStack;
43 import org.apache.commons.lang.BooleanUtils;
44 import org.xml.sax.Attributes JavaDoc;
45 import org.xml.sax.ContentHandler JavaDoc;
46 import org.xml.sax.SAXException JavaDoc;
47
48 /**
49  * Helper class for the implementation of the CForms template language with JXTemplate macros.
50  *
51  * @version $Id$
52  */

53 public class JXMacrosHelper {
54
55     private XMLConsumer cocoonConsumer;
56     private Request request;
57     private Locale JavaDoc locale;
58     private ArrayStack widgetStack = new ArrayStack();
59     private ArrayStack pipeStack = new ArrayStack();
60     private Map JavaDoc classes; // lazily created
61
private boolean ajaxRequest;
62     private boolean ajaxTemplate;
63     private Set JavaDoc updatedWidgets;
64     private Set JavaDoc childUpdatedWidgets;
65
66     /**
67      * Builds and helper object, given the generator's consumer.
68      *
69      * @param consumer the generator's consumer
70      * @return a helper object
71      */

72     public static JXMacrosHelper createHelper(XMLConsumer consumer, Request request, String JavaDoc locale) {
73         return new JXMacrosHelper(consumer, request, locale);
74     }
75
76     public JXMacrosHelper(XMLConsumer consumer, Request request, String JavaDoc locale) {
77         this.cocoonConsumer = consumer;
78         this.request = request;
79         this.locale = I18nUtils.parseLocale(locale);
80         this.ajaxRequest = request.getParameter("cocoon-ajax") != null;
81     }
82
83     public Form getForm(Form form, String JavaDoc attributeName) {
84         Form returnForm = form;
85         // if there hasn't been passed a form object, try to find it in the request
86
if(returnForm == null) {
87             returnForm = (Form) this.request.getAttribute(attributeName);
88         }
89         if(returnForm != null) {
90             return returnForm;
91         }
92         throw new FormsRuntimeException("The template cannot find a form object");
93     }
94
95     public void startForm(Form form, Map JavaDoc attributes) throws SAXException JavaDoc {
96         
97         this.updatedWidgets = form.getUpdatedWidgetIds();
98         this.childUpdatedWidgets = form.getChildUpdatedWidgetIds();
99         
100         // build attributes
101
AttributesImpl attrs = new AttributesImpl();
102         Iterator JavaDoc iter = attributes.entrySet().iterator();
103         while(iter.hasNext()) {
104             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)iter.next();
105             attrs.addCDATAAttribute((String JavaDoc)entry.getKey(), (String JavaDoc)entry.getValue());
106         }
107         
108         this.ajaxTemplate = "true".equals(attributes.get("ajax"));
109
110         this.cocoonConsumer.startPrefixMapping(FormsConstants.INSTANCE_PREFIX, FormsConstants.INSTANCE_NS);
111         this.cocoonConsumer.startElement(FormsConstants.INSTANCE_NS,
112                                          "form-template",
113                                          FormsConstants.INSTANCE_PREFIX_COLON + "form-template",
114                                          attrs);
115         // Push the form at the top of the stack
116
this.widgetStack.push(Boolean.FALSE); // Not in an updated template
117
this.widgetStack.push(form);
118     }
119
120     public void endForm() throws SAXException JavaDoc {
121         this.widgetStack.pop();
122         this.widgetStack.pop();
123         this.cocoonConsumer.endElement(FormsConstants.INSTANCE_NS,
124                                        "form-template",
125                                        FormsConstants.INSTANCE_PREFIX_COLON + "form-template");
126         this.cocoonConsumer.endPrefixMapping(FormsConstants.INSTANCE_PREFIX);
127         
128         this.ajaxTemplate = false;
129         this.updatedWidgets = null;
130     }
131     
132     private void startBuReplace(String JavaDoc id) throws SAXException JavaDoc {
133         AttributesImpl attr = new AttributesImpl();
134         attr.addCDATAAttribute("id", id);
135         this.cocoonConsumer.startElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace", attr);
136     }
137     
138     private void endBuReplace(String JavaDoc id) throws SAXException JavaDoc {
139         this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace");
140     }
141     
142     protected boolean pushWidget(String JavaDoc path, boolean unused) throws SAXException JavaDoc {
143         Widget parent = peekWidget();
144         if (path == null || path.length() == 0) {
145             throw new FormsRuntimeException("Missing 'id' attribute on template instruction");
146         }
147         Widget widget = parent.lookupWidget(path);
148         if (widget == null) {
149             throw new FormsRuntimeException(parent + " has no child named '" + path + "'", parent.getLocation());
150         }
151
152         String JavaDoc id = widget.getFullName();
153         // Is there an updated widget at a higher level in the template?
154
boolean inUpdatedTemplate = ((Boolean JavaDoc)widgetStack.peek(1)).booleanValue();
155
156         boolean display;
157
158         if (ajaxRequest) {
159             // An Ajax request. We will send partial updates
160
if (inUpdatedTemplate) {
161                 // A parent widget has been updated: redisplay this one also
162
display = true;
163             } else if (this.updatedWidgets.contains(id)) {
164                 // Widget has been updated. We are now in an updated template section,
165
// and widgets have to be surrounded with <bu:replace>
166
inUpdatedTemplate = true;
167                 display = true;
168             } else if (this.childUpdatedWidgets.contains(id)) {
169                 // A child need to be updated
170
display = true;
171             } else {
172                 // Doesn't need to be displayed
173
display = false;
174             }
175         } else {
176             // Not an ajax request
177
if (ajaxTemplate) {
178                 // Surround all widgets with <bu:replace>, which the bu tranformer will use to check structure
179
// consistency and add an id attribute to its child elements.
180
inUpdatedTemplate = true;
181             }
182             // Display the widget
183
display = true;
184         }
185         
186         if (display) {
187             // Widget needs to be displayed, but does it actually allows it?
188
if (widget.getState().isDisplayingValues()) {
189                 if (inUpdatedTemplate) {
190                     // Updated part of an Ajax template: surround with <bu:replace>
191
startBuReplace(id);
192                 }
193             } else {
194                 if (ajaxTemplate) {
195                     // Generate a placeholder, so that the page can be updated later
196
startBuReplace(id);
197                     AttributesImpl attrs = new AttributesImpl();
198                     attrs.addCDATAAttribute("id", id);
199                     this.cocoonConsumer.startElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder", attrs);
200                     this.cocoonConsumer.endElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder");
201                     endBuReplace(id);
202                 }
203                 // Production finished for this widget
204
display = false;
205             }
206         }
207
208         if (display) {
209             this.widgetStack.push(BooleanUtils.toBooleanObject(inUpdatedTemplate));
210             this.widgetStack.push(widget);
211         }
212         
213         return display;
214     }
215     
216     public Widget peekWidget() {
217         return (Widget)this.widgetStack.peek();
218     }
219     
220     public void popWidget() throws SAXException JavaDoc {
221         Widget widget = (Widget)this.widgetStack.pop();
222         boolean inUpdatedTemplate = ((Boolean JavaDoc)this.widgetStack.pop()).booleanValue();
223         
224         if (inUpdatedTemplate) {
225             // Close the bu:replace
226
endBuReplace(widget.getFullName());
227         }
228     }
229     
230     public boolean pushWidget(String JavaDoc path) throws SAXException JavaDoc {
231         return pushWidget(path, false);
232     }
233     
234     public boolean pushContainer(String JavaDoc path) throws SAXException JavaDoc {
235         return pushWidget(path, true);
236     }
237
238     /**
239      * Enter a repeater
240      *
241      * @param path widget path
242      * @param ajaxAware distinguishes between &lt;ft:repeater-widget&gt; and &lt;ft:repeater&gt;.
243      * @return true if the repeater template is to be executed
244      * @throws SAXException
245      */

246     public boolean pushRepeater(String JavaDoc path, boolean ajaxAware) throws SAXException JavaDoc {
247         if (!ajaxAware && this.ajaxTemplate) {
248             throw new IllegalStateException JavaDoc("Cannot use <ft:repeater-widget> in an Ajax form");
249         }
250         boolean result = pushWidget(path, true);
251         if (result && !(peekWidget() instanceof Repeater)) {
252             throw new IllegalArgumentException JavaDoc("Widget " + peekWidget() + " is not a repeater");
253         }
254         return result;
255     }
256     
257     /**
258      * Get a child widget of a given widget, throwing an exception if no such child exists.
259      *
260      * @param currentWidget
261      * @param id
262      */

263     public Widget getWidget(Widget currentWidget, String JavaDoc path) {
264         Widget result = currentWidget.lookupWidget(path);
265
266         if (result != null) {
267             return result;
268         }
269         throw new FormsRuntimeException(currentWidget + " has no child named '" + path + "'", currentWidget.getLocation());
270     }
271
272     private Repeater getRepeater(Widget currentWidget, String JavaDoc id) {
273         Widget child = getWidget(currentWidget, id);
274         if (child instanceof Repeater) {
275             return (Repeater)child;
276         }
277         throw new FormsRuntimeException(child + " is not a repeater", child.getLocation());
278     }
279
280     /**
281      * Generate a widget's SAX fragment, buffering the root element's <code>endElement()</code>
282      * event so that the template can insert styling information in it.
283      *
284      * @param widget
285      * @param locale
286      * @throws SAXException
287      */

288     public void generateWidget(Widget widget, Map JavaDoc arguments) throws SAXException JavaDoc {
289         // Needs to be buffered
290
RootBufferingPipe pipe = new RootBufferingPipe(this.cocoonConsumer, arguments);
291         this.pipeStack.push(pipe);
292         widget.generateSaxFragment(pipe, this.locale);
293     }
294
295     /**
296      * Flush the root element name that has been stored in
297      * {@link #generateWidget(Widget, Locale)}.
298      *
299      * @param obj the object that is terminated (widget or validation error)
300      * @throws SAXException
301      */

302     public void flushRootAndPop() throws SAXException JavaDoc {
303         ((RootBufferingPipe) pipeStack.pop()).flushRoot();
304         popWidget();
305     }
306
307     public void flushRoot() throws SAXException JavaDoc {
308         ((RootBufferingPipe) pipeStack.pop()).flushRoot();
309     }
310
311     public void generateWidgetLabel(Widget widget, String JavaDoc id) throws SAXException JavaDoc {
312         getWidget(widget, id).generateLabel(this.cocoonConsumer);
313     }
314
315     public void generateRepeaterWidgetLabel(Widget widget, String JavaDoc id, String JavaDoc widgetId) throws SAXException JavaDoc {
316         // Widget labels are allowed either inside or outside of <ft:repeater>
317
Repeater repeater = widget instanceof Repeater ? (Repeater)widget : getRepeater(widget, id);
318         repeater.generateWidgetLabel(widgetId, this.cocoonConsumer);
319     }
320
321     public void generateRepeaterSize(Widget widget, String JavaDoc id) throws SAXException JavaDoc {
322         getRepeater(widget, id).generateSize(this.cocoonConsumer);
323     }
324
325     private static final String JavaDoc VALIDATION_ERROR = "validation-error";
326
327     public void generateValidationError(ValidationError error) throws SAXException JavaDoc {
328         // Needs to be buffered
329
RootBufferingPipe pipe = new RootBufferingPipe(this.cocoonConsumer);
330         this.pipeStack.push(pipe);
331         pipe.startElement(FormsConstants.INSTANCE_NS, VALIDATION_ERROR, FormsConstants.INSTANCE_PREFIX_COLON + VALIDATION_ERROR, XMLUtils.EMPTY_ATTRIBUTES);
332         error.generateSaxFragment(pipe);
333         pipe.endElement(FormsConstants.INSTANCE_NS, VALIDATION_ERROR, FormsConstants.INSTANCE_PREFIX_COLON + VALIDATION_ERROR);
334     }
335
336     public boolean isValidationError(Object JavaDoc object) {
337         return object instanceof ValidationError;
338     }
339
340     public void defineClassBody(Form form, String JavaDoc id, Object JavaDoc body) {
341         // TODO: check that class actually exists in the form
342
if (this.classes == null) {
343             this.classes = new HashMap JavaDoc();
344         }
345
346         // TODO: check if class doesn't already exist?
347
this.classes.put(id, body);
348     }
349
350     public Object JavaDoc getClassBody(String JavaDoc id) {
351         Object JavaDoc result = this.classes == null ? null : this.classes.get(id);
352
353         if (result == null) {
354             throw new FormsRuntimeException("No class '" + id + "' has been defined.");
355         }
356         return result;
357     }
358
359     public boolean isSelectedCase(Widget unionWidget, String JavaDoc caseValue) {
360         String JavaDoc value = (String JavaDoc)unionWidget.getValue();
361         return caseValue.equals(value != null ? value : "");
362     }
363
364     public TreeWalker createWalker() {
365         return new TreeWalker((Tree)peekWidget());
366     }
367     
368     public boolean isVisible(Widget widget) throws SAXException JavaDoc {
369         boolean visible = widget.getCombinedState().isDisplayingValues();
370         
371         if (!visible) {
372             // Generate a placeholder it not visible
373
String JavaDoc id = widget.getRequestParameterName();
374             AttributesImpl attrs = new AttributesImpl();
375             attrs.addCDATAAttribute("id", id);
376             this.cocoonConsumer.startElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace", attrs);
377             this.cocoonConsumer.startElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder", attrs);
378             this.cocoonConsumer.endElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder");
379             this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace");
380         }
381
382         return visible;
383     }
384     
385     public boolean isModified(Widget widget) {
386         return this.updatedWidgets.contains(widget.getRequestParameterName());
387     }
388     
389     public boolean generateStyling(Map JavaDoc attributes) throws SAXException JavaDoc {
390         return generateStyling(this.cocoonConsumer, attributes);
391     }
392
393     /**
394      * Generate a <code>&lt;fi:styling&gt;</code> element holding the attributes of a <code>ft:*</code>
395      * element that are in the "fi:" namespace.
396      *
397      * @param attributes the template instruction attributes
398      * @return true if a <code>&lt;fi:styling&gt;</code> was produced
399      * @throws SAXException
400      */

401     public static boolean generateStyling(ContentHandler JavaDoc handler, Map JavaDoc attributes) throws SAXException JavaDoc {
402         AttributesImpl attr = null;
403         Iterator JavaDoc entries = attributes.entrySet().iterator();
404         while(entries.hasNext()) {
405             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)entries.next();
406             String JavaDoc key = (String JavaDoc)entry.getKey();
407             
408             // FIXME: JXTG only gives the local name of attributes, so we can't distinguish namespaces...
409
if (!"id".equals(key) && !"widget-id".equals(key)) {
410                 if (attr == null)
411                     attr = new AttributesImpl();
412                 attr.addCDATAAttribute(key, (String JavaDoc)entry.getValue());
413             }
414         }
415         
416         if (attr != null) {
417             // There were some styling attributes
418
handler.startElement(FormsConstants.INSTANCE_NS, "styling", FormsConstants.INSTANCE_PREFIX_COLON + "styling", attr);
419             handler.endElement(FormsConstants.INSTANCE_NS, "styling", FormsConstants.INSTANCE_PREFIX_COLON + "styling");
420             return true;
421         } else {
422             return false;
423         }
424     }
425
426     /**
427      * A SAX pipe that buffers the <code>endElement()</code> event of the root element.
428      * This is needed by the generator version of the FormsTransformer (see jx-macros.xml).
429      *
430      * @version $Id$
431      */

432     private static class RootBufferingPipe extends AbstractXMLPipe {
433         private int depth = 0;
434
435         private String JavaDoc rootUri;
436         private String JavaDoc rootLoc;
437         private String JavaDoc rootRaw;
438         private Map JavaDoc arguments;
439         private boolean forbidStyling = false;
440
441         public RootBufferingPipe(XMLConsumer next) {
442             this(next, Collections.EMPTY_MAP);
443         }
444
445         public RootBufferingPipe(XMLConsumer next, Map JavaDoc arguments) {
446             this.setConsumer(next);
447             this.arguments = arguments;
448         }
449
450         public void startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw, Attributes JavaDoc a)
451         throws SAXException JavaDoc {
452             super.startElement(uri, loc, raw, a);
453             if (depth == 0) {
454                 // Root element: keep its description
455
this.rootUri = uri;
456                 this.rootLoc = loc;
457                 this.rootRaw = raw;
458                 
459                 // And produce fi:styling from attributes
460
this.forbidStyling = generateStyling(this.contentHandler, arguments);
461             }
462             
463             if (depth == 1 && forbidStyling &&
464                 uri.equals(FormsConstants.INSTANCE_NS) && loc.equals("styling")) {
465                 throw new SAXException JavaDoc("Cannot use 'fi:*' attributes and <fi:styling> at the same time");
466             }
467
468             depth++;
469         }
470
471         public void endElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw)
472         throws SAXException JavaDoc {
473             depth--;
474             if (depth > 0) {
475                 // Propagate all but root element
476
super.endElement(uri, loc, raw);
477             }
478         }
479
480         public void flushRoot() throws SAXException JavaDoc {
481             if (depth != 0) {
482                 throw new IllegalStateException JavaDoc("Depth is not zero");
483             }
484             super.endElement(this.rootUri, this.rootLoc, this.rootRaw);
485         }
486     }
487 }
488
Popular Tags