Coverage Report - org.apache.tapestry.form.FormSupportImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
FormSupportImpl
0%
0/276
0%
0/102
2.941
 
 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 org.apache.hivemind.ApplicationRuntimeException;
 18  
 import org.apache.hivemind.HiveMind;
 19  
 import org.apache.hivemind.Location;
 20  
 import org.apache.hivemind.util.Defense;
 21  
 import org.apache.tapestry.*;
 22  
 import org.apache.tapestry.engine.ILink;
 23  
 import org.apache.tapestry.event.BrowserEvent;
 24  
 import org.apache.tapestry.javascript.JavascriptManager;
 25  
 import org.apache.tapestry.json.JSONObject;
 26  
 import org.apache.tapestry.services.DataSqueezer;
 27  
 import org.apache.tapestry.services.ResponseBuilder;
 28  
 import org.apache.tapestry.services.ServiceConstants;
 29  
 import org.apache.tapestry.valid.IValidationDelegate;
 30  
 
 31  
 import java.util.*;
 32  
 
 33  
 /**
 34  
  * Encapsulates most of the behavior of a Form component.
 35  
  *
 36  
  */
 37  
 public class FormSupportImpl implements FormSupport
 38  
 {
 39  
     /**
 40  
      * Name of query parameter storing the ids alloocated while rendering the form, as a comma
 41  
      * seperated list. This information is used when the form is submitted, to ensure that the
 42  
      * rewind allocates the exact same sequence of ids.
 43  
      */
 44  
 
 45  
     public static final String FORM_IDS = "formids";
 46  
 
 47  
     /**
 48  
      * Names of additional ids that were pre-reserved, as a comma-sepereated list. These are names
 49  
      * beyond that standard set. Certain engine services include extra parameter values that must be
 50  
      * accounted for, and page properties may be encoded as additional query parameters.
 51  
      */
 52  
 
 53  
     public static final String RESERVED_FORM_IDS = "reservedids";
 54  
 
 55  
     /**
 56  
      * {@link DataSqueezer} squeezed list of {@link IRequestCycle} id allocation state as it was just before this
 57  
      * form was rendered.  Is used to ensure that all generated form ids are globally unique and consistent
 58  
      * between requests.
 59  
      */
 60  
     public static final String SEED_IDS = "seedids";
 61  
 
 62  
     /**
 63  
      * Indicates why the form was submitted: whether for normal ("submit"), refresh, or because the
 64  
      * form was canceled.
 65  
      */
 66  
 
 67  
     public static final String SUBMIT_MODE = "submitmode";
 68  
 
 69  
     /**
 70  
      * Attribute set to true when a field has been focused; used to prevent conflicting JavaScript
 71  
      * for field focusing from being emitted.
 72  
      */
 73  
 
 74  
     public static final String FIELD_FOCUS_ATTRIBUTE = "org.apache.tapestry.field-focused";
 75  
 
 76  
     private static final Set _standardReservedIds;
 77  
 
 78  
     static
 79  
     {
 80  0
         Set set = new HashSet();
 81  
 
 82  0
         set.addAll(Arrays.asList(ServiceConstants.RESERVED_IDS));
 83  0
         set.add(FORM_IDS);
 84  0
         set.add(RESERVED_FORM_IDS);
 85  0
         set.add(SEED_IDS);
 86  0
         set.add(SUBMIT_MODE);
 87  0
         set.add(FormConstants.SUBMIT_NAME_PARAMETER);
 88  
 
 89  0
         _standardReservedIds = Collections.unmodifiableSet(set);
 90  
     }
 91  
 
 92  
     private static final Set _submitModes;
 93  
 
 94  
     static
 95  
     {
 96  0
         Set set = new HashSet();
 97  0
         set.add(FormConstants.SUBMIT_CANCEL);
 98  0
         set.add(FormConstants.SUBMIT_NORMAL);
 99  0
         set.add(FormConstants.SUBMIT_REFRESH);
 100  
 
 101  0
         _submitModes = Collections.unmodifiableSet(set);
 102  0
     }
 103  
 
 104  
     protected final IRequestCycle _cycle;
 105  
 
 106  
     /**
 107  
      * Used when rewinding the form to figure to match allocated ids (allocated during the rewind)
 108  
      * against expected ids (allocated in the previous request cycle, when the form was rendered).
 109  
      */
 110  
 
 111  
     private int _allocatedIdIndex;
 112  
 
 113  
     /**
 114  
      * The list of allocated ids for form elements within this form. This list is constructed when a
 115  
      * form renders, and is validated against when the form is rewound.
 116  
      */
 117  
 
 118  0
     private final List _allocatedIds = new ArrayList();
 119  
 
 120  
     private String _encodingType;
 121  
 
 122  0
     private final List _deferredRunnables = new ArrayList();
 123  
 
 124  
     /**
 125  
      * Map keyed on extended component id, value is the pre-rendered markup for that component.
 126  
      */
 127  
 
 128  0
     private final Map _prerenderMap = new HashMap();
 129  
 
 130  
     /**
 131  
      * {@link Map}, keyed on {@link FormEventType}. Values are either a String (the function name
 132  
      * of a single event handler), or a List of Strings (a sequence of event handler function
 133  
      * names).
 134  
      */
 135  
 
 136  
     private Map _events;
 137  
 
 138  
     private final IForm _form;
 139  
 
 140  0
     private final List _hiddenValues = new ArrayList();
 141  
 
 142  
     private final boolean _rewinding;
 143  
 
 144  
     private final IMarkupWriter _writer;
 145  
 
 146  
     private final IValidationDelegate _delegate;
 147  
 
 148  
     private final PageRenderSupport _pageRenderSupport;
 149  
 
 150  
     /**
 151  
      * Client side validation is built up using a json object syntax structure
 152  
      */
 153  
     private final JSONObject _profile;
 154  
 
 155  
     /**
 156  
      * Used to detect whether or not a form component has been updated and will require form sync on ajax requests
 157  
      */
 158  
     private boolean _fieldUpdating;
 159  
 
 160  
     private JavascriptManager _javascriptManager;
 161  
 
 162  
     private String _idSeed;
 163  
 
 164  
     public FormSupportImpl(IMarkupWriter writer, IRequestCycle cycle, IForm form)
 165  
     {
 166  0
         this(writer, cycle, form, null);
 167  0
     }
 168  
 
 169  
     public FormSupportImpl(IMarkupWriter writer, IRequestCycle cycle,
 170  
                            IForm form, JavascriptManager javascriptManager)
 171  0
     {
 172  0
         Defense.notNull(writer, "writer");
 173  0
         Defense.notNull(cycle, "cycle");
 174  0
         Defense.notNull(form, "form");
 175  
 
 176  0
         _writer = writer;
 177  0
         _cycle = cycle;
 178  0
         _form = form;
 179  0
         _delegate = form.getDelegate();
 180  
 
 181  0
         _rewinding = cycle.isRewound(form);
 182  0
         _allocatedIdIndex = 0;
 183  
 
 184  0
         _pageRenderSupport = TapestryUtils.getOptionalPageRenderSupport(cycle);
 185  0
         _profile = new JSONObject();
 186  0
         _javascriptManager = javascriptManager;
 187  0
     }
 188  
 
 189  
     /**
 190  
      * Alternate constructor used for testing only.
 191  
      *
 192  
      * @param cycle
 193  
      *          The current cycle.
 194  
      */
 195  
     FormSupportImpl(IRequestCycle cycle)
 196  0
     {
 197  0
         _cycle = cycle;
 198  0
         _form = null;
 199  0
         _rewinding = false;
 200  0
         _writer = null;
 201  0
         _delegate = null;
 202  0
         _pageRenderSupport = null;
 203  0
         _profile = null;
 204  0
     }
 205  
 
 206  
     /**
 207  
      * {@inheritDoc}
 208  
      */
 209  
     public IForm getForm()
 210  
     {
 211  0
         return _form;
 212  
     }
 213  
 
 214  
     /**
 215  
      * {@inheritDoc}
 216  
      */
 217  
     public void addEventHandler(FormEventType type, String functionName)
 218  
     {
 219  0
         if (_events == null)
 220  0
             _events = new HashMap();
 221  
 
 222  0
         List functionList = (List) _events.get(type);
 223  
 
 224  
         // The value can either be a String, or a List of String. Since
 225  
         // it is rare for there to be more than one event handling function,
 226  
         // we start with just a String.
 227  
 
 228  0
         if (functionList == null)
 229  
         {
 230  0
             functionList = new ArrayList();
 231  
 
 232  0
             _events.put(type, functionList);
 233  
         }
 234  
 
 235  0
         functionList.add(functionName);
 236  0
     }
 237  
 
 238  
     /**
 239  
      * Adds hidden fields for parameters provided by the {@link ILink}. These parameters define the
 240  
      * information needed to dispatch the request, plus state information. The names of these
 241  
      * parameters must be reserved so that conflicts don't occur that could disrupt the request
 242  
      * processing. For example, if the id 'page' is not reserved, then a conflict could occur with a
 243  
      * component whose id is 'page'. A certain number of ids are always reserved, and we find any
 244  
      * additional ids beyond that set.
 245  
      */
 246  
 
 247  
     private void addHiddenFieldsForLinkParameters(ILink link)
 248  
     {
 249  0
         String[] names = link.getParameterNames();
 250  0
         int count = Tapestry.size(names);
 251  
 
 252  0
         StringBuffer extraIds = new StringBuffer();
 253  0
         String sep = "";
 254  0
         boolean hasExtra = false;
 255  
 
 256  0
         for (int i = 0; i < count; i++)
 257  
         {
 258  0
             String name = names[i];
 259  
 
 260  
             // Reserve the name.
 261  
 
 262  0
             if (!_standardReservedIds.contains(name))
 263  
             {
 264  0
                 _cycle.getUniqueId(name);
 265  
 
 266  0
                 extraIds.append(sep);
 267  0
                 extraIds.append(name);
 268  
 
 269  0
                 sep = ",";
 270  0
                 hasExtra = true;
 271  
             }
 272  
 
 273  0
             addHiddenFieldsForLinkParameter(link, name);
 274  
         }
 275  
 
 276  0
         if (hasExtra)
 277  0
             addHiddenValue(RESERVED_FORM_IDS, extraIds.toString());
 278  0
     }
 279  
 
 280  
     public void addHiddenValue(String name, String value)
 281  
     {
 282  0
         _hiddenValues.add(new HiddenFieldData(name, value));
 283  0
     }
 284  
 
 285  
     public void addHiddenValue(String name, String id, String value)
 286  
     {
 287  0
         _hiddenValues.add(new HiddenFieldData(name, id, value));
 288  0
     }
 289  
 
 290  
     /**
 291  
      * Converts the allocateIds property into a string, a comma-separated list of ids. This is
 292  
      * included as a hidden field in the form and is used to identify discrepencies when the form is
 293  
      * submitted.
 294  
      */
 295  
 
 296  
     private String buildAllocatedIdList()
 297  
     {
 298  0
         StringBuffer buffer = new StringBuffer();
 299  0
         int count = _allocatedIds.size();
 300  
 
 301  0
         for (int i = 0; i < count; i++)
 302  
         {
 303  0
             if (i > 0)
 304  0
                 buffer.append(',');
 305  
 
 306  0
             buffer.append(_allocatedIds.get(i));
 307  
         }
 308  
 
 309  0
         return buffer.toString();
 310  
     }
 311  
 
 312  
     private void emitEventHandlers(String formId)
 313  
     {
 314  0
         if (_events == null || _events.isEmpty())
 315  0
             return;
 316  
 
 317  0
         StringBuffer buffer = new StringBuffer();
 318  
 
 319  0
         Iterator i = _events.entrySet().iterator();
 320  
 
 321  0
         while (i.hasNext())
 322  
         {
 323  0
             Map.Entry entry = (Map.Entry) i.next();
 324  0
             FormEventType type = (FormEventType) entry.getKey();
 325  0
             Object value = entry.getValue();
 326  
 
 327  0
             buffer.append("Tapestry.");
 328  0
             buffer.append(type.getAddHandlerFunctionName());
 329  0
             buffer.append("('");
 330  0
             buffer.append(formId);
 331  0
             buffer.append("', function (event)\n{");
 332  
 
 333  0
             List l = (List) value;
 334  0
             int count = l.size();
 335  
 
 336  0
             for (int j = 0; j < count; j++)
 337  
             {
 338  0
                 String functionName = (String) l.get(j);
 339  
 
 340  0
                 if (j > 0)
 341  
                 {
 342  0
                     buffer.append(";");
 343  
                 }
 344  
 
 345  0
                 buffer.append("\n  ");
 346  0
                 buffer.append(functionName);
 347  
 
 348  
                 // It's supposed to be function names, but some of Paul's validation code
 349  
                 // adds inline code to be executed instead.
 350  
 
 351  0
                 if (!functionName.endsWith(")"))
 352  
                 {
 353  0
                     buffer.append("()");
 354  
                 }
 355  
             }
 356  
 
 357  0
             buffer.append(";\n});\n");
 358  0
         }
 359  
 
 360  
         // TODO: If PRS is null ...
 361  
 
 362  0
         _pageRenderSupport.addInitializationScript(_form, buffer.toString());
 363  0
     }
 364  
 
 365  
     /**
 366  
      * Constructs a unique identifier (within the Form). The identifier consists of the component's
 367  
      * id, with an index number added to ensure uniqueness.
 368  
      * <p>
 369  
      * Simply invokes
 370  
      * {@link #getElementId(org.apache.tapestry.form.IFormComponent, java.lang.String)}with the
 371  
      * component's id.
 372  
      */
 373  
 
 374  
     public String getElementId(IFormComponent component)
 375  
     {
 376  0
         return getElementId(component, component.getSpecifiedId());
 377  
     }
 378  
 
 379  
     /**
 380  
      * Constructs a unique identifier (within the Form). The identifier consists of the component's
 381  
      * id, with an index number added to ensure uniqueness.
 382  
      * <p>
 383  
      * Simply invokes
 384  
      * {@link #getElementId(org.apache.tapestry.form.IFormComponent, java.lang.String)}with the
 385  
      * component's id.
 386  
      */
 387  
 
 388  
     public String getElementId(IFormComponent component, String baseId)
 389  
     {
 390  
         // $ is not a valid character in an XML/XHTML id, so convert it to an underscore.
 391  
 
 392  0
         String filteredId = TapestryUtils.convertTapestryIdToNMToken(baseId);
 393  
 
 394  0
         String result = _cycle.getUniqueId(filteredId);
 395  
 
 396  0
         if (_rewinding)
 397  
         {
 398  0
             if (_allocatedIdIndex >= _allocatedIds.size())
 399  
             {
 400  0
                 throw new StaleLinkException(FormMessages.formTooManyIds(_form, _allocatedIds.size(), component), component);
 401  
             }
 402  
 
 403  0
             String expected = (String) _allocatedIds.get(_allocatedIdIndex);
 404  
 
 405  0
             if (!result.equals(expected))
 406  0
                 throw new StaleLinkException(FormMessages.formIdMismatch(
 407  
                         _form,
 408  
                         _allocatedIdIndex,
 409  
                         expected,
 410  
                         result,
 411  
                         component), component);
 412  0
         }
 413  
         else
 414  
         {
 415  0
             _allocatedIds.add(result);
 416  
         }
 417  
 
 418  0
         _allocatedIdIndex++;
 419  
 
 420  0
         component.setName(result);
 421  0
         component.setClientId(result);
 422  
 
 423  0
         return result;
 424  
     }
 425  
 
 426  
     public String peekClientId(IFormComponent comp)
 427  
     {
 428  0
         String id = comp.getSpecifiedId();
 429  
 
 430  0
         if (id == null)
 431  0
             return null;
 432  
 
 433  0
         if (wasPrerendered(comp))
 434  0
             return comp.getClientId();
 435  
 
 436  0
         return _cycle.peekUniqueId(id);
 437  
     }
 438  
 
 439  
     public boolean isRewinding()
 440  
     {
 441  0
         return _rewinding;
 442  
     }
 443  
     
 444  
     /**
 445  
      * Invoked when rewinding a form to re-initialize the _allocatedIds and _elementIdAllocator.
 446  
      * Converts a string passed as a parameter (and containing a comma separated list of ids) back
 447  
      * into the allocateIds property. In addition, return the state of the ID allocater back to
 448  
      * where it was at the start of the render.
 449  
      *
 450  
      * @see #buildAllocatedIdList()
 451  
      * @since 3.0
 452  
      */
 453  
 
 454  
     private void reinitializeIdAllocatorForRewind()
 455  
     {
 456  0
         _cycle.initializeIdState(_cycle.getParameter(SEED_IDS));
 457  
 
 458  0
         String allocatedFormIds = _cycle.getParameter(FORM_IDS);
 459  
 
 460  0
         String[] ids = TapestryUtils.split(allocatedFormIds);
 461  
 
 462  0
         for (int i = 0; i < ids.length; i++)
 463  0
             _allocatedIds.add(ids[i]);
 464  
 
 465  
         // Now, reconstruct the initial state of the
 466  
         // id allocator.
 467  
 
 468  0
         String extraReservedIds = _cycle.getParameter(RESERVED_FORM_IDS);
 469  
 
 470  0
         ids = TapestryUtils.split(extraReservedIds);
 471  
 
 472  0
         for (int i = 0; i < ids.length; i++)
 473  
         {
 474  0
             _cycle.getUniqueId(ids[i]);
 475  
         }
 476  0
     }
 477  
 
 478  
     public void render(String method, IRender informalParametersRenderer, ILink link, String scheme, Integer port)
 479  
     {
 480  0
         String formId = _form.getName();
 481  
 
 482  0
         _idSeed = _cycle.encodeIdState();
 483  
 
 484  0
         emitEventManagerInitialization(formId);
 485  
 
 486  
         // Convert the link's query parameters into a series of
 487  
         // hidden field values (that will be rendered later).
 488  
 
 489  0
         addHiddenFieldsForLinkParameters(link);
 490  
 
 491  
         // Create a hidden field to store the submission mode, in case
 492  
         // client-side JavaScript forces an update.
 493  
 
 494  0
         addHiddenValue(SUBMIT_MODE, null);
 495  
 
 496  
         // And another for the name of the component that
 497  
         // triggered the submit.
 498  
 
 499  0
         addHiddenValue(FormConstants.SUBMIT_NAME_PARAMETER, null);
 500  
 
 501  0
         IMarkupWriter nested = _writer.getNestedWriter();
 502  
 
 503  0
         _form.renderBody(nested, _cycle);
 504  
 
 505  0
         runDeferredRunnables();
 506  
 
 507  0
         int portI = (port == null) ? 0 : port.intValue();
 508  
 
 509  0
         writeTag(_writer, method, link.getURL(scheme, null, portI, null, false));
 510  
 
 511  
         // For XHTML compatibility
 512  
 
 513  0
         _writer.attribute("id", formId);
 514  
 
 515  0
         if (_encodingType != null)
 516  0
             _writer.attribute("enctype", _encodingType);
 517  
 
 518  
         // Write out event handlers collected during the rendering.
 519  
 
 520  0
         emitEventHandlers(formId);
 521  
 
 522  0
         informalParametersRenderer.render(_writer, _cycle);
 523  
 
 524  
         // Finish the <form> tag
 525  
 
 526  0
         _writer.println();
 527  
 
 528  0
         writeHiddenFields();
 529  
 
 530  
         // Close the nested writer, inserting its contents.
 531  
 
 532  0
         nested.close();
 533  
 
 534  
         // Close the <form> tag.
 535  
 
 536  0
         _writer.end();
 537  
 
 538  0
         String fieldId = _delegate.getFocusField();
 539  
 
 540  0
         if (_pageRenderSupport == null)
 541  0
             return;
 542  
 
 543  0
         _pageRenderSupport.addInitializationScript(_form, "dojo.require(\"tapestry.form\");");
 544  
 
 545  
         // If the form doesn't support focus, or the focus has already been set by a different form,
 546  
         // then do nothing.
 547  
 
 548  0
         if (!_cycle.isFocusDisabled() && fieldId != null && _form.getFocus()
 549  
             && _cycle.getAttribute(FIELD_FOCUS_ATTRIBUTE) == null)
 550  
         {
 551  
             // needs to happen last to avoid dialog issues in ie - TAPESTRY-1705
 552  
 
 553  0
             _pageRenderSupport.addScriptAfterInitialization(_form, "tapestry.form.focusField('" + fieldId + "');");
 554  
 
 555  0
             _cycle.setAttribute(FIELD_FOCUS_ATTRIBUTE, Boolean.TRUE);
 556  
         }
 557  
 
 558  
         // register the validation profile with client side form manager
 559  
 
 560  0
         if (_form.isClientValidationEnabled())
 561  
         {
 562  0
             IPage page = _form.getPage();
 563  
 
 564  
             // only include dojo widget layer if it's not already been included
 565  
 
 566  0
             if (!page.hasWidgets())
 567  
             {
 568  0
                 if (_javascriptManager != null && _javascriptManager.getFirstWidgetAsset() != null)
 569  
                 {
 570  0
                     _pageRenderSupport.addExternalScript(_form,
 571  
                                                          _javascriptManager.getFirstWidgetAsset().getResourceLocation());
 572  
                 }
 573  
             }
 574  
 
 575  0
             _pageRenderSupport.addInitializationScript(_form, "tapestry.form.clearProfiles('"
 576  
                                                               + formId + "'); tapestry.form.registerProfile('" + formId + "',"
 577  
                                                               + _profile.toString() + ");");
 578  
         }
 579  0
     }
 580  
 
 581  
     /**
 582  
      * Pre-renders the form, setting up some client-side form support. Returns the name of the
 583  
      * client-side form event manager variable.
 584  
      *
 585  
      * @param formId
 586  
      *          The client id of the form.
 587  
      */
 588  
     protected void emitEventManagerInitialization(String formId)
 589  
     {
 590  0
         if (_pageRenderSupport == null)
 591  0
             return;
 592  
 
 593  0
         StringBuffer str = new StringBuffer("dojo.require(\"tapestry.form\");");
 594  0
         str.append("tapestry.form.registerForm(\"").append(formId).append("\"");
 595  
 
 596  0
         if (_form.isAsync())
 597  
         {
 598  0
             str.append(", true");
 599  
 
 600  0
             if (_form.isJson())
 601  
             {
 602  0
                 str.append(", true");
 603  
             }
 604  
         }
 605  
 
 606  0
         str.append(");");
 607  
 
 608  0
         _pageRenderSupport.addInitializationScript(_form, str.toString());
 609  0
     }
 610  
 
 611  
     public String rewind()
 612  
     {
 613  0
         _form.getDelegate().clear();
 614  
 
 615  0
         String mode = _cycle.getParameter(SUBMIT_MODE);
 616  
 
 617  
         // On a cancel, don't bother rendering the body or anything else at all.
 618  
 
 619  0
         if (FormConstants.SUBMIT_CANCEL.equals(mode))
 620  0
             return mode;
 621  
 
 622  0
         reinitializeIdAllocatorForRewind();
 623  
 
 624  0
         _form.renderBody(_writer, _cycle);
 625  
 
 626  
         // New, handles cases where an eventlistener
 627  
         // causes a form submission.
 628  
 
 629  0
         BrowserEvent event = new BrowserEvent(_cycle);
 630  
 
 631  0
         _form.getEventInvoker().invokeFormListeners(this, _cycle, event);
 632  
 
 633  0
         int expected = _allocatedIds.size();
 634  
 
 635  
         // The other case, _allocatedIdIndex > expected, is
 636  
         // checked for inside getElementId(). Remember that
 637  
         // _allocatedIdIndex is incremented after allocating.
 638  
 
 639  0
         if (_allocatedIdIndex < expected)
 640  
         {
 641  0
             String nextExpectedId = (String) _allocatedIds.get(_allocatedIdIndex);
 642  
 
 643  0
             throw new StaleLinkException(FormMessages.formTooFewIds(_form, expected - _allocatedIdIndex, nextExpectedId), _form);
 644  
         }
 645  
 
 646  0
         runDeferredRunnables();
 647  
         
 648  0
         if (_submitModes.contains(mode))
 649  
         {
 650  
             // clear errors during refresh
 651  
 
 652  0
             if (FormConstants.SUBMIT_REFRESH.equals(mode))
 653  
             {
 654  0
                 _form.getDelegate().clearErrors();
 655  
             }
 656  
 
 657  0
             return mode;
 658  
         }
 659  
 
 660  
         // Either something wacky on the client side, or a client without
 661  
         // javascript enabled.
 662  
 
 663  0
         return FormConstants.SUBMIT_NORMAL;
 664  
     }
 665  
 
 666  
     private void runDeferredRunnables()
 667  
     {
 668  0
         Iterator i = _deferredRunnables.iterator();
 669  0
         while (i.hasNext())
 670  
         {
 671  0
             Runnable r = (Runnable) i.next();
 672  
 
 673  0
             r.run();
 674  0
         }
 675  0
     }
 676  
 
 677  
     public void setEncodingType(String encodingType)
 678  
     {
 679  
 
 680  0
         if (_encodingType != null && !_encodingType.equals(encodingType))
 681  0
             throw new ApplicationRuntimeException(FormMessages.encodingTypeContention(_form, _encodingType, encodingType),
 682  
                                                   _form, null, null);
 683  
 
 684  0
         _encodingType = encodingType;
 685  0
     }
 686  
 
 687  
     /**
 688  
      * Overwridden by {@link org.apache.tapestry.wml.GoFormSupportImpl} (WML).
 689  
      */
 690  
     protected void writeHiddenField(IMarkupWriter writer, String name, String id, String value)
 691  
     {
 692  0
         writer.beginEmpty("input");
 693  0
         writer.attribute("type", "hidden");
 694  0
         writer.attribute("name", name);
 695  
 
 696  0
         if (HiveMind.isNonBlank(id))
 697  0
             writer.attribute("id", id);
 698  
 
 699  0
         writer.attribute("value", value == null ? "" : value);
 700  0
         writer.println();
 701  0
     }
 702  
 
 703  
     /**
 704  
      * Writes out all hidden values previously added by
 705  
      * {@link #addHiddenValue(String, String, String)}. Writes a &lt;div&gt; tag around
 706  
      * {@link #writeHiddenFieldList(IMarkupWriter)}. Overriden by
 707  
      * {@link org.apache.tapestry.wml.GoFormSupportImpl}.
 708  
      */
 709  
 
 710  
     protected void writeHiddenFields()
 711  
     {
 712  0
         IMarkupWriter writer = getHiddenFieldWriter();
 713  
 
 714  0
         writer.begin("div");
 715  0
         writer.attribute("style", "display:none;");
 716  0
         writer.attribute("id", _form.getName() + "hidden");
 717  
 
 718  0
         writeHiddenFieldList(writer);
 719  
 
 720  0
         writer.end();
 721  0
     }
 722  
 
 723  
     /**
 724  
      * Writes out all hidden values previously added by
 725  
      * {@link #addHiddenValue(String, String, String)}, plus the allocated id list.
 726  
      */
 727  
 
 728  
     protected void writeHiddenFieldList(IMarkupWriter writer)
 729  
     {
 730  0
         writeHiddenField(writer, FORM_IDS, null, buildAllocatedIdList());
 731  0
         writeHiddenField(writer, SEED_IDS, null, _idSeed);
 732  
 
 733  0
         Iterator i = _hiddenValues.iterator();
 734  0
         while (i.hasNext())
 735  
         {
 736  0
             HiddenFieldData data = (HiddenFieldData) i.next();
 737  
 
 738  0
             writeHiddenField(writer, data.getName(), data.getId(), data.getValue());
 739  0
         }
 740  0
     }
 741  
 
 742  
     /**
 743  
      * Determines if a hidden field change has occurred, which would require
 744  
      * that we write hidden form fields using the {@link ResponseBuilder} 
 745  
      * writer.
 746  
      *
 747  
      * @return The default {@link IMarkupWriter} if not doing a managed ajax/json
 748  
      *          response, else whatever is returned from {@link ResponseBuilder}.
 749  
      */
 750  
     protected IMarkupWriter getHiddenFieldWriter()
 751  
     {
 752  0
         if (_cycle.getResponseBuilder().contains(_form)
 753  
             || (!_fieldUpdating || !_cycle.getResponseBuilder().isDynamic()) )
 754  
         {
 755  0
             return _writer;
 756  
         }
 757  
 
 758  0
         return _cycle.getResponseBuilder().getWriter(_form.getName() + "hidden", ResponseBuilder.ELEMENT_TYPE);
 759  
     }
 760  
 
 761  
     private void addHiddenFieldsForLinkParameter(ILink link, String parameterName)
 762  
     {
 763  0
         String[] values = link.getParameterValues(parameterName);
 764  
 
 765  
         // In some cases, there are no values, but a space is "reserved" for the provided name.
 766  
 
 767  0
         if (values == null)
 768  0
             return;
 769  
 
 770  0
         for (int i = 0; i < values.length; i++)
 771  
         {
 772  0
             addHiddenValue(parameterName, values[i]);
 773  
         }
 774  0
     }
 775  
 
 776  
     protected void writeTag(IMarkupWriter writer, String method, String url)
 777  
     {
 778  0
         writer.begin("form");
 779  0
         writer.attribute("method", method);
 780  0
         writer.attribute("action", url);
 781  0
     }
 782  
 
 783  
     public void prerenderField(IMarkupWriter writer, IComponent field, Location location)
 784  
     {
 785  0
         Defense.notNull(writer, "writer");
 786  0
         Defense.notNull(field, "field");
 787  
 
 788  0
         String key = field.getExtendedId();
 789  
 
 790  0
         if (_prerenderMap.containsKey(key))
 791  0
             throw new ApplicationRuntimeException(FormMessages.fieldAlreadyPrerendered(field), field, location, null);
 792  
 
 793  0
         NestedMarkupWriter nested = writer.getNestedWriter();
 794  
 
 795  0
         TapestryUtils.storePrerender(_cycle, field);
 796  
 
 797  0
         _cycle.getResponseBuilder().render(nested, field, _cycle);
 798  
 
 799  0
         TapestryUtils.removePrerender(_cycle);
 800  
 
 801  0
         _prerenderMap.put(key, nested.getBuffer());
 802  0
     }
 803  
 
 804  
     public boolean wasPrerendered(IMarkupWriter writer, IComponent field)
 805  
     {
 806  0
         String key = field.getExtendedId();
 807  
 
 808  
         // During a rewind, if the form is pre-rendered, the buffer will be null,
 809  
         // so do the check based on the key, not a non-null value.
 810  
 
 811  0
         if (!_prerenderMap.containsKey(key))
 812  0
             return false;
 813  
 
 814  0
         String buffer = (String) _prerenderMap.get(key);
 815  
 
 816  0
         writer.printRaw(buffer);
 817  
 
 818  0
         _prerenderMap.remove(key);
 819  
 
 820  0
         return true;
 821  
     }
 822  
 
 823  
     public boolean wasPrerendered(IComponent field)
 824  
     {
 825  0
         return _prerenderMap.containsKey(field.getExtendedId());
 826  
     }
 827  
 
 828  
     public void addDeferredRunnable(Runnable runnable)
 829  
     {
 830  0
         Defense.notNull(runnable, "runnable");
 831  
 
 832  0
         _deferredRunnables.add(runnable);
 833  0
     }
 834  
 
 835  
     public void registerForFocus(IFormComponent field, int priority)
 836  
     {
 837  0
         _delegate.registerForFocus(field, priority);
 838  0
     }
 839  
 
 840  
     /**
 841  
      * {@inheritDoc}
 842  
      */
 843  
     public JSONObject getProfile()
 844  
     {
 845  0
         return _profile;
 846  
     }
 847  
 
 848  
     /**
 849  
      * {@inheritDoc}
 850  
      */
 851  
     public boolean isFormFieldUpdating()
 852  
     {
 853  0
         return _fieldUpdating;
 854  
     }
 855  
 
 856  
     /**
 857  
      * {@inheritDoc}
 858  
      */
 859  
     public void setFormFieldUpdating(boolean value)
 860  
     {
 861  0
         _fieldUpdating = value;
 862  0
     }
 863  
 }