1 16 package org.apache.cocoon.woody.transformation; 17 18 import org.apache.avalon.excalibur.pool.Recyclable; 19 import org.apache.cocoon.i18n.I18nUtils; 20 import org.apache.cocoon.woody.Constants; 21 import org.apache.cocoon.woody.formmodel.AggregateField; 22 import org.apache.cocoon.woody.formmodel.Repeater; 23 import org.apache.cocoon.woody.formmodel.Struct; 24 import org.apache.cocoon.woody.formmodel.Union; 25 import org.apache.cocoon.woody.formmodel.Widget; 26 import org.apache.cocoon.woody.validation.ValidationError; 27 import org.apache.cocoon.woody.validation.ValidationErrorAware; 28 import org.apache.cocoon.xml.AbstractXMLPipe; 29 import org.apache.cocoon.xml.SaxBuffer; 30 import org.apache.commons.jxpath.JXPathException; 31 32 import org.xml.sax.Attributes ; 33 import org.xml.sax.ContentHandler ; 34 import org.xml.sax.SAXException ; 35 import org.xml.sax.ext.LexicalHandler ; 36 import org.xml.sax.helpers.AttributesImpl ; 37 38 import java.util.HashMap ; 39 import java.util.LinkedList ; 40 import java.util.Locale ; 41 import java.util.Map ; 42 43 47 62 public class EffectWidgetReplacingPipe extends EffectPipe { 63 64 70 private static final String LOCATION = "location"; 71 72 private static final String CLASS = "class"; 73 private static final String CONTINUATION_ID = "continuation-id"; 74 private static final String FORM_TEMPLATE_EL = "form-template"; 75 private static final String NEW = "new"; 76 private static final String REPEATER_SIZE = "repeater-size"; 77 private static final String REPEATER_WIDGET = "repeater-widget"; 78 private static final String REPEATER_WIDGET_LABEL = "repeater-widget-label"; 79 private static final String AGGREGATE_WIDGET = "aggregate-widget"; 80 private static final String STRUCT = "struct"; 81 private static final String STYLING_EL = "styling"; 82 private static final String UNION = "union"; 83 private static final String VALIDATION_ERROR = "validation-error"; 84 private static final String WIDGET_LABEL = "widget-label"; 85 private static final String WIDGET = "widget"; 86 87 protected Widget contextWidget; 88 protected LinkedList contextWidgets; 89 protected String widgetId; 90 protected Widget widget; 91 protected Map classes; 92 93 private final DocHandler docHandler = new DocHandler(); 94 private final FormHandler formHandler = new FormHandler(); 95 private final NestedHandler nestedHandler = new NestedHandler(); 96 private final WidgetLabelHandler widgetLabelHandler = new WidgetLabelHandler(); 97 private final WidgetHandler widgetHandler = new WidgetHandler(); 98 private final RepeaterSizeHandler repeaterSizeHandler = new RepeaterSizeHandler(); 99 private final RepeaterWidgetLabelHandler repeaterWidgetLabelHandler = new RepeaterWidgetLabelHandler(); 100 private final RepeaterWidgetHandler repeaterWidgetHandler = new RepeaterWidgetHandler(); 101 private final AggregateWidgetHandler aggregateWidgetHandler = new AggregateWidgetHandler(); 102 private final StructHandler structHandler = new StructHandler(); 103 private final UnionHandler unionHandler = new UnionHandler(); 104 private final UnionPassThruHandler unionPassThruHandler = new UnionPassThruHandler(); 105 private final NewHandler newHandler = new NewHandler(); 106 private final ClassHandler classHandler = new ClassHandler(); 107 private final ContinuationIdHandler continuationIdHandler = new ContinuationIdHandler(); 108 private final StylingContentHandler stylingHandler = new StylingContentHandler(); 109 private final ValidationErrorHandler validationErrorHandler = new ValidationErrorHandler(); 110 111 114 private final Map templates = new HashMap (12, 1); 115 116 protected WoodyPipelineConfig pipeContext; 117 118 121 protected boolean gotStylingElement; 122 123 126 protected String namespacePrefix; 127 128 129 public EffectWidgetReplacingPipe() { 130 templates.put(WIDGET, widgetHandler); 132 templates.put(WIDGET_LABEL, widgetLabelHandler); 133 templates.put(REPEATER_WIDGET, repeaterWidgetHandler); 134 templates.put(AGGREGATE_WIDGET, aggregateWidgetHandler); 135 templates.put(REPEATER_SIZE, repeaterSizeHandler); 136 templates.put(REPEATER_WIDGET_LABEL, repeaterWidgetLabelHandler); 137 templates.put(STRUCT, structHandler); 138 templates.put(UNION, unionHandler); 139 templates.put(NEW, newHandler); 140 templates.put(CLASS, classHandler); 141 templates.put(CONTINUATION_ID, continuationIdHandler); 142 templates.put(VALIDATION_ERROR, validationErrorHandler); 143 } 144 145 private void throwSAXException(String message) throws SAXException { 146 throw new SAXException ("EffectWoodyTemplateTransformer: " + message); 147 } 148 149 public void init(Widget contextWidget, WoodyPipelineConfig pipeContext) { 150 super.init(); 151 this.pipeContext = pipeContext; 152 153 handler = docHandler; 155 156 contextWidgets = new LinkedList (); 158 classes = new HashMap (); 159 } 160 161 protected String getWidgetId(Attributes attributes) throws SAXException { 162 String widgetId = attributes.getValue("id"); 163 if (widgetId == null || widgetId.equals("")) { 164 throwSAXException("Missing required widget \"id\" attribute."); 165 } 166 return widgetId; 167 } 168 169 protected Widget getWidget(String widgetId) throws SAXException { 170 Widget widget = contextWidget.getWidget(widgetId); 171 if (widget == null) { 172 if (contextWidget.getFullyQualifiedId() == null) { 173 throwSAXException("Widget with id \"" + widgetId + "\" does not exist in the form container."); 174 } else { 175 throwSAXException("Widget with id \"" + widgetId + "\" does not exist in the container \"" + 176 contextWidget.getFullyQualifiedId() + "\""); 177 } 178 } 179 return widget; 180 } 181 182 protected void getRepeaterWidget(String handler) throws SAXException { 183 widgetId = getWidgetId(input.attrs); 184 widget = getWidget(widgetId); 185 if (!(widget instanceof Repeater)) { 186 throwWrongWidgetType("RepeaterWidgetLabelHandler", input.loc, "repeater"); 187 } 188 } 189 190 public void throwWrongWidgetType(String pipeName, String element, String widget) throws SAXException { 191 throwSAXException(pipeName + ": Element \"" + element + "\" can only be used for " + widget + " widgets."); 192 } 193 194 198 private ContentHandler getContentHandler() { 199 return this.contentHandler; 200 } 201 202 206 private LexicalHandler getLexicalHandler() { 207 return this.lexicalHandler; 208 } 209 210 public Handler nestedTemplate() throws SAXException { 211 if (Constants.WT_NS.equals(input.uri)) { 212 Handler handler = (Handler )templates.get(input.loc); 214 if (handler != null) { 215 return handler; 216 } else if (FORM_TEMPLATE_EL.equals(input.loc)) { 217 throwSAXException("Element \"form-template\" must not be nested."); 218 return null; } else { 220 throwSAXException("Unrecognized template: " + input.loc); 221 return null; } 223 } else { 224 return nestedHandler; 226 } 227 } 228 229 233 protected class DocHandler extends Handler { 234 public Handler process() throws SAXException { 235 switch (event) { 236 case EVENT_SET_DOCUMENT_LOCATOR: 237 return this; 238 case EVENT_START_PREFIX_MAPPING: 239 EffectWidgetReplacingPipe.this.namespacePrefix = input.prefix; 241 return this; 242 case EVENT_ELEMENT: 243 if (Constants.WT_NS.equals(input.uri)) { 244 if (FORM_TEMPLATE_EL.equals(input.loc)) { 245 return formHandler; 246 } else { 247 throwSAXException("Woody template \"" + input.loc + 248 "\" not permitted outside \"form-template\""); 249 } 250 } else { 251 return this; 252 } 253 case EVENT_END_PREFIX_MAPPING: 254 return this; 256 default: 257 out.copy(); 258 return this; 259 } 260 } 261 } 262 263 protected class FormHandler extends Handler { 264 public Handler process() throws SAXException { 265 switch(event) { 266 case EVENT_START_ELEMENT: 267 if (contextWidget != null) { 268 throwSAXException("Detected nested wt:form-template elements, this is not allowed."); 269 } 270 out.startPrefixMapping(Constants.WI_PREFIX, Constants.WI_NS); 271 272 String formJXPath = input.attrs.getValue(LOCATION); 275 if (formJXPath != null) { 276 AttributesImpl attrsCopy = new AttributesImpl (input.attrs); 278 attrsCopy.removeAttribute(input.attrs.getIndex(LOCATION)); 279 input.attrs = attrsCopy; 280 input.mine = true; 281 } 282 contextWidget = pipeContext.findForm(formJXPath); 283 284 String localeAttr = input.attrs.getValue("locale"); 287 if (localeAttr != null) { localeAttr = pipeContext.translateText(localeAttr); 289 pipeContext.setLocale(I18nUtils.parseLocale(localeAttr)); 290 } else if (pipeContext.getLocaleParameter() != null) { pipeContext.setLocale(pipeContext.getLocaleParameter()); 292 } else { 293 Object locale = null; 296 try { 297 locale = pipeContext.evaluateExpression("/locale"); 298 } catch (JXPathException e) {} 299 if (locale != null) { 300 pipeContext.setLocale((Locale )locale); 301 } 302 else { 303 pipeContext.setLocale(Locale.getDefault()); 305 } 306 } 307 308 String [] namesToTranslate = {"action"}; 309 Attributes transAttrs = translateAttributes(input.attrs, namesToTranslate); 310 out.element(Constants.WI_PREFIX, Constants.WI_NS, "form-template", transAttrs); 311 out.startElement(); 312 return this; 313 case EVENT_ELEMENT: 314 return nestedTemplate(); 315 case EVENT_END_ELEMENT: 316 out.copy(); 317 out.endPrefixMapping(Constants.WI_PREFIX); 318 contextWidget = null; 319 return this; 320 default: 321 out.copy(); 322 return this; 323 } 324 } 325 } 326 327 protected class NestedHandler extends Handler { 328 public Handler process() throws SAXException { 329 switch(event) { 330 case EVENT_ELEMENT: 331 return nestedTemplate(); 332 default: 333 out.copy(); 334 return this; 335 } 336 } 337 } 338 339 protected class WidgetLabelHandler extends Handler { 340 public Handler process() throws SAXException { 341 switch (event) { 342 case EVENT_START_ELEMENT: 343 widgetId = getWidgetId(input.attrs); 344 Widget widget = getWidget(widgetId); 345 widget.generateLabel(getContentHandler()); 346 widget = null; 347 return this; 348 case EVENT_ELEMENT: 349 return nullHandler; 350 case EVENT_END_ELEMENT: 351 return this; 352 default: 353 out.copy(); 354 return this; 355 } 356 } 357 } 358 359 protected class WidgetHandler extends Handler { 360 public Handler process() throws SAXException { 361 switch (event) { 362 case EVENT_START_ELEMENT: 363 widgetId = getWidgetId(input.attrs); 364 widget = getWidget(widgetId); 365 gotStylingElement = false; 366 out.bufferInit(); 367 return this; 368 369 case EVENT_ELEMENT: 370 if (Constants.WI_NS.equals(input.uri) && STYLING_EL.equals(input.loc)) { 371 gotStylingElement = true; 372 } 373 return bufferHandler; 374 375 case EVENT_END_ELEMENT: 376 stylingHandler.recycle(); 377 stylingHandler.setSaxFragment(out.getBuffer()); 378 stylingHandler.setContentHandler(getContentHandler()); 379 stylingHandler.setLexicalHandler(getLexicalHandler()); 380 widget.generateSaxFragment(stylingHandler, pipeContext.getLocale()); 381 widget = null; 382 out.bufferFini(); 383 return this; 384 385 default: 386 out.copy(); 387 return this; 388 } 389 } 390 } 391 392 protected class RepeaterSizeHandler extends Handler { 393 public Handler process() throws SAXException { 394 switch(event) { 395 case EVENT_START_ELEMENT: 396 getRepeaterWidget("RepeaterSizeHandler"); 397 ((Repeater)widget).generateSize(getContentHandler()); 398 widget = null; 399 return this; 400 case EVENT_ELEMENT: 401 return nullHandler; 402 case EVENT_END_ELEMENT: 403 return this; 404 default: 405 out.copy(); 406 return this; 407 } 408 } 409 } 410 411 protected class RepeaterWidgetLabelHandler extends Handler { 412 public Handler process() throws SAXException { 413 switch(event) { 414 case EVENT_START_ELEMENT: 415 getRepeaterWidget("RepeaterWidgetLabelHandler"); 416 String widgetId = input.attrs.getValue("widget-id"); 417 if (widgetId == null || widgetId.equals("")) 418 throwSAXException("Element repeater-widget-label missing required widget-id attribute."); 419 ((Repeater)widget).generateWidgetLabel(widgetId, getContentHandler()); 420 widget = null; 421 return this; 422 case EVENT_ELEMENT: 423 return nullHandler; 424 case EVENT_END_ELEMENT: 425 return this; 426 default: 427 out.copy(); 428 return this; 429 } 430 } 431 } 432 433 protected class RepeaterWidgetHandler extends Handler { 434 public Handler process() throws SAXException { 435 switch(event) { 436 case EVENT_START_ELEMENT: 437 getRepeaterWidget("RepeaterWidgetHandler"); 438 out.bufferInit(); 439 return this; 440 case EVENT_ELEMENT: 441 return bufferHandler; 442 case EVENT_END_ELEMENT: 443 Repeater repeater = (Repeater)widget; 444 int rowCount = repeater.getSize(); 445 handlers.addFirst(handler); 446 handler = nestedHandler; 447 contextWidgets.addFirst(contextWidget); 448 for (int i = 0; i < rowCount; i++) { 449 Repeater.RepeaterRow row = repeater.getRow(i); 450 contextWidget = row; 451 out.getBuffer().toSAX(EffectWidgetReplacingPipe.this); 452 } 453 contextWidget = (Widget)contextWidgets.removeFirst(); 454 handler = (Handler )handlers.removeFirst(); 455 widget = null; 456 out.bufferFini(); 457 return this; 458 default: 459 out.buffer(); 460 return this; 461 } 462 } 463 } 464 465 protected class AggregateWidgetHandler extends Handler { 466 public Handler process() throws SAXException { 467 switch(event) { 468 case EVENT_START_ELEMENT: 469 widgetId = getWidgetId(input.attrs); 470 widget = getWidget(widgetId); 471 if (!(widget instanceof AggregateField)) { 472 throwWrongWidgetType("AggregateWidgetHandler", input.loc, "aggregate"); 473 } 474 contextWidgets.addFirst(contextWidget); 475 contextWidget = widget; 476 return this; 477 case EVENT_ELEMENT: 478 return nestedTemplate(); 479 case EVENT_END_ELEMENT: 480 contextWidget = (Widget)contextWidgets.removeFirst(); 481 return this; 482 default: 483 out.copy(); 484 return this; 485 } 486 } 487 } 488 489 protected class StructHandler extends Handler { 490 public Handler process() throws SAXException { 491 switch(event) { 492 case EVENT_START_ELEMENT: 493 widgetId = getWidgetId(input.attrs); 494 widget = getWidget(widgetId); 495 if (!(widget instanceof Struct)) { 496 throwWrongWidgetType("StructHandler", input.loc, "struct"); 497 } 498 contextWidgets.addFirst(contextWidget); 499 contextWidget = widget; 500 out.element(Constants.WI_PREFIX, Constants.WI_NS, "struct"); 501 out.attributes(); 502 out.startElement(); 503 return this; 504 case EVENT_ELEMENT: 505 return nestedTemplate(); 506 case EVENT_END_ELEMENT: 507 out.copy(); 508 contextWidget = (Widget)contextWidgets.removeFirst(); 509 return this; 510 default: 511 out.copy(); 512 return this; 513 } 514 } 515 } 516 517 protected class UnionHandler extends Handler { 518 public Handler process() throws SAXException { 519 switch(event) { 520 case EVENT_START_ELEMENT: 521 widgetId = getWidgetId(input.attrs); 522 widget = getWidget(widgetId); 523 if (!(widget instanceof Union)) { 524 throwWrongWidgetType("UnionHandler", input.loc, "union"); 525 } 526 contextWidgets.addFirst(contextWidget); 527 contextWidget = widget; 528 out.element(Constants.WI_PREFIX, Constants.WI_NS, "union"); 529 out.startElement(); 530 return this; 531 case EVENT_ELEMENT: 532 if (Constants.WT_NS.equals(input.uri)) { 533 if ("case".equals(input.loc)) { 534 String id = input.attrs.getValue("id"); 535 if (id == null) throwSAXException("Element \"case\" missing required \"id\" attribute."); 536 String value = (String )contextWidget.getValue(); 537 if (id.equals(value != null ? value : "")) { 538 return nestedHandler; 539 } else { 540 return nullHandler; 541 } 542 } else if (FORM_TEMPLATE_EL.equals(input.loc)) { 543 throwSAXException("Element \"form-template\" must not be nested."); 544 } else { 545 throwSAXException("Unrecognized template: " + input.loc); 546 } 547 } else { 548 return unionPassThruHandler; 549 } 550 case EVENT_END_ELEMENT: 551 out.endElement(); 552 contextWidget = (Widget)contextWidgets.removeFirst(); 553 return this; 554 default: 555 out.copy(); 556 return this; 557 } 558 } 559 } 560 561 protected class UnionPassThruHandler extends Handler { 562 public Handler process() throws SAXException { 563 switch(event) { 564 case EVENT_ELEMENT: 565 if (Constants.WT_NS.equals(input.uri)) { 566 if ("case".equals(input.loc)) { 567 if (contextWidget.getValue().equals(input.attrs.getValue("id"))) { 568 return nestedHandler; 569 } else { 570 return nullHandler; 571 } 572 } else if (FORM_TEMPLATE_EL.equals(input.loc)) { 573 throwSAXException("Element \"form-template\" must not be nested."); 574 } else { 575 throwSAXException("Unrecognized template: " + input.loc); 576 } 577 } else { 578 return this; 579 } 580 default: 581 out.copy(); 582 return this; 583 } 584 } 585 } 586 587 protected class NewHandler extends Handler { 588 public Handler process() throws SAXException { 589 switch (event) { 590 case EVENT_START_ELEMENT: 591 widgetId = getWidgetId(input.attrs); 592 SaxBuffer classBuffer = (SaxBuffer)classes.get(widgetId); 593 if (classBuffer == null) { 594 throwSAXException("New: Class \"" + widgetId + "\" does not exist."); 595 } 596 handlers.addFirst(handler); 597 handler = nestedHandler; 598 classBuffer.toSAX(EffectWidgetReplacingPipe.this); 599 handler = (Handler )handlers.removeFirst(); 600 return this; 601 case EVENT_ELEMENT: 602 return nullHandler; 603 case EVENT_END_ELEMENT: 604 return this; 605 default: 606 out.copy(); 607 return this; 608 } 609 } 610 } 611 612 protected class ClassHandler extends Handler { 613 public Handler process() throws SAXException { 614 switch (event) { 615 case EVENT_START_ELEMENT: 616 widgetId = getWidgetId(input.attrs); 617 out.bufferInit(); 618 return this; 619 case EVENT_ELEMENT: 620 return bufferHandler; 621 case EVENT_END_ELEMENT: 622 classes.put(widgetId, out.getBuffer()); 623 out.bufferFini(); 624 return this; 625 default: 626 out.buffer(); 627 return this; 628 } 629 } 630 } 631 632 protected class ContinuationIdHandler extends Handler { 633 public Handler process() throws SAXException { 634 switch(event) { 635 case EVENT_START_ELEMENT: 636 Object idObj = pipeContext.evaluateExpression("$continuation/id"); 639 if (idObj == null) { 640 throwSAXException("No continuation found"); 641 } 642 643 String id = idObj.toString(); 644 out.element(Constants.WI_PREFIX, Constants.WI_NS, "continuation-id", input.attrs); 645 out.startElement(); 646 out.characters(id.toCharArray(), 0, id.length()); 647 out.endElement(); 648 return this; 649 case EVENT_END_ELEMENT: 650 return this; 651 case EVENT_IGNORABLE_WHITESPACE: 652 return this; 653 default: 654 throwSAXException("ContinuationIdHandler: No content allowed in \"continuation-id\" element"); 655 return null; } 657 } 658 } 659 660 664 protected class StylingContentHandler extends AbstractXMLPipe implements Recyclable { 665 private int elementNesting; 666 private SaxBuffer saxBuffer; 667 668 public void setSaxFragment(SaxBuffer saxFragment) { 669 saxBuffer = saxFragment; 670 } 671 672 public void recycle() { 673 super.recycle(); 674 elementNesting = 0; 675 saxBuffer = null; 676 } 677 678 public void startElement(String uri, String loc, String raw, Attributes a) 679 throws SAXException { 680 elementNesting++; 681 super.startElement(uri, loc, raw, a); 682 } 683 684 public void endElement(String uri, String loc, String raw) 685 throws SAXException { 686 elementNesting--; 687 if (elementNesting == 0 && saxBuffer != null) { 688 if (gotStylingElement) { 689 saxBuffer.toSAX(getContentHandler()); 691 } else { 692 out.startElement(Constants.WI_NS, STYLING_EL, Constants.WI_PREFIX_COLON + STYLING_EL, Constants.EMPTY_ATTRS); 694 saxBuffer.toSAX(getContentHandler()); 695 out.endElement(Constants.WI_NS, STYLING_EL, Constants.WI_PREFIX_COLON + STYLING_EL); 696 } 697 } 698 super.endElement(uri, loc, raw); 699 } 700 } 701 702 705 protected class ValidationErrorHandler extends Handler { 706 public Handler process() throws SAXException { 707 switch (event) { 708 case EVENT_START_ELEMENT: 709 widgetId = getWidgetId(input.attrs); 710 widget = getWidget(widgetId); 711 out.bufferInit(); 712 return this; 713 714 case EVENT_ELEMENT: 715 return bufferHandler; 716 717 case EVENT_END_ELEMENT: 718 if (widget instanceof ValidationErrorAware) { 719 ValidationError error = ((ValidationErrorAware)widget).getValidationError(); 720 if (error != null) { 721 out.startElement(Constants.WI_NS, VALIDATION_ERROR, Constants.WI_PREFIX_COLON + VALIDATION_ERROR, Constants.EMPTY_ATTRS); 722 error.generateSaxFragment(stylingHandler); 723 out.endElement(Constants.WI_NS, VALIDATION_ERROR, Constants.WI_PREFIX_COLON + VALIDATION_ERROR); 724 } 725 } 726 widget = null; 727 out.bufferFini(); 728 return this; 729 730 default: 731 out.copy(); 732 return this; 733 } 734 } 735 } 736 737 738 private Attributes translateAttributes(Attributes attributes, String [] names) { 739 AttributesImpl newAtts = new AttributesImpl (attributes); 740 if (names!= null) { 741 for (int i = 0; i < names.length; i++) { 742 String name = names[i]; 743 int position = newAtts.getIndex(name); 744 String newValue = pipeContext.translateText(newAtts.getValue(position)); 745 newAtts.setValue(position, newValue); 746 } 747 } 748 return newAtts; 749 } 750 751 797 801 public void recycle() { 802 super.recycle(); 803 this.contextWidget = null; 804 this.widget = null; 805 this.widgetId = null; 806 this.pipeContext = null; 807 this.namespacePrefix = null; 808 } 809 } 810 | Popular Tags |