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