blob: f9b7ca7f85d57ea4401a6408b5f8186bd923d900 [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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.tapestry.engine.NullWriter;
import org.apache.tapestry.event.*;
import org.apache.tapestry.services.ResponseBuilder;
import org.apache.tapestry.util.StringSplitter;
import javax.swing.event.EventListenerList;
import java.util.EventListener;
import java.util.Locale;
/**
* Abstract base class implementing the {@link IPage}interface.
*
* @author Howard Lewis Ship, David Solis
* @since 0.2.9
*/
public abstract class AbstractPage extends BaseComponent implements IPage
{
private static final Log LOG = LogFactory.getLog(AbstractPage.class);
/**
* Object to be notified when a observered property changes. Observered properties are the ones
* that will be persisted between request cycles. Unobserved properties are reconstructed.
*/
private ChangeObserver _changeObserver;
/**
* The {@link IEngine}the page is currently attached to.
*/
private IEngine _engine;
/**
* The qualified name of the page, which may be prefixed by the namespace.
*
* @since 2.3
*/
private String _pageName;
/**
* Set when the page is attached to the engine.
*/
private IRequestCycle _requestCycle;
/**
* The locale of the page, initially determined from the {@link IEngine engine}.
*/
private Locale _locale;
/**
* A list of listeners for the page.
*
* @see PageBeginRenderListener
* @see PageEndRenderListener
* @see PageDetachListener
* @since 1.0.5
*/
private EventListenerList _listenerList;
/**
* The output encoding to be used when rendering this page. This value is cached from the
* engine.
*
* @since 3.0
*/
private String _outputEncoding;
/**
* Used to dynamically include script content automatically for form specific includes.
* @since 4.1.2
*/
private boolean _hasForms;
/**
* Dynamically causes widget dojo layer to be included if set to true.
* @since 4.1.2.
*/
private boolean _hasWidgets;
/**
* Standard constructor. Does nothing.
*
* @since 2.2
*/
public AbstractPage()
{
}
/**
* Prepares the page to be returned to the pool.
* <ul>
* <li>Clears the changeObserved property
* <li>Invokes {@link PageDetachListener#pageDetached(PageEvent)}on all listeners
* <li>Clears the engine and requestCycle properties
* </ul>
* <p>
* Subclasses may override this method, but must invoke this implementation (usually, last).
*
* @see PageDetachListener
*/
public void detach()
{
Tapestry.addMethodInvocation(Tapestry.ABSTRACTPAGE_DETACH_METHOD_ID);
// Do this first,so that any changes to persistent properties do not
// cause errors.
_changeObserver = null;
firePageDetached();
_engine = null;
_requestCycle = null;
}
public IEngine getEngine()
{
return _engine;
}
public ChangeObserver getChangeObserver()
{
return _changeObserver;
}
/**
* Returns the name of the page.
*/
public String getExtendedId()
{
return _pageName;
}
/**
* Pages always return null for idPath.
*/
public String getIdPath()
{
return null;
}
/**
* Returns the locale for the page, which may be null if the locale is not known (null
* corresponds to the "default locale").
*/
public Locale getLocale()
{
return _locale;
}
public void setLocale(Locale value)
{
if (_locale != null)
throw new ApplicationRuntimeException(Tapestry
.getMessage("AbstractPage.attempt-to-change-locale"));
_locale = value;
}
public IComponent getNestedComponent(String path)
{
StringSplitter splitter;
IComponent current;
String[] elements;
int i;
if (path == null)
return this;
splitter = new StringSplitter('.');
current = this;
elements = splitter.splitToArray(path);
for (i = 0; i < elements.length; i++)
{
current = current.getComponent(elements[i]);
}
return current;
}
/**
* Called by the {@link IEngine engine} to attach the page to itself. Does <em>not</em> change
* the locale, but since a page is selected from the
* {@link org.apache.tapestry.engine.IPageSource} pool based on its locale matching the engine's
* locale, they should match anyway.
*/
public void attach(IEngine engine, IRequestCycle cycle)
{
if (_engine != null)
LOG.error(this + " attach(" + engine + "), but engine = " + _engine);
_engine = engine;
_requestCycle = cycle;
}
/**
* Renders the page.
* <ul>
* <li>Invokes {@link PageBeginRenderListener#pageBeginRender(PageEvent)}
* <li>Invokes {@link #beginPageRender()}
* <li>Invokes {@link IRequestCycle#commitPageChanges()}(if not rewinding)
* <li>Invokes {@link #render(IMarkupWriter, IRequestCycle)}
* <li>Invokes {@link PageEndRenderListener#pageEndRender(PageEvent)}(this occurs even if a
* previous step throws an exception)
* </ul>
*/
public void renderPage(ResponseBuilder builder, IRequestCycle cycle)
{
try
{
firePageBeginRender();
if (!cycle.isRewinding())
cycle.commitPageChanges();
builder.render(cycle.isRewinding() ? NullWriter.getSharedInstance() : null, this, cycle);
}
finally
{
firePageEndRender();
}
}
public void setChangeObserver(ChangeObserver value)
{
_changeObserver = value;
}
/** @since 3.0 * */
public void setPageName(String pageName)
{
if (_pageName != null)
throw new ApplicationRuntimeException(Tapestry
.getMessage("AbstractPage.attempt-to-change-name"));
_pageName = pageName;
}
/**
* By default, pages are not protected and this method does nothing.
*/
public void validate(IRequestCycle cycle)
{
Tapestry.addMethodInvocation(Tapestry.ABSTRACTPAGE_VALIDATE_METHOD_ID);
firePageValidate();
}
public IRequestCycle getRequestCycle()
{
return _requestCycle;
}
public void addPageDetachListener(PageDetachListener listener)
{
addListener(PageDetachListener.class, listener);
}
private void addListener(Class listenerClass, EventListener listener)
{
if (_listenerList == null)
_listenerList = new EventListenerList();
_listenerList.add(listenerClass, listener);
}
/**
* @since 2.1-beta-2
*/
private void removeListener(Class listenerClass, EventListener listener)
{
if (_listenerList != null)
_listenerList.remove(listenerClass, listener);
}
/** @since 4.0 */
public void addPageBeginRenderListener(PageBeginRenderListener listener)
{
addListener(PageBeginRenderListener.class, listener);
}
/** @since 4.0 */
public void addPageEndRenderListener(PageEndRenderListener listener)
{
addListener(PageEndRenderListener.class, listener);
}
/** @since 4.0 */
public void removePageBeginRenderListener(PageBeginRenderListener listener)
{
removeListener(PageBeginRenderListener.class, listener);
}
/** @since 4.0 */
public void removePageEndRenderListener(PageEndRenderListener listener)
{
removeListener(PageEndRenderListener.class, listener);
}
/**
* @since 4.0
*/
public void firePageAttached()
{
if (_listenerList == null)
return;
PageEvent event = null;
Object[] listeners = _listenerList.getListenerList();
for(int i = listeners.length-2; i >= 0; i -= 2)
{
if (listeners[i] == PageAttachListener.class)
{
PageAttachListener l = (PageAttachListener) listeners[i + 1];
if (event == null)
event = new PageEvent(this, _requestCycle);
l.pageAttached(event);
}
}
}
/**
* @since 1.0.5
*/
protected void firePageDetached()
{
if (_listenerList == null)
return;
PageEvent event = null;
Object[] listeners = _listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2)
{
if (listeners[i] == PageDetachListener.class)
{
PageDetachListener l = (PageDetachListener) listeners[i + 1];
if (event == null)
event = new PageEvent(this, _requestCycle);
l.pageDetached(event);
}
}
}
/**
* @since 1.0.5
*/
protected void firePageBeginRender()
{
if (_listenerList == null)
return;
PageEvent event = null;
Object[] listeners = _listenerList.getListenerList();
for(int i = listeners.length-2; i >= 0; i -= 2)
{
if (listeners[i] == PageBeginRenderListener.class)
{
PageBeginRenderListener l = (PageBeginRenderListener)listeners[i + 1];
if (event == null)
event = new PageEvent(this, _requestCycle);
l.pageBeginRender(event);
}
}
}
/**
* @since 1.0.5
*/
protected void firePageEndRender()
{
if (_listenerList == null)
return;
PageEvent event = null;
Object[] listeners = _listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2)
{
if (listeners[i] == PageEndRenderListener.class)
{
PageEndRenderListener l = (PageEndRenderListener) listeners[i + 1];
if (event == null)
event = new PageEvent(this, _requestCycle);
l.pageEndRender(event);
}
}
}
/**
* @since 2.1-beta-2
*/
public void removePageDetachListener(PageDetachListener listener)
{
removeListener(PageDetachListener.class, listener);
}
/** @since 2.2 * */
public void beginPageRender()
{
firePageBeginRender();
}
/** @since 2.2 * */
public void endPageRender()
{
firePageEndRender();
}
protected void cleanupAfterRender(IRequestCycle cycle)
{
}
/** @since 3.0 * */
public String getPageName()
{
return _pageName;
}
public void addPageValidateListener(PageValidateListener listener)
{
addListener(PageValidateListener.class, listener);
}
public void removePageValidateListener(PageValidateListener listener)
{
removeListener(PageValidateListener.class, listener);
}
/** @since 4.0 */
public void addPageAttachListener(PageAttachListener listener)
{
addListener(PageAttachListener.class, listener);
}
/** @since 4.0 */
public void removePageAttachListener(PageAttachListener listener)
{
removeListener(PageAttachListener.class, listener);
}
protected void firePageValidate()
{
if (_listenerList == null)
return;
PageEvent event = null;
Object[] listeners = _listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2)
{
if (listeners[i] == PageValidateListener.class)
{
PageValidateListener l = (PageValidateListener) listeners[i + 1];
if (event == null)
event = new PageEvent(this, _requestCycle);
l.pageValidate(event);
}
}
}
/**
* Returns the output encoding to be used when rendering this page. This value is usually cached
* from the Engine.
*
* @since 3.0
*/
protected String getOutputEncoding()
{
if (_outputEncoding == null)
_outputEncoding = getEngine().getOutputEncoding();
return _outputEncoding;
}
public boolean hasFormComponents()
{
return _hasForms;
}
public void setHasFormComponents(boolean value)
{
_hasForms = value;
}
public boolean hasWidgets()
{
return _hasWidgets;
}
public void setHasWidgets(boolean value)
{
_hasWidgets = value;
}
}