Clover coverage report - Code Coverage for tapestry release 4.0-beta-13
Coverage timestamp: Sat Nov 12 2005 13:42:12 EST
file stats: LOC: 726   Methods: 27
NCLOC: 383   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
FormSupportImpl.java 95.6% 93.5% 92.6% 93.9%
coverage coverage
 1    // Copyright 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.tapestry.form;
 16   
 17    import java.util.ArrayList;
 18    import java.util.Arrays;
 19    import java.util.Collections;
 20    import java.util.HashMap;
 21    import java.util.HashSet;
 22    import java.util.Iterator;
 23    import java.util.List;
 24    import java.util.Map;
 25    import java.util.Set;
 26   
 27    import org.apache.hivemind.ApplicationRuntimeException;
 28    import org.apache.hivemind.HiveMind;
 29    import org.apache.hivemind.Location;
 30    import org.apache.hivemind.Resource;
 31    import org.apache.hivemind.util.ClasspathResource;
 32    import org.apache.hivemind.util.Defense;
 33    import org.apache.tapestry.IComponent;
 34    import org.apache.tapestry.IForm;
 35    import org.apache.tapestry.IMarkupWriter;
 36    import org.apache.tapestry.IRender;
 37    import org.apache.tapestry.IRequestCycle;
 38    import org.apache.tapestry.NestedMarkupWriter;
 39    import org.apache.tapestry.PageRenderSupport;
 40    import org.apache.tapestry.StaleLinkException;
 41    import org.apache.tapestry.Tapestry;
 42    import org.apache.tapestry.TapestryUtils;
 43    import org.apache.tapestry.engine.ILink;
 44    import org.apache.tapestry.services.ServiceConstants;
 45    import org.apache.tapestry.util.IdAllocator;
 46    import org.apache.tapestry.valid.IValidationDelegate;
 47   
 48    /**
 49    * Encapsulates most of the behavior of a Form component.
 50    *
 51    * @author Howard M. Lewis Ship
 52    * @since 4.0
 53    */
 54    public class FormSupportImpl implements FormSupport
 55    {
 56    /**
 57    * Name of query parameter storing the ids alloocated while rendering the form, as a comma
 58    * seperated list. This information is used when the form is submitted, to ensure that the
 59    * rewind allocates the exact same sequence of ids.
 60    */
 61   
 62    public static final String FORM_IDS = "formids";
 63   
 64    /**
 65    * Names of additional ids that were pre-reserved, as a comma-sepereated list. These are names
 66    * beyond that standard set. Certain engine services include extra parameter values that must be
 67    * accounted for, and page properties may be encoded as additional query parameters.
 68    */
 69   
 70    public static final String RESERVED_FORM_IDS = "reservedids";
 71   
 72    /**
 73    * Indicates why the form was submitted: whether for normal ("submit"), refresh, or because the
 74    * form was canceled.
 75    */
 76   
 77    public static final String SUBMIT_MODE = "submitmode";
 78   
 79    public static final String SCRIPT = "/org/apache/tapestry/form/Form.js";
 80   
 81    private final static Set _standardReservedIds;
 82   
 83    /**
 84    * Attribute set to true when a field has been focused; used to prevent conflicting JavaScript
 85    * for field focusing from being emitted.
 86    */
 87   
 88    public static final String FIELD_FOCUS_ATTRIBUTE = "org.apache.tapestry.field-focused";
 89   
 90    static
 91    {
 92  1 Set set = new HashSet();
 93   
 94  1 set.addAll(Arrays.asList(ServiceConstants.RESERVED_IDS));
 95  1 set.add(FORM_IDS);
 96  1 set.add(RESERVED_FORM_IDS);
 97  1 set.add(SUBMIT_MODE);
 98  1 set.add(FormConstants.SUBMIT_NAME_PARAMETER);
 99   
 100  1 _standardReservedIds = Collections.unmodifiableSet(set);
 101    }
 102   
 103    private final static Set _submitModes;
 104   
 105    static
 106    {
 107  1 Set set = new HashSet();
 108  1 set.add(FormConstants.SUBMIT_CANCEL);
 109  1 set.add(FormConstants.SUBMIT_NORMAL);
 110  1 set.add(FormConstants.SUBMIT_REFRESH);
 111   
 112  1 _submitModes = Collections.unmodifiableSet(set);
 113    }
 114   
 115    /**
 116    * Used when rewinding the form to figure to match allocated ids (allocated during the rewind)
 117    * against expected ids (allocated in the previous request cycle, when the form was rendered).
 118    */
 119   
 120    private int _allocatedIdIndex;
 121   
 122    /**
 123    * The list of allocated ids for form elements within this form. This list is constructed when a
 124    * form renders, and is validated against when the form is rewound.
 125    */
 126   
 127    private final List _allocatedIds = new ArrayList();
 128   
 129    private final IRequestCycle _cycle;
 130   
 131    private final IdAllocator _elementIdAllocator = new IdAllocator();
 132   
 133    private String _encodingType;
 134   
 135    private final List _deferredRunnables = new ArrayList();
 136   
 137    /**
 138    * Map keyed on extended component id, value is the pre-rendered markup for that component.
 139    */
 140   
 141    private final Map _prerenderMap = new HashMap();
 142   
 143    /**
 144    * {@link Map}, keyed on {@link FormEventType}. Values are either a String (the function name
 145    * of a single event handler), or a List of Strings (a sequence of event handler function
 146    * names).
 147    */
 148   
 149    private Map _events;
 150   
 151    private final IForm _form;
 152   
 153    private final List _hiddenValues = new ArrayList();
 154   
 155    private final boolean _rewinding;
 156   
 157    private final IMarkupWriter _writer;
 158   
 159    private final Resource _script;
 160   
 161    private final IValidationDelegate _delegate;
 162   
 163    private final PageRenderSupport _pageRenderSupport;
 164   
 165  93 public FormSupportImpl(IMarkupWriter writer, IRequestCycle cycle, IForm form)
 166    {
 167  93 Defense.notNull(writer, "writer");
 168  93 Defense.notNull(cycle, "cycle");
 169  93 Defense.notNull(form, "form");
 170   
 171  93 _writer = writer;
 172  93 _cycle = cycle;
 173  93 _form = form;
 174  93 _delegate = form.getDelegate();
 175   
 176  93 _rewinding = cycle.isRewound(form);
 177  93 _allocatedIdIndex = 0;
 178   
 179  93 _script = new ClasspathResource(cycle.getEngine().getClassResolver(), SCRIPT);
 180   
 181  93 _pageRenderSupport = TapestryUtils.getOptionalPageRenderSupport(cycle);
 182    }
 183   
 184    /**
 185    * Adds an event handler for the form, of the given type.
 186    */
 187   
 188  6 public void addEventHandler(FormEventType type, String functionName)
 189    {
 190  6 if (_events == null)
 191  3 _events = new HashMap();
 192   
 193  6 List functionList = (List) _events.get(type);
 194   
 195    // The value can either be a String, or a List of String. Since
 196    // it is rare for there to be more than one event handling function,
 197    // we start with just a String.
 198   
 199  6 if (functionList == null)
 200    {
 201  3 functionList = new ArrayList();
 202   
 203  3 _events.put(type, functionList);
 204    }
 205   
 206  6 functionList.add(functionName);
 207    }
 208   
 209    /**
 210    * Adds hidden fields for parameters provided by the {@link ILink}. These parameters define the
 211    * information needed to dispatch the request, plus state information. The names of these
 212    * parameters must be reserved so that conflicts don't occur that could disrupt the request
 213    * processing. For example, if the id 'page' is not reserved, then a conflict could occur with a
 214    * component whose id is 'page'. A certain number of ids are always reserved, and we find any
 215    * additional ids beyond that set.
 216    */
 217   
 218  58 private void addHiddenFieldsForLinkParameters(ILink link)
 219    {
 220  58 String[] names = link.getParameterNames();
 221  58 int count = Tapestry.size(names);
 222   
 223  58 StringBuffer extraIds = new StringBuffer();
 224  58 String sep = "";
 225  58 boolean hasExtra = false;
 226   
 227    // All the reserved ids, which are essential for
 228    // dispatching the request, are automatically reserved.
 229    // Thus, if you have a component with an id of 'service', its element id
 230    // will likely be 'service$0'.
 231   
 232  58 preallocateReservedIds();
 233   
 234  58 for (int i = 0; i < count; i++)
 235    {
 236  293 String name = names[i];
 237   
 238    // Reserve the name.
 239   
 240  293 if (!_standardReservedIds.contains(name))
 241    {
 242  12 _elementIdAllocator.allocateId(name);
 243   
 244  12 extraIds.append(sep);
 245  12 extraIds.append(name);
 246   
 247  12 sep = ",";
 248  12 hasExtra = true;
 249    }
 250   
 251  293 addHiddenFieldsForLinkParameter(link, name);
 252    }
 253   
 254  58 if (hasExtra)
 255  12 addHiddenValue(RESERVED_FORM_IDS, extraIds.toString());
 256    }
 257   
 258  343 public void addHiddenValue(String name, String value)
 259    {
 260  343 _hiddenValues.add(new HiddenFieldData(name, value));
 261    }
 262   
 263  5 public void addHiddenValue(String name, String id, String value)
 264    {
 265  5 _hiddenValues.add(new HiddenFieldData(name, id, value));
 266    }
 267   
 268    /**
 269    * Converts the allocateIds property into a string, a comma-separated list of ids. This is
 270    * included as a hidden field in the form and is used to identify discrepencies when the form is
 271    * submitted.
 272    */
 273   
 274  53 private String buildAllocatedIdList()
 275    {
 276  53 StringBuffer buffer = new StringBuffer();
 277  53 int count = _allocatedIds.size();
 278   
 279  53 for (int i = 0; i < count; i++)
 280    {
 281  61 if (i > 0)
 282  23 buffer.append(',');
 283   
 284  61 buffer.append(_allocatedIds.get(i));
 285    }
 286   
 287  53 return buffer.toString();
 288    }
 289   
 290  53 private void emitEventHandlers(String formId)
 291    {
 292  53 if (_events == null || _events.isEmpty())
 293  50 return;
 294   
 295  3 StringBuffer buffer = new StringBuffer();
 296   
 297  3 Iterator i = _events.entrySet().iterator();
 298   
 299  3 while (i.hasNext())
 300    {
 301  3 Map.Entry entry = (Map.Entry) i.next();
 302  3 FormEventType type = (FormEventType) entry.getKey();
 303  3 Object value = entry.getValue();
 304   
 305  3 buffer.append("Tapestry.");
 306  3 buffer.append(type.getAddHandlerFunctionName());
 307  3 buffer.append("('");
 308  3 buffer.append(formId);
 309  3 buffer.append("', function (event)\n{");
 310   
 311  3 List l = (List) value;
 312  3 int count = l.size();
 313   
 314  3 for (int j = 0; j < count; j++)
 315    {
 316  6 String functionName = (String) l.get(j);
 317   
 318  6 if (j > 0)
 319    {
 320  3 buffer.append(";");
 321    }
 322   
 323  6 buffer.append("\n ");
 324  6 buffer.append(functionName);
 325   
 326    // It's supposed to be function names, but some of Paul's validation code
 327    // adds inline code to be executed instead.
 328   
 329  6 if (!functionName.endsWith(")"))
 330    {
 331  5 buffer.append("()");
 332    }
 333    }
 334   
 335  3 buffer.append(";\n});\n");
 336    }
 337   
 338    // TODO: If PRS is null ...
 339   
 340  3 _pageRenderSupport.addInitializationScript(buffer.toString());
 341    }
 342   
 343    /**
 344    * Constructs a unique identifier (within the Form). The identifier consists of the component's
 345    * id, with an index number added to ensure uniqueness.
 346    * <p>
 347    * Simply invokes
 348    * {@link #getElementId(org.apache.tapestry.form.IFormComponent, java.lang.String)}with the
 349    * component's id.
 350    */
 351   
 352  20 public String getElementId(IFormComponent component)
 353    {
 354  20 return getElementId(component, component.getId());
 355    }
 356   
 357    /**
 358    * Constructs a unique identifier (within the Form). The identifier consists of the component's
 359    * id, with an index number added to ensure uniqueness.
 360    * <p>
 361    * Simply invokes
 362    * {@link #getElementId(org.apache.tapestry.form.IFormComponent, java.lang.String)}with the
 363    * component's id.
 364    */
 365   
 366  116 public String getElementId(IFormComponent component, String baseId)
 367    {
 368    // $ is not a valid character in an XML/XHTML id, so convert it to an underscore.
 369   
 370  116 String filteredId = TapestryUtils.convertTapestryIdToNMToken(baseId);
 371   
 372  116 String result = _elementIdAllocator.allocateId(filteredId);
 373   
 374  116 if (_rewinding)
 375    {
 376  53 if (_allocatedIdIndex >= _allocatedIds.size())
 377    {
 378  1 throw new StaleLinkException(FormMessages.formTooManyIds(_form, _allocatedIds
 379    .size(), component), component);
 380    }
 381   
 382  52 String expected = (String) _allocatedIds.get(_allocatedIdIndex);
 383   
 384  52 if (!result.equals(expected))
 385  2 throw new StaleLinkException(FormMessages.formIdMismatch(
 386    _form,
 387    _allocatedIdIndex,
 388    expected,
 389    result,
 390    component), component);
 391    }
 392    else
 393    {
 394  63 _allocatedIds.add(result);
 395    }
 396   
 397  113 _allocatedIdIndex++;
 398   
 399  113 component.setName(result);
 400   
 401  113 return result;
 402    }
 403   
 404  154 public boolean isRewinding()
 405    {
 406  154 return _rewinding;
 407    }
 408   
 409  92 private void preallocateReservedIds()
 410    {
 411  92 for (int i = 0; i < ServiceConstants.RESERVED_IDS.length; i++)
 412  552 _elementIdAllocator.allocateId(ServiceConstants.RESERVED_IDS[i]);
 413    }
 414   
 415    /**
 416    * Invoked when rewinding a form to re-initialize the _allocatedIds and _elementIdAllocator.
 417    * Converts a string passed as a parameter (and containing a comma separated list of ids) back
 418    * into the allocateIds property. In addition, return the state of the ID allocater back to
 419    * where it was at the start of the render.
 420    *
 421    * @see #buildAllocatedIdList()
 422    * @since 3.0
 423    */
 424   
 425  34 private void reinitializeIdAllocatorForRewind()
 426    {
 427  34 String allocatedFormIds = _cycle.getParameter(FORM_IDS);
 428   
 429  34 String[] ids = TapestryUtils.split(allocatedFormIds);
 430   
 431  34 for (int i = 0; i < ids.length; i++)
 432  57 _allocatedIds.add(ids[i]);
 433   
 434    // Now, reconstruct the the initial state of the
 435    // id allocator.
 436   
 437  34 preallocateReservedIds();
 438   
 439  34 String extraReservedIds = _cycle.getParameter(RESERVED_FORM_IDS);
 440   
 441  34 ids = TapestryUtils.split(extraReservedIds);
 442   
 443  34 for (int i = 0; i < ids.length; i++)
 444  3 _elementIdAllocator.allocateId(ids[i]);
 445    }
 446   
 447  58 public void render(String method, IRender informalParametersRenderer, ILink link, String scheme)
 448    {
 449  58 String formId = _form.getName();
 450   
 451  58 emitEventManagerInitialization(formId);
 452   
 453    // Convert the link's query parameters into a series of
 454    // hidden field values (that will be rendered later).
 455   
 456  58 addHiddenFieldsForLinkParameters(link);
 457   
 458    // Create a hidden field to store the submission mode, in case
 459    // client-side JavaScript forces an update.
 460   
 461  58 addHiddenValue(SUBMIT_MODE, null);
 462   
 463    // And another for the name of the component that
 464    // triggered the submit.
 465   
 466  58 addHiddenValue(FormConstants.SUBMIT_NAME_PARAMETER, null);
 467   
 468  58 IMarkupWriter nested = _writer.getNestedWriter();
 469   
 470  58 _form.renderBody(nested, _cycle);
 471   
 472  53 runDeferredRunnables();
 473   
 474  53 writeTag(_writer, method, link.getURL(scheme, null, 0, null, false));
 475   
 476    // For HTML compatibility
 477  53 _writer.attribute("name", formId);
 478   
 479    // For XHTML compatibility
 480  53 _writer.attribute("id", formId);
 481   
 482  53 if (_encodingType != null)
 483  3 _writer.attribute("enctype", _encodingType);
 484   
 485    // Write out event handlers collected during the rendering.
 486   
 487  53 emitEventHandlers(formId);
 488   
 489  53 informalParametersRenderer.render(_writer, _cycle);
 490   
 491    // Finish the <form> tag
 492   
 493  53 _writer.println();
 494   
 495  53 writeHiddenFields();
 496   
 497    // Close the nested writer, inserting its contents.
 498   
 499  53 nested.close();
 500   
 501    // Close the <form> tag.
 502   
 503  53 _writer.end();
 504   
 505  53 String fieldId = _delegate.getFocusField();
 506   
 507  53 if (fieldId == null || _pageRenderSupport == null)
 508  42 return;
 509   
 510    // If the form doesn't support focus, or the focus has already been set by a different form,
 511    // then do nothing.
 512   
 513  11 if (!_form.getFocus() || _cycle.getAttribute(FIELD_FOCUS_ATTRIBUTE) != null)
 514  2 return;
 515   
 516  9 _pageRenderSupport.addInitializationScript("Tapestry.set_focus('" + fieldId + "');");
 517   
 518  9 _cycle.setAttribute(FIELD_FOCUS_ATTRIBUTE, Boolean.TRUE);
 519    }
 520   
 521    /**
 522    * Pre-renders the form, setting up some client-side form support. Returns the name of the
 523    * client-side form event manager variable.
 524    */
 525  58 protected void emitEventManagerInitialization(String formId)
 526    {
 527  58 if (_pageRenderSupport == null)
 528  8 return;
 529   
 530  50 _pageRenderSupport.addExternalScript(_script);
 531   
 532  50 _pageRenderSupport.addInitializationScript("Tapestry.register_form('" + formId + "');");
 533    }
 534   
 535  35 public String rewind()
 536    {
 537  35 _form.getDelegate().clear();
 538   
 539  35 String mode = _cycle.getParameter(SUBMIT_MODE);
 540   
 541    // On a cancel, don't bother rendering the body or anything else at all.
 542   
 543  35 if (FormConstants.SUBMIT_CANCEL.equals(mode))
 544  1 return mode;
 545   
 546  34 reinitializeIdAllocatorForRewind();
 547   
 548  34 _form.renderBody(_writer, _cycle);
 549   
 550  30 int expected = _allocatedIds.size();
 551   
 552    // The other case, _allocatedIdIndex > expected, is
 553    // checked for inside getElementId(). Remember that
 554    // _allocatedIdIndex is incremented after allocating.
 555   
 556  30 if (_allocatedIdIndex < expected)
 557    {
 558  1 String nextExpectedId = (String) _allocatedIds.get(_allocatedIdIndex);
 559   
 560  1 throw new StaleLinkException(FormMessages.formTooFewIds(_form, expected
 561    - _allocatedIdIndex, nextExpectedId), _form);
 562    }
 563   
 564  29 runDeferredRunnables();
 565   
 566  29 if (_submitModes.contains(mode))
 567  5 return mode;
 568   
 569    // Either something wacky on the client side, or a client without
 570    // javascript enabled.
 571   
 572  24 return FormConstants.SUBMIT_NORMAL;
 573   
 574    }
 575   
 576  82 private void runDeferredRunnables()
 577    {
 578  82 Iterator i = _deferredRunnables.iterator();
 579  82 while (i.hasNext())
 580    {
 581  2 Runnable r = (Runnable) i.next();
 582   
 583  2 r.run();
 584    }
 585    }
 586   
 587  5 public void setEncodingType(String encodingType)
 588    {
 589   
 590  5 if (_encodingType != null && !_encodingType.equals(encodingType))
 591  1 throw new ApplicationRuntimeException(FormMessages.encodingTypeContention(
 592    _form,
 593    _encodingType,
 594    encodingType), _form, null, null);
 595   
 596  4 _encodingType = encodingType;
 597    }
 598   
 599    /**
 600    * Overwridden by {@link org.apache.tapestry.wml.GoFormSupportImpl} (WML).
 601    */
 602  332 protected void writeHiddenField(IMarkupWriter writer, String name, String id, String value)
 603    {
 604  332 writer.beginEmpty("input");
 605  332 writer.attribute("type", "hidden");
 606  332 writer.attribute("name", name);
 607   
 608  332 if (HiveMind.isNonBlank(id))
 609  2 writer.attribute("id", id);
 610   
 611  332 writer.attribute("value", value == null ? "" : value);
 612  332 writer.println();
 613    }
 614   
 615  374 private void writeHiddenField(String name, String id, String value)
 616    {
 617  374 writeHiddenField(_writer, name, id, value);
 618    }
 619   
 620    /**
 621    * Writes out all hidden values previously added by
 622    * {@link #addHiddenValue(String, String, String)}. Writes a &lt;div&gt; tag around
 623    * {@link #writeHiddenFieldList()}. Overriden by
 624    * {@link org.apache.tapestry.wml.GoFormSupportImpl}.
 625    */
 626   
 627  47 protected void writeHiddenFields()
 628    {
 629  47 _writer.begin("div");
 630  47 _writer.attribute("style", "display:none;");
 631   
 632  47 writeHiddenFieldList();
 633   
 634  47 _writer.end();
 635    }
 636   
 637    /**
 638    * Writes out all hidden values previously added by
 639    * {@link #addHiddenValue(String, String, String)}, plus the allocated id list.
 640    */
 641   
 642  53 protected void writeHiddenFieldList()
 643    {
 644  53 writeHiddenField(FORM_IDS, null, buildAllocatedIdList());
 645   
 646  53 Iterator i = _hiddenValues.iterator();
 647  53 while (i.hasNext())
 648    {
 649  321 HiddenFieldData data = (HiddenFieldData) i.next();
 650   
 651  321 writeHiddenField(data.getName(), data.getId(), data.getValue());
 652    }
 653    }
 654   
 655  293 private void addHiddenFieldsForLinkParameter(ILink link, String parameterName)
 656    {
 657  293 String[] values = link.getParameterValues(parameterName);
 658   
 659    // In some cases, there are no values, but a space is "reserved" for the provided name.
 660   
 661  293 if (values == null)
 662  109 return;
 663   
 664  184 for (int i = 0; i < values.length; i++)
 665    {
 666  184 addHiddenValue(parameterName, values[i]);
 667    }
 668    }
 669   
 670  47 protected void writeTag(IMarkupWriter writer, String method, String url)
 671    {
 672  47 writer.begin("form");
 673  47 writer.attribute("method", method);
 674  47 writer.attribute("action", url);
 675    }
 676   
 677  0 public void prerenderField(IMarkupWriter writer, IComponent field, Location location)
 678    {
 679  0 Defense.notNull(writer, "writer");
 680  0 Defense.notNull(field, "field");
 681   
 682  0 String key = field.getExtendedId();
 683   
 684  0 if (_prerenderMap.containsKey(key))
 685  0 throw new ApplicationRuntimeException(FormMessages.fieldAlreadyPrerendered(field),
 686    location, null);
 687   
 688  0 NestedMarkupWriter nested = writer.getNestedWriter();
 689   
 690  0 field.render(nested, _cycle);
 691   
 692  0 _prerenderMap.put(key, nested.getBuffer());
 693    }
 694   
 695  75 public boolean wasPrerendered(IMarkupWriter writer, IComponent field)
 696    {
 697  75 String key = field.getExtendedId();
 698   
 699    // During a rewind, if the form is pre-rendered, the buffer will be null,
 700    // so do the check based on the key, not a non-null value.
 701   
 702  75 if (!_prerenderMap.containsKey(key))
 703  75 return false;
 704   
 705  0 String buffer = (String) _prerenderMap.get(key);
 706   
 707  0 writer.printRaw(buffer);
 708   
 709  0 _prerenderMap.remove(key);
 710   
 711  0 return true;
 712    }
 713   
 714  2 public void addDeferredRunnable(Runnable runnable)
 715    {
 716  2 Defense.notNull(runnable, "runnable");
 717   
 718  2 _deferredRunnables.add(runnable);
 719    }
 720   
 721  0 public void registerForFocus(IFormComponent field, int priority)
 722    {
 723  0 _delegate.registerForFocus(field, priority);
 724    }
 725   
 726    }