1 16 17 package org.apache.cocoon.forms.flow.javascript.v2; 18 import org.apache.cocoon.forms.datatype.Datatype; 19 import org.apache.cocoon.forms.datatype.SelectionList; 20 import org.apache.cocoon.forms.event.ActionEvent; 21 import org.apache.cocoon.forms.event.FormHandler; 22 import org.apache.cocoon.forms.event.ValueChangedEvent; 23 import org.apache.cocoon.forms.event.WidgetEvent; 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.Field; 30 import org.apache.cocoon.forms.formmodel.Form; 31 import org.apache.cocoon.forms.formmodel.MultiValueField; 32 import org.apache.cocoon.forms.formmodel.Output; 33 import org.apache.cocoon.forms.formmodel.Repeater; 34 import org.apache.cocoon.forms.formmodel.SelectableWidget; 35 import org.apache.cocoon.forms.formmodel.Submit; 36 import org.apache.cocoon.forms.formmodel.Upload; 37 import org.apache.cocoon.forms.formmodel.Widget; 38 import org.apache.cocoon.forms.formmodel.WidgetState; 39 import org.apache.cocoon.forms.validation.ValidationError; 40 import org.apache.cocoon.forms.validation.ValidationErrorAware; 41 import org.apache.commons.lang.BooleanUtils; 42 import org.mozilla.javascript.Context; 43 import org.mozilla.javascript.JavaScriptException; 44 import org.mozilla.javascript.NativeArray; 45 import org.mozilla.javascript.Function; 46 import org.mozilla.javascript.Scriptable; 47 import org.mozilla.javascript.ScriptableObject; 48 import org.mozilla.javascript.Undefined; 49 import org.mozilla.javascript.Wrapper; 50 import java.math.BigDecimal ; 51 import java.util.List ; 52 import java.util.LinkedList ; 53 import java.util.Iterator ; 54 import java.util.Map ; 55 import java.util.HashMap ; 56 57 61 public class ScriptableWidget extends ScriptableObject { 62 63 final static String WIDGETS_PROPERTY = "__widgets__"; 64 65 Widget delegate; 66 ScriptableWidget formWidget; 67 68 class ScriptableFormHandler implements FormHandler { 69 public void handleEvent(WidgetEvent widgetEvent) { 70 Widget src = widgetEvent.getSourceWidget(); 71 ScriptableWidget w = wrap(src); 72 w.handleEvent(widgetEvent); 73 } 74 } 75 76 public String getClassName() { 77 return "Widget"; 78 } 79 80 public ScriptableWidget() { 81 } 82 83 public ScriptableWidget(Object widget) { 84 this.delegate = (Widget)unwrap(widget); 85 if (delegate instanceof Form) { 86 Form form = (Form)delegate; 87 form.setFormHandler(new ScriptableFormHandler()); 88 formWidget = this; 89 Map widgetMap = new HashMap (); 90 widgetMap.put(delegate, this); 91 defineProperty(WIDGETS_PROPERTY, widgetMap, DONTENUM|PERMANENT); 92 } 93 } 94 95 static private Object unwrap(Object obj) { 96 if (obj == Undefined.instance) { 97 return null; 98 } 99 if (obj instanceof Wrapper) { 100 return ((Wrapper)obj).unwrap(); 101 } 102 return obj; 103 } 104 105 private void deleteWrapper(Widget w) { 106 if (delegate instanceof Form) { 107 Map widgetMap = (Map )super.get(WIDGETS_PROPERTY, this); 108 widgetMap.remove(w); 109 } 110 } 111 112 private ScriptableWidget wrap(Widget w) { 113 if (w == null) return null; 114 if (delegate instanceof Form) { 115 Map widgetMap = (Map )super.get(WIDGETS_PROPERTY, this); 116 ScriptableWidget result = null; 117 result = (ScriptableWidget)widgetMap.get(w); 118 if (result == null) { 119 result = new ScriptableWidget(w); 120 result.formWidget = this; 121 result.setPrototype(getClassPrototype(this, getClassName())); 122 result.setParentScope(getParentScope()); 123 widgetMap.put(w, result); 124 } 125 return result; 126 } else { 127 return formWidget.wrap(w); 128 } 129 } 130 131 public boolean has(String id, Scriptable start) { 132 if (delegate != null && delegate instanceof ContainerWidget) { 133 Widget sub = ((ContainerWidget)delegate).getChild(id); 134 if (sub != null) { 135 return true; 136 } 137 } 138 return super.has(id, start); 139 } 140 141 public boolean has(int index, Scriptable start) { 142 if (super.has(index, start)) { 143 return true; 144 } 145 if (delegate instanceof Repeater) { 146 Repeater repeater = (Repeater)delegate; 147 return index >= 0 && index < repeater.getSize(); 148 } 149 if (delegate instanceof MultiValueField) { 150 Object [] values = (Object [])delegate.getValue(); 151 return index >= 0 && index < values.length; 152 } 153 return false; 154 } 155 156 public Object get(String id, Scriptable start) { 157 Object result = super.get(id, start); 158 if (result != NOT_FOUND) { 159 return result; 160 } 161 if (delegate != null && delegate instanceof ContainerWidget) { 162 Widget sub = ((ContainerWidget)delegate).getChild(id); 163 if (sub != null) { 164 return wrap(sub); 165 } 166 } 167 return NOT_FOUND; 168 } 169 170 public Object get(int index, Scriptable start) { 171 Object result = super.get(index, start); 172 if (result != NOT_FOUND) { 173 return result; 174 } 175 if (delegate instanceof Repeater) { 176 Repeater repeater = (Repeater)delegate; 177 if (index >= 0) { 178 int count = index + 1 - repeater.getSize(); 179 if (count > 0) { 180 ScriptableWidget[] rows = new ScriptableWidget[count]; 181 for (int i = 0; i < count; i++) { 182 rows[i] = wrap(repeater.addRow()); 183 } 184 for (int i = 0; i < count; i++) { 185 rows[i].notifyAddRow(); 186 } 187 } 188 return wrap(repeater.getRow(index)); 189 } 190 } else if (delegate instanceof MultiValueField) { 191 Object [] values = (Object [])delegate.getValue(); 192 if (index >= 0 && index < values.length) { 193 return values[index]; 194 } 195 } 196 return NOT_FOUND; 197 } 198 199 public Object [] getAllIds() { 200 Object [] result = super.getAllIds(); 201 return addWidgetIds(result); 202 } 203 204 public Object [] getIds() { 205 Object [] result = super.getIds(); 206 return addWidgetIds(result); 207 } 208 209 private Object [] addWidgetIds(Object [] result) { 210 if (delegate instanceof ContainerWidget) { 211 Iterator iter = ((ContainerWidget)delegate).getChildren(); 212 List list = new LinkedList (); 213 for (int i = 0; i < result.length; i++) { 214 list.add(result[i]); 215 } 216 while (iter.hasNext()) { 217 Widget widget = (Widget)iter.next(); 218 list.add(widget.getId()); 219 } 220 result = list.toArray(); 221 } 222 return result; 223 } 224 225 private void deleteRow(Repeater repeater, int index) { 226 Widget row = repeater.getRow(index); 227 ScriptableWidget s = wrap(row); 228 s.notifyRemoveRow(); 229 formWidget.deleteWrapper(row); 230 repeater.removeRow(index); 231 } 232 233 private void notifyAddRow() { 234 ScriptableWidget repeater = wrap(delegate.getParent()); 235 Object prop = getProperty(repeater, "onAddRow"); 236 if (prop instanceof Function) { 237 try { 238 Function fun = (Function)prop; 239 Object [] args = new Object [1]; 240 Scriptable scope = getTopLevelScope(this); 241 Scriptable thisObj = scope; 242 Context cx = Context.getCurrentContext(); 243 args[0] = this; 244 fun.call(cx, scope, thisObj, args); 245 } catch (Exception exc) { 246 throw Context.reportRuntimeError(exc.getMessage()); 247 } 248 } 249 } 250 251 private void notifyRemoveRow() { 252 ScriptableWidget repeater = wrap(delegate.getParent()); 253 Object prop = getProperty(repeater, "onRemoveRow"); 254 if (prop instanceof Function) { 255 try { 256 Function fun = (Function)prop; 257 Object [] args = new Object [1]; 258 Scriptable scope = getTopLevelScope(this); 259 Scriptable thisObj = scope; 260 Context cx = Context.getCurrentContext(); 261 args[0] = this; 262 fun.call(cx, scope, thisObj, args); 263 } catch (Exception exc) { 264 throw Context.reportRuntimeError(exc.getMessage()); 265 } 266 } 267 } 268 269 public void delete(int index) { 270 if (delegate instanceof Repeater) { 271 Repeater repeater = (Repeater)delegate; 272 if (index >= 0 && index < repeater.getSize()) { 273 deleteRow(repeater, index); 274 return; 275 } 276 } else if (delegate instanceof MultiValueField) { 277 MultiValueField field = (MultiValueField)delegate; 278 Object [] values = (Object [])field.getValue(); 279 if (values != null && values.length > index) { 280 Object [] newValues = new Object [values.length-1]; 281 int i; 282 for (i = 0; i < index; i++) { 283 newValues[i] = values[i]; 284 } 285 i++; 286 for (;i < values.length; i++) { 287 newValues[i-1] = values[i]; 288 } 289 field.setValues(newValues); 290 } 291 return; 292 } 293 super.delete(index); 294 } 295 296 public Object jsGet_value() { 297 return delegate.getValue(); 298 } 299 300 public Object jsFunction_getValue() { 301 return jsGet_value(); 302 } 303 304 public void jsFunction_setValue(Object value) throws JavaScriptException { 305 jsSet_value(value); 306 } 307 308 public void jsSet_length(int len) { 309 if (delegate instanceof Repeater) { 310 Repeater repeater = (Repeater)delegate; 311 int size = repeater.getSize(); 312 if (size > len) { 313 while (repeater.getSize() > len) { 314 deleteRow(repeater, repeater.getSize() - 1); 315 } 316 } else { 317 for (int i = size; i < len; ++i) { 318 wrap(repeater.addRow()).notifyAddRow(); 319 } 320 } 321 } 322 } 323 324 public Object jsGet_length() { 325 if (delegate instanceof Repeater) { 326 Repeater repeater = (Repeater)delegate; 327 return new Integer (repeater.getSize()); 328 } 329 return Undefined.instance; 330 } 331 332 public void jsSet_value(Object value) throws JavaScriptException { 333 if (delegate instanceof AggregateField) { 334 AggregateField aggregateField = (AggregateField)delegate; 335 if (value instanceof Scriptable) { 336 Scriptable obj = (Scriptable)value; 337 Object [] ids = obj.getIds(); 338 for (int i = 0; i < ids.length; i++) { 339 String id = String.valueOf(ids[i]); 340 Object val = getProperty(obj, id); 341 ScriptableWidget wid = wrap(aggregateField.getChild(id)); 342 if (wid == null) { 343 throw new JavaScriptException("No field \"" + id + "\" in widget \"" + aggregateField.getId() + "\""); 344 } 345 if (wid.delegate instanceof Field || 346 wid.delegate instanceof BooleanField || 347 wid.delegate instanceof Output) { 348 if (val instanceof Scriptable) { 349 Scriptable s = (Scriptable)val; 350 if (s.has("value", s)) { 351 wid.jsSet_value(s.get("value", s)); 352 } 353 } 354 } else { 355 wid.jsSet_value(val); 356 } 357 } 358 aggregateField.combineFields(); 359 return; 360 } 361 } 363 if (delegate instanceof DataWidget) { 364 value = unwrap(value); 365 if (value != null) { 366 Datatype datatype = ((DataWidget)delegate).getDatatype(); 368 Class typeClass = datatype.getTypeClass(); 369 if (typeClass == String .class) { 370 value = Context.toString(value); 371 } else if (typeClass == boolean.class || 372 typeClass == Boolean .class) { 373 value = Context.toBoolean(value) ? Boolean.TRUE : Boolean.FALSE; 374 } else { 375 if (value instanceof Double ) { 376 if (typeClass == long.class || typeClass == Long .class) { 378 value = new Long (((Number )value).longValue()); 379 } else if (typeClass == int.class || 380 typeClass == Integer .class) { 381 value = new Integer (((Number )value).intValue()); 382 } else if (typeClass == float.class || 383 typeClass == Float .class) { 384 value = new Float (((Number )value).floatValue()); 385 } else if (typeClass == short.class || 386 typeClass == Short .class) { 387 value = new Short (((Number )value).shortValue()); 388 } else if (typeClass == BigDecimal .class) { 389 value = new BigDecimal (((Number )value).doubleValue()); 390 } 391 } 392 } 393 } 394 delegate.setValue(value); 395 } else if (delegate instanceof BooleanField) { 396 BooleanField field = (BooleanField)delegate; 397 field.setValue(BooleanUtils.toBooleanObject(Context.toBoolean(value))); 398 } else if (delegate instanceof Repeater) { 399 Repeater repeater = (Repeater)delegate; 400 if (value instanceof NativeArray) { 401 NativeArray arr = (NativeArray)value; 402 Object length = getProperty(arr, "length"); 403 int len = ((Number )length).intValue(); 404 for (int i = repeater.getSize(); i >= len; --i) { 405 deleteRow(repeater, i); 406 } 407 for (int i = 0; i < len; i++) { 408 Object elemValue = getProperty(arr, i); 409 ScriptableWidget wid = wrap(repeater.getRow(i)); 410 wid.jsSet_value(elemValue); 411 } 412 } 413 } else if (delegate instanceof Repeater.RepeaterRow) { 414 Repeater.RepeaterRow row = (Repeater.RepeaterRow)delegate; 415 if (value instanceof Scriptable) { 416 Scriptable obj = (Scriptable)value; 417 Object [] ids = obj.getIds(); 418 for (int i = 0; i < ids.length; i++) { 419 String id = String.valueOf(ids[i]); 420 Object val = getProperty(obj, id); 421 ScriptableWidget wid = wrap(row.getChild(id)); 422 if (wid == null) { 423 throw new JavaScriptException("No field \"" + id + "\" in row " + i + " of repeater \"" + row.getParent().getId() + "\""); 424 } 425 if (wid.delegate instanceof Field || 426 wid.delegate instanceof BooleanField || 427 wid.delegate instanceof Output) { 428 if (val instanceof Scriptable) { 429 Scriptable s = (Scriptable)val; 430 if (s.has("value", s)) { 431 wid.jsSet_value(s.get("value", s)); 432 } 433 } 434 } else { 435 wid.jsSet_value(val); 436 } 437 } 438 } else { 439 throw new JavaScriptException("Expected an object instead of: " + Context.toString(value)); 440 } 441 } else if (delegate instanceof MultiValueField) { 442 MultiValueField field = (MultiValueField)delegate; 443 Object [] values = null; 444 if (value instanceof NativeArray) { 445 NativeArray arr = (NativeArray)value; 446 Object length = getProperty(arr, "length"); 447 int len = ((Number )length).intValue(); 448 values = new Object [len]; 449 for (int i = 0; i < len; i++) { 450 Object elemValue = getProperty(arr, i); 451 values[i] = unwrap(elemValue); 452 } 453 } else if (value instanceof Object []) { 454 values = (Object [])value; 455 } 456 field.setValues(values); 457 } else { 458 delegate.setValue(value); 459 } 460 } 461 462 public String jsFunction_getId() { 463 return delegate.getId(); 464 } 465 466 public WidgetState jsGet_state() { 467 return delegate.getState(); 468 } 469 470 public void jsSet_state(Object stateObj) { 471 Object obj = unwrap(stateObj); 472 WidgetState state = null; 473 474 if (obj instanceof String ) { 475 state = WidgetState.stateForName((String )obj); 476 } else if (obj instanceof WidgetState) { 477 state = (WidgetState)obj; 478 } 479 480 if (state == null) { 481 throw new IllegalArgumentException ("Invalid value for widgetState " + stateObj); 482 } 483 484 delegate.setState(state); 485 } 486 487 public ScriptableWidget jsFunction_getSubmitWidget() { 488 return wrap(delegate.getForm().getSubmitWidget()); 489 } 490 491 public String jsFunction_getRequestParameterName() { 492 return delegate.getRequestParameterName(); 493 } 494 495 public Object jsFunction_getParent() { 496 if (delegate != null) { 497 return wrap(delegate.getParent()); 498 } 499 return Undefined.instance; 500 } 501 502 public boolean jsFunction_isRequired() { 503 return delegate.isRequired(); 504 } 505 506 public ScriptableWidget jsFunction_getForm() { 507 return formWidget; 508 } 509 510 public boolean jsFunction_equals(Object other) { 511 if (other instanceof ScriptableWidget) { 512 ScriptableWidget otherWidget = (ScriptableWidget)other; 513 return delegate.equals(otherWidget.delegate); 514 } 515 return false; 516 } 517 518 public ScriptableWidget jsFunction_getChild(String id) { 519 Widget sub = null; 520 if (delegate instanceof ContainerWidget) 521 sub = ((ContainerWidget)delegate).getChild(id); 522 return wrap(sub); 523 } 524 525 public ScriptableWidget jsFunction_lookupWidget(String id) { 526 Widget sub = null; 527 sub = delegate.lookupWidget(id); 528 return wrap(sub); 529 } 530 531 public void jsFunction_setValidationError(Object message , 532 Object parameters) { 533 if (delegate instanceof ValidationErrorAware) { 534 String [] parms = null; 535 if (parameters != null && parameters != Undefined.instance) { 536 Scriptable obj = Context.toObject(parameters, this); 537 int len = (int) 538 Context.toNumber(getProperty(obj, "length")); 539 parms = new String [len]; 540 for (int i = 0; i < len; i++) { 541 parms[i] = Context.toString(getProperty(obj, i)); 542 } 543 } 544 ValidationError validationError = null; 545 if (message != null) { 546 if (parms != null && parms.length > 0) { 547 validationError = 548 new ValidationError(Context.toString(message), parms); 549 } else { 550 validationError = 551 new ValidationError(Context.toString(message), parms != null); 552 } 553 } 554 ((ValidationErrorAware)delegate).setValidationError(validationError); 555 formWidget.notifyValidationErrorListener(this, validationError); 556 } 557 } 558 559 private void notifyValidationErrorListener(ScriptableWidget widget, 560 ValidationError error) { 561 Object fun = getProperty(this, "validationErrorListener"); 562 if (fun instanceof Function) { 563 try { 564 Scriptable scope = getTopLevelScope(this); 565 Scriptable thisObj = scope; 566 Context cx = Context.getCurrentContext(); 567 Object [] args = new Object [2]; 568 args[0] = widget; 569 args[1] = error; 570 ((Function)fun).call(cx, scope, thisObj, args); 571 } catch (Exception exc) { 572 throw Context.reportRuntimeError(exc.getMessage()); 573 } 574 } 575 } 576 577 public Widget jsFunction_unwrap() { 578 return delegate; 579 } 580 581 public ScriptableWidget jsFunction_addRow() { 582 ScriptableWidget result = null; 583 if (delegate instanceof Repeater) { 584 result = wrap(((Repeater)delegate).addRow()); 585 result.notifyAddRow(); 586 } 587 return result; 588 } 589 590 public ScriptableObject jsFunction_getRow(int index) { 591 if (delegate instanceof Repeater) { 592 return wrap(((Repeater)delegate).getRow(index)); 593 } 594 return null; 595 } 596 597 public void jsFunction_removeRow(Object obj) throws JavaScriptException { 598 if (delegate instanceof Repeater) { 599 Repeater repeater = (Repeater)delegate; 600 if (obj instanceof Function) { 601 Function fun = (Function)obj; 602 int len = repeater.getSize(); 603 boolean[] index = new boolean[len]; 604 Object [] args = new Object [1]; 605 Scriptable scope = getTopLevelScope(this); 606 Scriptable thisObj = scope; 607 Context cx = Context.getCurrentContext(); 608 for (int i = 0; i < len; i++) { 609 ScriptableWidget row = wrap(repeater.getRow(i)); 610 args[0] = row; 611 Object result = fun.call(cx, scope, thisObj, args); 612 index[i] = Context.toBoolean(result); 613 } 614 for (int i = len-1; i >= 0; --i) { 615 if (index[i]) { 616 deleteRow(repeater, i); 617 } 618 } 619 } else { 620 int index = (int)Context.toNumber(obj); 621 if (index >= 0 && index < repeater.getSize()) { 622 deleteRow(repeater, index); 623 } 624 } 625 } 626 } 627 628 private void handleEvent(WidgetEvent e) { 629 if (e instanceof ActionEvent) { 630 Object obj = super.get("onClick", this); 631 if (obj instanceof Function) { 632 try { 633 Function fun = (Function)obj; 634 Object [] args = new Object [1]; 635 Scriptable scope = getTopLevelScope(this); 636 Scriptable thisObj = scope; 637 Context cx = Context.getCurrentContext(); 638 args[0] = ((ActionEvent)e).getActionCommand(); 639 fun.call(cx, scope, thisObj, args); 640 } catch (Exception exc) { 641 throw Context.reportRuntimeError(exc.getMessage()); 642 } 643 } 644 } else if (e instanceof ValueChangedEvent) { 645 ValueChangedEvent vce = (ValueChangedEvent)e; 646 Object obj = super.get("onChange", this); 647 if (obj instanceof Function) { 648 try { 649 Function fun = (Function)obj; 650 Object [] args = new Object [2]; 651 Scriptable scope = getTopLevelScope(this); 652 Scriptable thisObj = scope; 653 Context cx = Context.getCurrentContext(); 654 args[0] = vce.getOldValue(); 655 args[1] = vce.getNewValue(); 656 fun.call(cx, scope, thisObj, args); 657 } catch (Exception exc) { 658 throw Context.reportRuntimeError(exc.getMessage()); 659 } 660 } 661 } 662 } 663 664 public void jsFunction_setSelectionList(Object arg, 665 Object valuePathArg, 666 Object labelPathArg) 667 throws Exception { 668 if (delegate instanceof SelectableWidget) { 669 arg = unwrap(arg); 670 if (valuePathArg != Undefined.instance && labelPathArg != Undefined.instance) { 671 String valuePath = Context.toString(valuePathArg); 672 String labelPath = Context.toString(labelPathArg); 673 ((SelectableWidget)delegate).setSelectionList(arg, valuePath, labelPath); 674 } else { 675 if (arg instanceof SelectionList) { 676 SelectionList selectionList = (SelectionList)arg; 677 ((SelectableWidget)delegate).setSelectionList(selectionList); 678 } else { 679 String str = Context.toString(arg); 680 ((SelectableWidget)delegate).setSelectionList(str); 681 } 682 } 683 } 684 } 685 686 static final Object [] WIDGET_CLASS_MAP = { 687 Form.class, "Form", 688 Field.class, "Field", 689 Action.class, "Action", 690 Repeater.class, "Repeater", 691 Repeater.RepeaterRow.class, "RepeaterRow", 692 AggregateField.class, "AggregateField", 693 BooleanField.class, "BooleanField", 694 MultiValueField.class, "MultiValueField", 695 Output.class, "Output", 696 Submit.class, "Submit", 697 Upload.class, "Upload" 698 }; 699 700 public String jsFunction_getWidgetClass() { 701 for (int i = 0; i < WIDGET_CLASS_MAP.length; i += 2) { 702 Class c = (Class )WIDGET_CLASS_MAP[i]; 703 if (c.isAssignableFrom(delegate.getClass())) { 704 return (String )WIDGET_CLASS_MAP[i + 1]; 705 } 706 } 707 return "<unknown>"; 708 } 709 710 public String jsFunction_toString() { 711 return "[object Widget (" + jsFunction_getWidgetClass() + ")]"; 712 } 713 714 } 715 | Popular Tags |