/*
* JBoss, Home of Professional Open Source
* Copyright ${year}, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.event;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.MethodExpression;
import javax.el.MethodNotFoundException;
import javax.faces.component.StateHolder;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;
/**
* <p>
* <strong><span class="changed_modified_2_0">MethodExpressionEventListener</span></strong> is a {@link FacesListener} that
* wraps a {@link MethodExpression}. When it receives a {@link FacesEvent}, it executes a method on an object identified by the
* {@link MethodExpression}.
* </p>
*
* @author akolonitsky
* @version 1.0
*
*/
public abstract class MethodExpressionEventListener implements FacesListener, StateHolder {
private static final Class<?>[] EVENT_LISTENER_ZERO_ARG_SIG = new Class[] {};
private static final Object[] NO_PARAMS = new Object[0];
// ------------------------------------------------------ Instance Variables
private MethodExpression methodExpressionOneArg = null;
private MethodExpression methodExpressionZeroArg = null;
private boolean isTransient;
protected MethodExpressionEventListener() {
}
/**
* <p>
* <span class="changed_modified_2_0">Construct</span> a {@link FacesListener} that contains a {@link MethodExpression}
* .<span class="changed_added_2_0">To accomodate method expression targets that take no arguments instead of taking a
* {@link FacesEvent} argument</span>, the implementation of this class must take the argument
* <code>methodExpressionOneArg</code>, extract its expression string, and create another <code>MethodExpression</code>
* whose expected param types match those of a zero argument method. The usage requirements for both of these
* <code>MethodExpression</code> instances are described in {@link #processEvent}.</span>
* </p>
*
* @param methodExpressionOneArg a <code>MethodExpression</code> that points to a method that returns <code>void</code> and
* takes a single argument of type {@link FacesEvent}.
*/
protected MethodExpressionEventListener(MethodExpression methodExpressionOneArg) {
super();
this.methodExpressionOneArg = methodExpressionOneArg;
FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();
this.methodExpressionZeroArg = context
.getApplication()
.getExpressionFactory()
.createMethodExpression(elContext, methodExpressionOneArg.getExpressionString(), Void.class,
EVENT_LISTENER_ZERO_ARG_SIG);
}
/**
* <p>
* Construct a {@link FacesListener} that contains a {@link MethodExpression}.
* </p>
*
* @param methodExprOneArg
* @param methodExprZeroArg
*/
protected MethodExpressionEventListener(MethodExpression methodExprOneArg, MethodExpression methodExprZeroArg) {
super();
this.methodExpressionOneArg = methodExprOneArg;
this.methodExpressionZeroArg = methodExprZeroArg;
}
// ------------------------------------------------------- Event Method
/**
* <p>
* <span class="changed_modified_2_0">Call</span> through to the {@link MethodExpression} passed in our constructor. <span
* class="changed_added_2_0">First, try to invoke the <code>MethodExpression</code> passed to the constructor of this
* instance, passing the argument {@link FacesEvent} as the argument. If a {@link MethodNotFoundException} is thrown, call
* to the zero argument <code>MethodExpression</code> derived from the <code>MethodExpression</code> passed to the
* constructor of this instance. If that fails for any reason, throw an {@link AbortProcessingException}, including the
* cause of the failure.</span>
* </p>
*
* @throws NullPointerException {@inheritDoc}
* @throws AbortProcessingException {@inheritDoc}
*/
public void processEvent(FacesEvent event) throws AbortProcessingException {
if (event == null) {
throw new NullPointerException();
}
FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();
// PENDING: The corresponding code in MethodExpressionActionListener
// has an elaborate message capture, logging, and rethrowing block.
// Why not here?
try {
methodExpressionOneArg.invoke(elContext, new Object[] { event });
} catch (MethodNotFoundException mnf) {
if (null != methodExpressionZeroArg) {
try {
// try to invoke a no-arg version
methodExpressionZeroArg.invoke(elContext, NO_PARAMS);
} catch (ELException e) {
throw new AbortProcessingException(e.getMessage(), e.getCause());
}
}
} catch (ELException e) {
throw new AbortProcessingException(e.getMessage(), e.getCause());
}
}
// ------------------------------------------------ Methods from StateHolder
/**
* <p class="changed_modified_2_0">
* Both {@link MethodExpression} instances described in the constructor must be saved.
* </p>
*/
public Object saveState(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
return new Object[] { UIComponentBase.saveAttachedState(context, methodExpressionOneArg),
UIComponentBase.saveAttachedState(context, methodExpressionZeroArg) };
}
/**
* <p class="changed_modified_2_0">
* Both {@link MethodExpression} instances described in the constructor must be restored.
* </p>
*/
public void restoreState(FacesContext context, Object state) {
if (context == null) {
throw new NullPointerException();
}
if (state == null) {
return;
}
methodExpressionOneArg = (MethodExpression) UIComponentBase.restoreAttachedState(context, ((Object[]) state)[0]);
methodExpressionZeroArg = (MethodExpression) UIComponentBase.restoreAttachedState(context, ((Object[]) state)[1]);
}
public boolean isTransient() {
return isTransient;
}
public void setTransient(boolean newTransientValue) {
isTransient = newTransientValue;
}
}