Author: gvanmatre Date: Wed Apr 5 19:46:34 2006 New Revision: 391884 URL: http://svn.apache.org/viewcvs?rev=391884&view=rev Log: Fix for bugs 37932, 39171 and 39156 reported by Mark Shifman and Paul Devine.
Added: struts/shale/trunk/core-library/src/java/org/apache/shale/faces/ValidatorRenderKit.java (with props) struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java (with props) struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorInputRenderer.java (with props) Modified: struts/shale/trunk/core-library/src/conf/validator-rules.xml struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java struts/shale/trunk/core-library/src/java/org/apache/shale/view/faces/ViewViewHandler.java Modified: struts/shale/trunk/core-library/src/conf/validator-rules.xml URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/conf/validator-rules.xml?rev=391884&r1=391883&r2=391884&view=diff ============================================================================== --- struts/shale/trunk/core-library/src/conf/validator-rules.xml (original) +++ struts/shale/trunk/core-library/src/conf/validator-rules.xml Wed Apr 5 19:46:34 2006 @@ -419,7 +419,7 @@ <validator name="float" classname="org.apache.commons.validator.GenericValidator" method="isDouble" - methodParams="double" + methodParams="java.lang.String" depends="" msg="errors.float" jsFunctionName="FloatValidations"> Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java?rev=391884&r1=391883&r2=391884&view=diff ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java (original) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/component/ValidatorScript.java Wed Apr 5 19:46:34 2006 @@ -21,6 +21,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.StringTokenizer; import javax.faces.component.EditableValueHolder; @@ -31,6 +32,7 @@ import javax.faces.el.ValueBinding; import org.apache.commons.validator.ValidatorAction; +import org.apache.shale.renderer.ValidatorInputRenderer; import org.apache.shale.validator.CommonsValidator; /** @@ -147,11 +149,11 @@ /** * <p>Recursively finds all Commons validators for the all of the - * components in a component hierarchy and adds them to a map. + * components in a component hierarchy and adds them to a map.</p> * <p>If a validator's type is required, this method sets the * associated component's required property to true. This is * necessary because JSF does not validate empty fields unless - * a component's required property is true. + * a component's required property is true.</p> * * @param c The component at the root of the component tree * @param context The FacesContext for this request @@ -164,16 +166,39 @@ if (vs[i] instanceof CommonsValidator) { CommonsValidator v = (CommonsValidator) vs[i]; if (Boolean.TRUE.equals(v.getClient())) { - String id = c.getClientId(context); - addValidator(v.getType(), id, v); - - ValidatorAction action = v.getValidatorAction(); - List list = action.getDependencyList(); - Iterator iter = list.iterator(); - while (iter.hasNext()) { - String type = (String) iter.next(); - addValidator(type, id, v); - } + + //look for the clientId set + Set clientIds = (Set) c.getAttributes().get(ValidatorInputRenderer.VALIDATOR_CLIENTIDS_ATTR); + if (clientIds != null) { + Iterator ci = clientIds.iterator(); + while (ci.hasNext()) { + String id = (String) ci.next(); + addValidator(v.getType(), id, v); + + ValidatorAction action = v.getValidatorAction(); + List list = action.getDependencyList(); + Iterator iter = list.iterator(); + while (iter.hasNext()) { + String type = (String) iter.next(); + addValidator(type, id, v); + } + + } + + } else { + //otherwise just try using the client id + String id = c.getClientId(context); + addValidator(v.getType(), id, v); + + ValidatorAction action = v.getValidatorAction(); + List list = action.getDependencyList(); + Iterator iter = list.iterator(); + while (iter.hasNext()) { + String type = (String) iter.next(); + addValidator(type, id, v); + } + } + } if (Boolean.TRUE.equals(v.getServer())) { // Fields with empty values are not validated, so Added: struts/shale/trunk/core-library/src/java/org/apache/shale/faces/ValidatorRenderKit.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/faces/ValidatorRenderKit.java?rev=391884&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/faces/ValidatorRenderKit.java (added) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/faces/ValidatorRenderKit.java Wed Apr 5 19:46:34 2006 @@ -0,0 +1,107 @@ +/* + * Copyright 2006 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.shale.faces; + +import java.io.OutputStream; +import java.io.Writer; + +import javax.faces.context.ResponseStream; +import javax.faces.context.ResponseWriter; +import javax.faces.render.RenderKit; +import javax.faces.render.Renderer; +import javax.faces.render.ResponseStateManager; + +import org.apache.shale.renderer.ValidatorCommandRenderer; +import org.apache.shale.renderer.ValidatorInputRenderer; + +/** + * <p>Decorates the original <code>RenderKit</code> passed by the + * overloaded constructor in the [EMAIL PROTECTED] org.apache.shale.view.faces.ViewViewHandler}. + * The majority of the implementation is passed on the the original + * <code>RenderKit</code> but requests for renderers registered with the + * "javax.faces.Command" and "javax.faces.Input" families are decorated. + * The wrapper adds special behavior for the + * [EMAIL PROTECTED] org.apache.shale.validator.CommonsValidator} validator and + * [EMAIL PROTECTED] org.apache.shale.component.ValidatorScript} component.</p> + */ +public class ValidatorRenderKit extends RenderKit { + + private static String COMMAND_FAMILY = "javax.faces.Command"; + private static String INPUT_FAMILY = "javax.faces.Input"; + + + /** + * <p>The original RenderKit.</p> + */ + private RenderKit defaultRenderKit = null; + /** + * <p>This constructor is overloaded to pass the original + * <code>RenderKit</p>. + */ + public ValidatorRenderKit(RenderKit defaultRenderKit) { + this.defaultRenderKit = defaultRenderKit; + } + + // Specified by default RenderKit + public void addRenderer(String componentFamily, String rendererType, Renderer renderer) { + this.defaultRenderKit.addRenderer(componentFamily, rendererType, renderer); + } + + /** + * <p>If the component family is not "javax.faces.Command" or + * "javax.faces.Input", the <code>defaultRenderKit</code> handles the + * request. If the family is "javax.faces.Command", the default + * renderer is decorated with [EMAIL PROTECTED] org.apache.shale.renderer.ValidatorCommandRenderer}. + * If the component family is "javax.faces.Input", the default + * renderer is decorated with [EMAIL PROTECTED] org.apache.shale.renderer.ValidatorInputRenderer}. + */ + public Renderer getRenderer(String componentFamily, String rendererType) { + Renderer target = defaultRenderKit.getRenderer(componentFamily, rendererType); + if (componentFamily.equals(COMMAND_FAMILY)) { + if (!(target instanceof ValidatorCommandRenderer)) { + target = new ValidatorCommandRenderer(target); + addRenderer(componentFamily, rendererType, target); + } + } else if (componentFamily.equals(INPUT_FAMILY)) { + if (!(target instanceof ValidatorInputRenderer)) { + target = new ValidatorInputRenderer(target); + addRenderer(componentFamily, rendererType, target); + } + } + + return target; + } + + // Specified by default RenderKit + public ResponseStateManager getResponseStateManager() { + return defaultRenderKit.getResponseStateManager(); + } + + // Specified by default RenderKit + public ResponseWriter createResponseWriter(Writer writer, + String contentTypeList, + String characterEncoding){ + return defaultRenderKit.createResponseWriter(writer, contentTypeList, characterEncoding); + } + + // Specified by default RenderKit + public ResponseStream createResponseStream(OutputStream outputStream) { + return defaultRenderKit.createResponseStream(outputStream); + } + +} Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/faces/ValidatorRenderKit.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/faces/ValidatorRenderKit.java ------------------------------------------------------------------------------ svn:keywords = date author id rev Added: struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java?rev=391884&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java (added) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java Wed Apr 5 19:46:34 2006 @@ -0,0 +1,181 @@ +/* + * Copyright 2006 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.shale.renderer; + +import java.io.IOException; +import java.io.StringWriter; + +import javax.faces.component.UICommand; +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseWriter; +import javax.faces.convert.ConverterException; +import javax.faces.render.Renderer; + +/** + * This renderer is a hybrid renderer decorator that is dynamically + * registered by the [EMAIL PROTECTED] org.apache.shale.faces.ValidatorRenderKit} + * for component renderers in the "javax.faces.Command" family.</p> + */ +public class ValidatorCommandRenderer extends Renderer { + + /** + * <p>The Original Renderer.</p> + */ + private Renderer defaultRenderer = null; + + /** + * <p>The overloaded constructor is passed the original + * <code>Renderer</code> for the family and component type.</p> + */ + public ValidatorCommandRenderer(Renderer defaultRenderer) { + this.defaultRenderer = defaultRenderer; + } + + + /** + * <p>Attribute name used to override the default behavior of how the immediate + * attribute effects the execution of client side javascript validation.</p> + */ + public static final String OVERRIDE_IMMEDIATE = "org.apache.shale.validator.immediate"; + + private static final int ENCODE_BEGIN = 0; + + private static final int ENCODE_CHILDREN = 1; + + private static final int ENCODE_END = 2; + + /** + * <b>Interrogates the component's immediate property and the component's + * immediate override attribute to determine if client side validation is + * invoked. If either the property or attribute override is false, client + * side validation is invoked. Otherwise, the response writer is hijacked + * and the original render is invoked. The result is buffered and a + * statement of javascript is injected into the onclick event which cancels + * client side validation. The original response writer is restored and the + * modified markup is written to the response writer. The + * <code>encodeSwitch</code> determines if the encodeBegin, encodeChildren + * or encodeEnd methods should be invoked on the decorated renderer.</b> + */ + protected void encode(FacesContext context, UIComponent component, + int encodeSwitch) throws IOException { + + UICommand command = (UICommand) component; + + // look for a override to the default + boolean immediateOverride = true; + String attr = (String) component.getAttributes() + .get(OVERRIDE_IMMEDIATE); + if (attr != null) { + immediateOverride = Boolean.valueOf(attr).booleanValue(); + } + + if (command.isImmediate() && immediateOverride) { + + ResponseWriter hijackedWriter = context.getResponseWriter(); + // builds a buffer to write the page to + StringWriter writer = new StringWriter(); + // create a buffered response writer + ResponseWriter buffResponsewriter = context.getRenderKit() + .createResponseWriter(writer, null, + hijackedWriter.getCharacterEncoding()); + // push buffered writer to the faces context + context.setResponseWriter(buffResponsewriter); + + if (encodeSwitch == ENCODE_BEGIN) + defaultRenderer.encodeBegin(context, component); + else if (encodeSwitch == ENCODE_CHILDREN) + defaultRenderer.encodeChildren(context, component); + else + defaultRenderer.encodeEnd(context, component); + + buffResponsewriter.write(' '); + buffResponsewriter.flush(); + buffResponsewriter.close(); + writer.flush(); + writer.close(); + StringBuffer buff = writer.getBuffer(); + int i = buff.indexOf("onclick=\""); + if (i > 0) { + buff.insert(i + "onclick=\"".length(), "bCancel=true;"); + } + + hijackedWriter.write(buff.toString()); + context.setResponseWriter(hijackedWriter); + + } else { + if (encodeSwitch == ENCODE_BEGIN) + defaultRenderer.encodeBegin(context, component); + else if (encodeSwitch == ENCODE_CHILDREN) + defaultRenderer.encodeChildren(context, component); + else + defaultRenderer.encodeEnd(context, component); + } + } + + // Specified by original Renderer + public String convertClientId(FacesContext context, String id) { + return defaultRenderer.convertClientId(context, id); + } + + + // Specified by original Renderer + public Object getConvertedValue(FacesContext context, UIComponent component, Object o) throws ConverterException { + return defaultRenderer.getConvertedValue(context, component, o); + } + + // Specified by original Renderer + public void decode(FacesContext context, UIComponent component) { + defaultRenderer.decode(context, component); + } + + /** + * <p> + * Invokes the <code>encode</code> method passing + * <code>ENCODE_BEGIN</code> for the encodeSwitch parameter. + * </p> + */ + public void encodeBegin(FacesContext context, UIComponent component) + throws IOException { + encode(context, component, ENCODE_BEGIN); + } + + /** + * <p>Invokes the <code>encode</code> method passing + * <code>ENCODE_CHILDREN</code> for the encodeSwitch parameter.</p> + */ + public void encodeChildren(FacesContext context, UIComponent component) + throws IOException { + encode(context, component, ENCODE_CHILDREN); + } + + /** + * <p>Invokes the <code>encode</code> method passing <code>ENCODE_END</code> + * for the encodeSwitch parameter.</p> + */ + public void encodeEnd(FacesContext context, UIComponent component) + throws IOException { + encode(context, component, ENCODE_END); + } + + // Specified by original Renderer + public boolean getRendersChildren() { + return defaultRenderer.getRendersChildren(); + } + +} Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorCommandRenderer.java ------------------------------------------------------------------------------ svn:keywords = date author id rev Added: struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorInputRenderer.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorInputRenderer.java?rev=391884&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorInputRenderer.java (added) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorInputRenderer.java Wed Apr 5 19:46:34 2006 @@ -0,0 +1,105 @@ +/* + * Copyright 2006 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.shale.renderer; + +import java.io.IOException; +import java.util.Set; +import java.util.TreeSet; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.convert.ConverterException; +import javax.faces.render.Renderer; + +/** + * This renderer is a hybrid renderer decorator that is dynamically + * registered by the [EMAIL PROTECTED] org.apache.shale.faces.ValidatorRenderKit} + * for component renderers in the "javax.faces.Input" family.</p> + */ +public class ValidatorInputRenderer extends Renderer { + + private Renderer defaultRenderer = null; + + /** + * <p>This constant is the name of a reserved attribute that will hold + * a <code>Set</code> of clientId's for the component.</p> + */ + public static final String VALIDATOR_CLIENTIDS_ATTR = "org.apache.shale.validator.clientIdSet"; + + /** + * <p>Overloaded constructor is passed the original + * <code>Renderer</code>.</p> + */ + public ValidatorInputRenderer(Renderer defaultRenderer) { + this.defaultRenderer = defaultRenderer; + } + + // Specified by original Renderer + public String convertClientId(FacesContext context, String id) { + return defaultRenderer.convertClientId(context, id); + } + + // Specified by original Renderer + public void decode(FacesContext context, UIComponent component) { + defaultRenderer.decode(context, component); + } + + + /** + * <p>This override captures the clientId of the target component before + * passing on to the original renderer. The clientId is added to a Set + * that is used by the [EMAIL PROTECTED] org.apache.shale.component.ValidatorScript} + * component for adding client side JavaScript validation. This hook is + * needed when the [EMAIL PROTECTED] org.apache.shale.validator.CommonsValidator} + * is added to a UIData subclass. The components in this class are not + * unique per row so the clientId can only be captured during the rendering + * process.</p> + */ + public void encodeBegin(FacesContext context, UIComponent component) throws IOException { + + Set ids = (Set) component.getAttributes().get(VALIDATOR_CLIENTIDS_ATTR); + if (ids == null) { + ids = new TreeSet(); + component.getAttributes().put(VALIDATOR_CLIENTIDS_ATTR, ids); + } + + ids.add(component.getClientId(context)); + defaultRenderer.encodeBegin(context, component); + } + + // Specified by original Renderer + public void encodeChildren(FacesContext context, UIComponent component) throws IOException { + defaultRenderer.encodeChildren(context, component); + } + + // Specified by original Renderer + public void encodeEnd(FacesContext context, UIComponent component) throws IOException { + defaultRenderer.encodeEnd(context, component); + } + + // Specified by original Renderer + public Object getConvertedValue(FacesContext context, UIComponent component, Object value) throws ConverterException { + return defaultRenderer.getConvertedValue(context, component, value); + } + + // Specified by original Renderer + public boolean getRendersChildren() { + return defaultRenderer.getRendersChildren(); + } + +} Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorInputRenderer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/renderer/ValidatorInputRenderer.java ------------------------------------------------------------------------------ svn:keywords = date author id rev Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/view/faces/ViewViewHandler.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/view/faces/ViewViewHandler.java?rev=391884&r1=391883&r2=391884&view=diff ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/view/faces/ViewViewHandler.java (original) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/view/faces/ViewViewHandler.java Wed Apr 5 19:46:34 2006 @@ -23,15 +23,18 @@ import java.util.Map; import javax.faces.FacesException; +import javax.faces.FactoryFinder; import javax.faces.application.ViewHandler; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; import javax.faces.el.EvaluationException; -import javax.faces.el.ValueBinding; import javax.faces.el.VariableResolver; +import javax.faces.render.RenderKit; +import javax.faces.render.RenderKitFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.shale.faces.ValidatorRenderKit; import org.apache.shale.util.Messages; import org.apache.shale.view.Constants; import org.apache.shale.view.ViewController; @@ -124,6 +127,7 @@ public UIViewRoot createView(FacesContext context, String viewId) { UIViewRoot view = original.createView(context, viewId); setupViewController(context, view, viewId, false); + setupRenderKit(context, view); return view; } @@ -300,5 +304,18 @@ } + /** + * <p>The default RenderKit is decorated with [EMAIL PROTECTED] org.apache.shale.faces.ValidatorRenderKit}. + * This wrapper intercepts component renderer's decorating them.</p> + * + * @param context <code>FacesContext</code> for the current request + * @param view <code>UIViewRoot</code> for the current component tree + */ + private void setupRenderKit(FacesContext context, UIViewRoot view) { + RenderKitFactory factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); + RenderKit defaultRenderKit = factory.getRenderKit(context, view.getRenderKitId()); + if (!(defaultRenderKit instanceof ValidatorRenderKit)) + factory.addRenderKit(view.getRenderKitId(), new ValidatorRenderKit(defaultRenderKit)); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]