blob: 338df2777344dce69aa685c63f8faa3ce10e93cb [file] [log] [blame]
// Copyright 2004, 2005 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry.form;
import org.apache.tapestry.*;
import org.apache.tapestry.engine.NullWriter;
import org.apache.tapestry.valid.IValidationDelegate;
import org.apache.tapestry.valid.ValidationConstants;
/**
* A base class for building components that correspond to HTML form elements. All such components
* must be wrapped (directly or indirectly) by a {@link Form} component.
*
* @author Howard Lewis Ship
* @author Paul Ferraro
* @since 1.0.3
*/
public abstract class AbstractFormComponent extends AbstractComponent implements IFormComponent
{
public abstract IForm getForm();
public abstract void setForm(IForm form);
public abstract String getName();
public abstract void setName(String name);
/**
* Returns true if the corresponding field, on the client side, can accept user focus (i.e.,
* implements the focus() method). Most components can take focus (if not disabled), but a few ({@link Hidden})
* override this method to always return false.
*/
protected boolean getCanTakeFocus()
{
return !isDisabled();
}
/**
* Should be connected to a parameter named "id" (annotations would be helpful here!). For
* components w/o such a parameter, this will simply return null.
*/
public abstract String getIdParameter();
/**
* Invoked by {@link AbstractComponent#render(IMarkupWriter, IRequestCycle)} to actually
* render the component (with any parameter values already set).
* This implementation checks the rewinding state of the {@link IForm} that contains the
* component and forwards processing to either
* {@link #renderFormComponent(IMarkupWriter, IRequestCycle)} or
* {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)}.
* Those two are the methods that subclasses should implement.
*
* @see org.apache.tapestry.AbstractComponent#renderComponent(org.apache.tapestry.IMarkupWriter,
* org.apache.tapestry.IRequestCycle)
*/
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
{
IForm form = TapestryUtils.getForm(cycle, this);
setForm(form);
if (form.wasPrerendered(writer, this))
return;
IValidationDelegate delegate = form.getDelegate();
delegate.setFormComponent(this);
setName(form);
if (form.isRewinding())
{
if (!isDisabled())
{
rewindFormComponent(writer, cycle);
}
// This is for the benefit of the couple of components (LinkSubmit) that allow a body.
// The body should render when the component rewinds.
if (getRenderBodyOnRewind())
renderBody(writer, cycle);
}
else if (!cycle.isRewinding())
{
if (!NullWriter.class.isInstance(writer))
form.setFormFieldUpdating(true);
renderFormComponent(writer, cycle);
if (getCanTakeFocus() && !isDisabled())
{
delegate.registerForFocus(this,
delegate.isInError()
? ValidationConstants.ERROR_FIELD
: ValidationConstants.NORMAL_FIELD);
}
}
}
/**
* A small number of components should always render their body on rewind (even if the component
* is itself disabled) and should override this method to return true. Components that
* explicitly render their body inside
* {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} should leave this method returning
* false. Remember that if the component is {@link IFormComponent#isDisabled() disabled} then
* {@link #rewindFormComponent(IMarkupWriter, IRequestCycle)} won't be invoked.
*
* @return false; override this method to change.
*/
protected boolean getRenderBodyOnRewind()
{
return false;
}
protected void renderDelegatePrefix(IMarkupWriter writer, IRequestCycle cycle)
{
getForm().getDelegate().writePrefix(writer, cycle, this, null);
}
protected void renderDelegateAttributes(IMarkupWriter writer, IRequestCycle cycle)
{
getForm().getDelegate().writeAttributes(writer, cycle, this, null);
}
protected void renderDelegateSuffix(IMarkupWriter writer, IRequestCycle cycle)
{
getForm().getDelegate().writeSuffix(writer, cycle, this, null);
}
protected void setName(IForm form)
{
setName(form.getElementId(this));
}
/**
* {@inheritDoc}
*/
protected void generateClientId()
{
}
/**
* {@inheritDoc}
*/
public String peekClientId()
{
if (getPage() == null)
return null;
IForm form = (IForm) getPage().getRequestCycle().getAttribute(TapestryUtils.FORM_ATTRIBUTE);
if (form == null)
return null;
return form.peekClientId(this);
}
/**
* Returns false. Subclasses that might be required must override this method. Typically, this
* involves checking against the component's validators.
*
* @since 4.0
*/
public boolean isRequired()
{
return false;
}
/**
* Invoked from {@link #renderComponent(IMarkupWriter, IRequestCycle)}
* to render the component.
*
* @param writer
* @param cycle
*/
protected abstract void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle);
/**
* Invoked from {@link #renderComponent(IMarkupWriter, IRequestCycle)} to rewind the
* component. If the component is {@link IFormComponent#isDisabled() disabled}
* this will not be invoked.
*
* @param writer
* @param cycle
*/
protected abstract void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle);
}