1 16 17 package org.apache.cocoon.forms.generation; 18 19 import java.util.Collections ; 20 import java.util.HashMap ; 21 import java.util.Iterator ; 22 import java.util.Locale ; 23 import java.util.Map ; 24 import java.util.Set ; 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 ; 45 import org.xml.sax.ContentHandler ; 46 import org.xml.sax.SAXException ; 47 48 53 public class JXMacrosHelper { 54 55 private XMLConsumer cocoonConsumer; 56 private Request request; 57 private Locale locale; 58 private ArrayStack widgetStack = new ArrayStack(); 59 private ArrayStack pipeStack = new ArrayStack(); 60 private Map classes; private boolean ajaxRequest; 62 private boolean ajaxTemplate; 63 private Set updatedWidgets; 64 private Set childUpdatedWidgets; 65 66 72 public static JXMacrosHelper createHelper(XMLConsumer consumer, Request request, String locale) { 73 return new JXMacrosHelper(consumer, request, locale); 74 } 75 76 public JXMacrosHelper(XMLConsumer consumer, Request request, String 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 attributeName) { 84 Form returnForm = form; 85 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 attributes) throws SAXException { 96 97 this.updatedWidgets = form.getUpdatedWidgetIds(); 98 this.childUpdatedWidgets = form.getChildUpdatedWidgetIds(); 99 100 AttributesImpl attrs = new AttributesImpl(); 102 Iterator iter = attributes.entrySet().iterator(); 103 while(iter.hasNext()) { 104 Map.Entry entry = (Map.Entry )iter.next(); 105 attrs.addCDATAAttribute((String )entry.getKey(), (String )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 this.widgetStack.push(Boolean.FALSE); this.widgetStack.push(form); 118 } 119 120 public void endForm() throws SAXException { 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 id) throws SAXException { 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 id) throws SAXException { 139 this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace"); 140 } 141 142 protected boolean pushWidget(String path, boolean unused) throws SAXException { 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 id = widget.getFullName(); 153 boolean inUpdatedTemplate = ((Boolean )widgetStack.peek(1)).booleanValue(); 155 156 boolean display; 157 158 if (ajaxRequest) { 159 if (inUpdatedTemplate) { 161 display = true; 163 } else if (this.updatedWidgets.contains(id)) { 164 inUpdatedTemplate = true; 167 display = true; 168 } else if (this.childUpdatedWidgets.contains(id)) { 169 display = true; 171 } else { 172 display = false; 174 } 175 } else { 176 if (ajaxTemplate) { 178 inUpdatedTemplate = true; 181 } 182 display = true; 184 } 185 186 if (display) { 187 if (widget.getState().isDisplayingValues()) { 189 if (inUpdatedTemplate) { 190 startBuReplace(id); 192 } 193 } else { 194 if (ajaxTemplate) { 195 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 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 { 221 Widget widget = (Widget)this.widgetStack.pop(); 222 boolean inUpdatedTemplate = ((Boolean )this.widgetStack.pop()).booleanValue(); 223 224 if (inUpdatedTemplate) { 225 endBuReplace(widget.getFullName()); 227 } 228 } 229 230 public boolean pushWidget(String path) throws SAXException { 231 return pushWidget(path, false); 232 } 233 234 public boolean pushContainer(String path) throws SAXException { 235 return pushWidget(path, true); 236 } 237 238 246 public boolean pushRepeater(String path, boolean ajaxAware) throws SAXException { 247 if (!ajaxAware && this.ajaxTemplate) { 248 throw new IllegalStateException ("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 ("Widget " + peekWidget() + " is not a repeater"); 253 } 254 return result; 255 } 256 257 263 public Widget getWidget(Widget currentWidget, String 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 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 288 public void generateWidget(Widget widget, Map arguments) throws SAXException { 289 RootBufferingPipe pipe = new RootBufferingPipe(this.cocoonConsumer, arguments); 291 this.pipeStack.push(pipe); 292 widget.generateSaxFragment(pipe, this.locale); 293 } 294 295 302 public void flushRootAndPop() throws SAXException { 303 ((RootBufferingPipe) pipeStack.pop()).flushRoot(); 304 popWidget(); 305 } 306 307 public void flushRoot() throws SAXException { 308 ((RootBufferingPipe) pipeStack.pop()).flushRoot(); 309 } 310 311 public void generateWidgetLabel(Widget widget, String id) throws SAXException { 312 getWidget(widget, id).generateLabel(this.cocoonConsumer); 313 } 314 315 public void generateRepeaterWidgetLabel(Widget widget, String id, String widgetId) throws SAXException { 316 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 id) throws SAXException { 322 getRepeater(widget, id).generateSize(this.cocoonConsumer); 323 } 324 325 private static final String VALIDATION_ERROR = "validation-error"; 326 327 public void generateValidationError(ValidationError error) throws SAXException { 328 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 object) { 337 return object instanceof ValidationError; 338 } 339 340 public void defineClassBody(Form form, String id, Object body) { 341 if (this.classes == null) { 343 this.classes = new HashMap (); 344 } 345 346 this.classes.put(id, body); 348 } 349 350 public Object getClassBody(String id) { 351 Object 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 caseValue) { 360 String value = (String )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 { 369 boolean visible = widget.getCombinedState().isDisplayingValues(); 370 371 if (!visible) { 372 String 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 attributes) throws SAXException { 390 return generateStyling(this.cocoonConsumer, attributes); 391 } 392 393 401 public static boolean generateStyling(ContentHandler handler, Map attributes) throws SAXException { 402 AttributesImpl attr = null; 403 Iterator entries = attributes.entrySet().iterator(); 404 while(entries.hasNext()) { 405 Map.Entry entry = (Map.Entry )entries.next(); 406 String key = (String )entry.getKey(); 407 408 if (!"id".equals(key) && !"widget-id".equals(key)) { 410 if (attr == null) 411 attr = new AttributesImpl(); 412 attr.addCDATAAttribute(key, (String )entry.getValue()); 413 } 414 } 415 416 if (attr != null) { 417 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 432 private static class RootBufferingPipe extends AbstractXMLPipe { 433 private int depth = 0; 434 435 private String rootUri; 436 private String rootLoc; 437 private String rootRaw; 438 private Map 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 arguments) { 446 this.setConsumer(next); 447 this.arguments = arguments; 448 } 449 450 public void startElement(String uri, String loc, String raw, Attributes a) 451 throws SAXException { 452 super.startElement(uri, loc, raw, a); 453 if (depth == 0) { 454 this.rootUri = uri; 456 this.rootLoc = loc; 457 this.rootRaw = raw; 458 459 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 ("Cannot use 'fi:*' attributes and <fi:styling> at the same time"); 466 } 467 468 depth++; 469 } 470 471 public void endElement(String uri, String loc, String raw) 472 throws SAXException { 473 depth--; 474 if (depth > 0) { 475 super.endElement(uri, loc, raw); 477 } 478 } 479 480 public void flushRoot() throws SAXException { 481 if (depth != 0) { 482 throw new IllegalStateException ("Depth is not zero"); 483 } 484 super.endElement(this.rootUri, this.rootLoc, this.rootRaw); 485 } 486 } 487 } 488 | Popular Tags |