Issue: 280-EZComp MethodExpression passing
SECTION: Modified Files
----------------------------
M jsf-api/src/javax/faces/webapp/pdl/PDLUtils.java
- Add new method retargetMethodExpressions()
Leverage the component metadata specified in section 4.3.2 for the
purpose of re-targeting any method expressions from the top level
component to the appropriate inner component. For each attribute that is
a MethodExpression (as indicated by the presence of a "method-signature"
attribute and the absence of a "type" attribute), the following action
must be taken:
Get the value of the applyTo attribute. If not found, log an error and
continue to the next attribute.
Find the inner component of the topLevelComponent with the id equal to
applyTo. For discussion, this component is called target. If not found,
log and error and continue to the next attribute.
For discussion the declared name of the attribute is called name.
In the attributes map of the topLevelComponent, look up the entry under
the key name. Assume the result is a ValueExpression. For discussion,
this is attributeValueExpression. If not found, log an error and
continue to the next attribute.
If name is equal to the string "action", or "actionListener" without the
quotes, assume target is an ActionSource2.
If name is equal to the string "validator", or "valueChangeListener"
without the quotes, assume target is an EditableValueHolder.
Call getExpressionString() on the attributeValueExpression and use that
string to create a MethodExpression of the appropriate signature for
name.
If name is not equal to any of the previously listed strings, call
getExpressionString() on the attributeValueExpression and use that
string to create a MethodExpression where the signature is created based
on the value of the "method-signature" attribute of the
<composite:attribute /> tag.
Let the resultant MethodExpression be called attributeMethodExpression
for discussion.
If name is equal to the string "action" without the quotes, call
ActionSource2.setActionExpression(javax.el.MethodExpression) on target,
passing attributeMethodExpression.
If name is equal to the string "actionListener" without the quotes, call
ActionSource.addActionListener(javax.faces.event.ActionListener) on
target, passing attributeMethodExpression wrapped in a
MethodExpressionActionListener.
If name is equal to the string "validator" without the quotes, call
EditableValueHolder.addValidator(javax.faces.validator.Validator) on
target, passing attributeMethodExpression wrapped in a
MethodExpressionValidator.
If name is equal to the string "valueChangeListener" without the quotes,
call
EditableValueHolder.addValueChangeListener(javax.faces.event.ValueChangeListener)
on target, passing attributeMethodExpression wrapped in a
MethodExpressionValueChangeListener.
Otherwise, look for a JavaBeans setter that matches name and assume it
takes a MethodExpression, passing attributeMethodExpression as that
expression. If such a setter does not exist, or does not take a
MethodExpression, log an error and continue to the next attribute.
M jsf-ri/src/com/sun/faces/facelets/tag/composite/AttributeHandler.java
- Add type, method-signature, and applyTo attributes
M jsf-ri/src/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java
- Call the new retargetMethodExpressions() method
M jsf-demo/ezcomp02/src/main/webapp/index.xhtml
M jsf-demo/ezcomp02/src/main/webapp/resources/ezcomp/loginPanel.xhtml
- Test new code
SECTION: Diffs
----------------------------
Index: jsf-api/src/javax/faces/webapp/pdl/PDLUtils.java
===================================================================
--- jsf-api/src/javax/faces/webapp/pdl/PDLUtils.java (revision 5481)
+++ jsf-api/src/javax/faces/webapp/pdl/PDLUtils.java (working copy)
-39,12 +39,21 @@
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
+import java.beans.PropertyDescriptor;
import java.util.List;
+import java.util.logging.Logger;
+import javax.el.ValueExpression;
+import javax.el.ExpressionFactory;
+import javax.el.MethodExpression;
import javax.faces.component.ActionSource2;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
-import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
+import javax.faces.event.ActionEvent;
+import javax.faces.event.MethodExpressionActionListener;
+import javax.faces.event.MethodExpressionValueChangeListener;
+import javax.faces.event.ValueChangeEvent;
+import javax.faces.validator.MethodExpressionValidator;
/**
* <p class="changed_added_2_0">It is very unlikely this class will be
-56,6 +65,9 @@
* @since 2.0
*/
public class PDLUtils {
+
+ private static final Logger LOGGER =
+ Logger.getLogger("javax.faces", "javax.faces.LogStrings");
/**
* <p class="changed_added_2_0">Leverage the component metadata
-150,7 +162,7 @@
*
* </div>
*
- * @param context
+ * @param context the FacesContext for this request.
* @param topLevelComponent The UIComponent in the view to which the
* attached objects must be attached. This UIComponent must have
-233,4 +245,283 @@
}
}
+ /**
+ * <p class="changed_added_2_0">Leverage the component metadata
+ * specified in section 4.3.2 for the purpose of re-targeting any
+ * method expressions from the top level component to the
+ * appropriate inner component. For each attribute that is a
+ * <code>MethodExpression</code> (as indicated by the presence of a
+ * "<code>method-signature</code>" attribute and the absence of a
+ * "<code>type</code>" attribute), the following action must be
+ * taken:</p>
+
+ * <div class="changed_added_2_0">
+ *
+ * <ul>
+
+ <li><p>Get the value of the <em>applyTo</em> attribute. If
+ not found, log an error and continue to the next
+ attribute.</p></li>
+
+ <li><p>Find the inner component of the
+ <em>topLevelComponent</em> with the id equal to
+ <em>applyTo</em>. For discussion, this component is called
+ <em>target</em>. If not found, log and error and continue to
+ the next attribute.</p></li>
+
+ <li><p>For discussion the declared name of the attribute is
+ called <em>name</em>.</p></li>
+
+ <li><p>In the attributes map of the
+ <em>topLevelComponent</em>, look up the entry under the key
+ <em>name</em>. Assume the result is a
+ <code>ValueExpression</code>. For discussion, this is
+ <em>attributeValueExpression</em>. If not found, log an error
+ and continue to the next attribute.</p></li>
+
+ <li><p>If <em>name</em> is equal to the string "action", or
+ "actionListener" without the quotes, assume <em>target</em> is
+ an {_at_link javax.faces.component.ActionSource2}.</p></li>
+
+ <li><p>If <em>name</em> is equal to the string "validator", or
+ "valueChangeListener" without the quotes, assume
+ <em>target</em> is an {_at_link
+ javax.faces.component.EditableValueHolder}.</p></li>
+
+ <li><p>Call <code>getExpressionString()</code> on the
+ <em>attributeValueExpression</em> and use that string to
+ create a <code>MethodExpression</code> of the appropriate
+ signature for <em>name</em>.</p></li>
+
+ <li><p>If <em>name</em> is not equal to any of the previously
+ listed strings, call <code>getExpressionString()</code> on the
+ <em>attributeValueExpression</em> and use that string to
+ create a <code>MethodExpression</code> where the signature is
+ created based on the value of the
+ "<code>method-signature</code>" attribute of the
+ <code><composite:attribute /></code> tag.</p></li>
+
+ <li><p>Let the resultant <code>MethodExpression</code> be
+ called <em>attributeMethodExpression</em> for discussion.
+ </p></li>
+
+ <li><p>If <em>name</em> is equal to the string "action"
+ without the quotes, call {_at_link
+ ActionSource2#setActionExpression} on <em>target</em>, passing
+ <em>attributeMethodExpression</em>.</p></li>
+
+ <li><p>If <em>name</em> is equal to the string
+ "actionListener" without the quotes, call {_at_link
+ javax.faces.component.ActionSource#addActionListener} on
+ <em>target</em>, passing <em>attributeMethodExpression</em>
+ wrapped in a {_at_link MethodExpressionActionListener}.</p></li>
+
+ <li><p>If <em>name</em> is equal to the string
+ "validator" without the quotes, call {_at_link
+ EditableValueHolder#addValidator} on <em>target</em>, passing
+ <em>attributeMethodExpression</em> wrapped in a {_at_link
+ MethodExpressionValidator}.</p></li>
+
+ <li><p>If <em>name</em> is equal to the string
+ "valueChangeListener" without the quotes, call {_at_link
+ EditableValueHolder#addValueChangeListener} on <em>target</em>, passing
+ <em>attributeMethodExpression</em> wrapped in a {_at_link
+ MethodExpressionValueChangeListener}.</p></li>
+
+ <li><p>Otherwise, look for a JavaBeans setter that matches
+ <em>name</em> and assume it takes a
+ <code>MethodExpression</code>, passing
+ <em>attributeMethodExpression</em> as that expression. If
+ such a setter does not exist, or does not take a
+ <code>MethodExpression</code>, log an error and continue to
+ the next attribute.</p></li>
+
+ </ul>
+
+ * </div>
+ *
+ * @param context the FacesContext for this request.
+
+ * @param topLevelComponent The UIComponent in the view to which the
+ * attached objects must be attached. This UIComponent must have
+ * its component metadata already associated and available from via
+ * the JavaBeans API.
+
+ * @since 2.0
+ */
+
+ public static void retargetMethodExpressions(FacesContext context,
+ UIComponent topLevelComponent) {
+ BeanInfo componentBeanInfo = (BeanInfo)
+ topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
+ // PENDING(edburns): log error message if componentBeanInfo is null;
+ if (null == componentBeanInfo) {
+ return;
+ }
+ PropertyDescriptor attributes[] = componentBeanInfo.getPropertyDescriptors();
+ String applyTo = null, strValue = null;
+ UIComponent target = null;
+ ExpressionFactory expressionFactory = null;
+ ValueExpression valueExpression = null;
+ MethodExpression toApply = null;
+ Class expectedReturnType = null;
+ Class expectedParameters[] = null;
+ boolean logError = false;
+
+ for (PropertyDescriptor cur : attributes){
+ // If the current attribute represents a ValueExpression
+ if (null != (valueExpression = (ValueExpression) cur.getValue("type"))) {
+ // take no action on this attribute.
+ continue;
+ }
+ // If the current attribute representes a MethodExpression
+ if (null != (valueExpression = (ValueExpression) cur.getValue("method-signature"))) {
+ strValue = (String) valueExpression.getValue(context.getELContext());
+ if (null != strValue) {
+
+ // This is the name of the attribute on the top level component,
+ // and on the inner component.
+ logError = false;
+ if (null != (valueExpression = (ValueExpression) cur.getValue("applyTo"))) {
+ applyTo = (String) valueExpression.getValue(context.getELContext());
+ if (null == applyTo) {
+ logError = true;
+ }
+ }
+
+ if (logError) {
+ // PENDING error message in page?
+ LOGGER.severe("Unable to retarget MethodExpression. " +
+ "Please specify \"applyTo\" attribute on <composite:attribute />");
+ continue;
+ }
+
+ // This is the inner component to which the attribute should
+ // be applied
+ target = topLevelComponent.findComponent(applyTo);
+ if (null == applyTo) {
+ // PENDING error message in page?
+ LOGGER.severe("Unable to retarget MethodExpression. " +
+ "Unable to find inner component with id " +
+ applyTo + ".");
+ continue;
+ }
+
+ strValue = cur.getName();
+
+ // Find the attribute on the top level component
+ valueExpression = (ValueExpression) topLevelComponent.getAttributes().
+ get(strValue);
+ if (null == valueExpression) {
+ // PENDING error message in page?
+ LOGGER.severe("Unable to find attribute with name \"" + strValue +
+ "\" in top level component in consuming page. " +
+ "Page author error.");
+ continue;
+ }
+
+ // lazily initialize this local variable
+ if (null == expressionFactory) {
+ expressionFactory = context.getApplication().getExpressionFactory();
+ }
+
+ // If the attribute is one of the pre-defined
+ // MethodExpression attributes
+ if (strValue.equals("action")) {
+ expectedReturnType = Object.class;
+ expectedParameters = new Class[]{};
+ toApply = expressionFactory.createMethodExpression(context.getELContext(),
+ valueExpression.getExpressionString(),
+ expectedReturnType, expectedParameters);
+ ((ActionSource2) target).setActionExpression(toApply);
+ } else if (strValue.equals("actionListener")) {
+ expectedReturnType = Void.TYPE;
+ expectedParameters = new Class[]{
+ ActionEvent.class
+ };
+ toApply = expressionFactory.createMethodExpression(context.getELContext(),
+ valueExpression.getExpressionString(),
+ expectedReturnType, expectedParameters);
+ ((ActionSource2) target).addActionListener(new MethodExpressionActionListener(toApply));
+ } else if (strValue.equals("validator")) {
+ expectedReturnType = Void.TYPE;
+ expectedParameters = new Class[]{
+ FacesContext.class,
+ UIComponent.class,
+ Object.class
+ };
+ toApply = expressionFactory.createMethodExpression(context.getELContext(),
+ valueExpression.getExpressionString(),
+ expectedReturnType, expectedParameters);
+ ((EditableValueHolder) target).addValidator(new MethodExpressionValidator(toApply));
+ } else if (strValue.equals("valueChangeListener")) {
+ expectedReturnType = Void.TYPE;
+ expectedParameters = new Class[]{
+ ValueChangeEvent.class
+ };
+ toApply = expressionFactory.createMethodExpression(context.getELContext(),
+ valueExpression.getExpressionString(),
+ expectedReturnType, expectedParameters);
+ ((EditableValueHolder) target).addValueChangeListener(new MethodExpressionValueChangeListener(toApply));
+ } else {
+ // If the attribute is not one of the pre-defined
+ // MethodExpression attributes, look it up reflectively,
+ // assuming there is a setter of type MethodExpression
+
+ }
+ }
+ }
+
+ }
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
}
Index: jsf-ri/src/com/sun/faces/facelets/tag/composite/AttributeHandler.java
===================================================================
--- jsf-ri/src/com/sun/faces/facelets/tag/composite/AttributeHandler.java (revision 5481)
+++ jsf-ri/src/com/sun/faces/facelets/tag/composite/AttributeHandler.java (working copy)
-154,7 +154,22 @@
ve = attr.getValueExpression(ctx, String.class);
propertyDescriptor.setValue("default", ve);
}
+ if (null != (attr = this.getAttribute("type"))) {
+ ve = attr.getValueExpression(ctx, String.class);
+ propertyDescriptor.setValue("type", ve);
+ } else {
+ if (null != (attr = this.getAttribute("method-signature"))) {
+ ve = attr.getValueExpression(ctx, String.class);
+ propertyDescriptor.setValue("method-signature", ve);
+ }
+ }
+ if (null != (attr = this.getAttribute("applyTo"))) {
+ ve = attr.getValueExpression(ctx, String.class);
+ propertyDescriptor.setValue("applyTo", ve);
+ }
+
+
}
}
Index: jsf-ri/src/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java
===================================================================
--- jsf-ri/src/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java (revision 5481)
+++ jsf-ri/src/com/sun/faces/facelets/tag/jsf/CompositeComponentTagHandler.java (working copy)
-146,8 +146,10 @@
applyCompositeComponent(ctx, c);
// Allow any PDL declared attached objects to be retargeted
if (ComponentSupport.isNew(c)) {
- PDLUtils.retargetAttachedObjects(ctx.getFacesContext(), c,
+ FacesContext context = ctx.getFacesContext();
+ PDLUtils.retargetAttachedObjects(context, c,
getAttachedObjectHandlers(c, false));
+ PDLUtils.retargetMethodExpressions(context, c);
}
}
Index: jsf-demo/ezcomp02/src/main/webapp/index.xhtml
===================================================================
--- jsf-demo/ezcomp02/src/main/webapp/index.xhtml (revision 5481)
+++ jsf-demo/ezcomp02/src/main/webapp/index.xhtml (working copy)
-62,7 +62,8 @@
<div id="compositeComponent" class="grayBox" style="border: 1px solid #090;">
<ez:loginPanel id="loginPanel" usernameLabel="Username" pinLabel="PIN"
- loginLabel="Login" model="#{bean}">
+ loginLabel="Login" model="#{bean}"
+ action="#{bean.loginAction}">
<f:valueChangeListener for="usernameInput"
binding="#{bean.useridValueChangeListener}" />
Index: jsf-demo/ezcomp02/src/main/webapp/resources/ezcomp/loginPanel.xhtml
===================================================================
--- jsf-demo/ezcomp02/src/main/webapp/resources/ezcomp/loginPanel.xhtml (revision 5481)
+++ jsf-demo/ezcomp02/src/main/webapp/resources/ezcomp/loginPanel.xhtml (working copy)
-59,7 +59,9 @@
<composite:attribute name="pinLabel" default="PIN" />
<composite:attribute name="loginLabel" default="Login" />
<composite:attribute name="model" required="true"/>
+ <composite:attribute name="action" method-signature="java.lang.Object action()" applyTo="loginEvent"/>
+
<composite:editableValueHolder name="usernameInput" />
<composite:editableValueHolder name="pinInput" />
<composite:editableValueHolder name="allInputs" targets="usernameInput,pinInput" />
-86,7 +88,6 @@
<ui:define name="loginEvent">
<h:commandButton id="loginEvent" value="#{compositeComponent.attrs.loginLabel}"
- action="#{compositeComponent.attrs.model.loginAction}"
onkeypress="javascript: submitenter(event, 'loginButton', Login');"
onclick="javascript: submitAndDisable(this, 'Login');"
onfocus="javascript: if (this.disabled==0) this.className='Btn1Hov'"
-95,9 +96,10 @@
onmouseover="javascript: if (this.disabled==0) this.className='Btn1Hov'"
tabindex="3"
title="Log In to Sun Java System Application Server"
- class="Btn1"
- />
+ class="Btn1">
+ </h:commandButton>
+
</ui:define>
</ui:decorate>
--
| ed.burns_at_sun.com | office: 408 884 9519 OR x31640
| homepage: | http://ridingthecrest.com/