Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Won't Fix
    • Affects Version/s: 1.2
    • Fix Version/s: 1.2
    • Component/s: Uncategorized
    • Labels:
      None
    • Environment:

      Operating System: All
      Platform: All

    • Issuezilla Id:
      12
    • Status Whiteboard:
      Hide

      enact

      Show
      enact

      Description

      Our implementation to set up hidden fields in the form introduces
      tightly coupled renderer dependencies. For the original cause of
      this implementation see bugster report 5055795.

      For example.. We expose public static methods on FormRenderer that
      are used in CommandLinkRenderer as well as HiddenFieldRenderer.
      (ex: FormRenderer.addNeededHiddenField....)
      Anyone now using our CommandLinkRenderer must also use our FormRenderer..

      For more information on this see:
      https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=26

      The EG has outlined some possible solutions - but whatever we do must be spec'd.

        Activity

        Hide
        Ed Burns added a comment -

        up to p2

        Show
        Ed Burns added a comment - up to p2
        Hide
        Ed Burns added a comment -

        Craig McClanahan has a parallel implementation in struts faces:

        Craig wrote:

        FWIW, Struts-Faces had to create its own CommandLinkRenderer because the
        RI one was tied to the RI's FormRenderer (and Struts needed its own).
        Not only does it work with multiple forms, but it also decouples the two
        renderer implementations, and will work inside anyone's form renderer.
        You might want to consider doing something like that – and get rid of
        the unfortunate language requiring linkage (in
        standard-html-renderkit.html) in JSF 1.2.

        Craig

        /*

        • Copyright 2002-2004 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.struts.faces.renderer;

        import java.io.IOException;
        import java.util.Iterator;

        import javax.faces.component.NamingContainer;
        import javax.faces.component.UICommand;
        import javax.faces.component.UIComponent;
        import javax.faces.component.UIForm;
        import javax.faces.component.UIParameter;
        import javax.faces.context.FacesContext;
        import javax.faces.context.ResponseWriter;
        import javax.faces.event.ActionEvent;

        import org.apache.commons.logging.Log;
        import org.apache.commons.logging.LogFactory;
        import org.apache.struts.Globals;
        import org.apache.struts.config.ActionConfig;
        import org.apache.struts.config.ModuleConfig;

        /**

        • <p><code>Renderer</code> implementation for the <code>commandLink</code>
        • tag from the <em>Struts-Faces Integration Library</em>.</p>
          *
        • @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
          */

        public class CommandLinkRenderer extends AbstractRenderer {

        // -------------------------------------------------------- Static Variables

        /**

        • <p>Token for private names.</p>
          */
          private static final String TOKEN =
          "org_apache_struts_faces_renderer_CommandLinkRenderer";

        /**

        • <p>The <code>Log</code> instance for this class.</p>
          */
          private static Log log = LogFactory.getLog(CommandLinkRenderer.class);

        // ---------------------------------------------------------- Public Methods

        /**

        • <p>Perform setup processing that will be required for decoding the
        • incoming request.</p>
          *
        • @param context FacesContext for the request we are processing
        • @param component UIComponent to be processed
          *
        • @exception NullPointerException if <code>context</code>
        • or <code>component</code> is null
          */
          public void decode(FacesContext context, UIComponent component) {

        // Implement spec requirements on NullPointerException
        if ((context == null) || (component == null))

        { throw new NullPointerException(); }

        // Skip this component if it is not relevant
        if (!component.isRendered() || isDisabled(component) ||
        isReadOnly(component)) { return; }

        // Set up variables we will need
        UIForm form = null;
        UIComponent parent = component.getParent();
        while (parent != null) {
        if (parent instanceof UIForm) { form = (UIForm) parent; break; }
        parent = parent.getParent();
        }
        if (form == null) { log.warn("CommandLinkComponent not nested inside UIForm, ignored"); return; }
        String formClientId = form.getClientId(context);

        // Was this the component that submitted this form?
        String paramId = TOKEN;
        String value = (String)
        context.getExternalContext().getRequestParameterMap().get(paramId);
        if ((value == null) || !value.equals(component.getClientId(context))) {
        if (log.isTraceEnabled()) { log.trace("decode(" + component.getId() + ") --> not active"); }
        return;
        }

        // Queue an ActionEvent from this component
        if (log.isTraceEnabled()) { log.trace("decode(" + component.getId() + ") --> queueEvent()"); }
        component.queueEvent(new ActionEvent(component));

        }


        private static String passThrough[] =
        { "accesskey", "charset", "dir", "hreflang", "lang", "onblur", /* "onclick", */ "ondblclick", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "rel", "rev", "style", "tabindex", "target", "title", "type" };


        /**
        * <p>Render the beginning of a hyperlink to submit this form.</p>
        *
        * @param context FacesContext for the request we are processing
        * @param component UIComponent to be rendered
        * @param writer ResponseWriter we are rendering to
        *
        * @exception IOException if an input/output error occurs while rendering
        * @exception NullPointerException if <code>context</code>
        * or <code>component</code> is null
        */
        public void renderStart(FacesContext context, UIComponent component,
        ResponseWriter writer)
        throws IOException {

        // Skip this component if it is not relevant
        if (!component.isRendered() || isDisabled(component) ||
        isReadOnly(component)) { return; }

        // Set up variables we will need
        UIForm form = null;
        UIComponent parent = component.getParent();
        while (parent != null) {
        if (parent instanceof UIForm) { form = (UIForm) parent; break; }
        parent = parent.getParent();
        }
        if (form == null) { log.warn("CommandLinkComponent not nested inside UIForm, ignored"); return; }
        String formClientId = form.getClientId(context);

        // If this is the first nested command link inside this form,
        // render a hidden variable to identify which link did the submit
        String key = formClientId + NamingContainer.SEPARATOR_CHAR + TOKEN;
        if (context.getExternalContext().getRequestMap().get(key) == null) { writer.startElement("input", null); writer.writeAttribute("name", TOKEN, null); writer.writeAttribute("type", "hidden", null); writer.writeAttribute("value", "", null); writer.endElement("input"); context.getExternalContext().getRequestMap().put (key, Boolean.TRUE); }


        // Render the beginning of this hyperlink
        writer.startElement("a", component);

        }


        /**
        * <p>Render the attributes of a hyperlink to submit this form.</p>
        *
        * @param context FacesContext for the request we are processing
        * @param component UIComponent to be rendered
        * @param writer ResponseWriter we are rendering to
        *
        * @exception IOException if an input/output error occurs while rendering
        * @exception NullPointerException if <code>context</code>
        * or <code>component</code> is null
        */
        public void renderAttributes(FacesContext context, UIComponent component,
        ResponseWriter writer)
        throws IOException {

        // Skip this component if it is not relevant
        if (!component.isRendered() || isDisabled(component) ||
        isReadOnly(component)) { return; }

        // Set up variables we will need
        UIForm form = null;
        UIComponent parent = component.getParent();
        while (parent != null) {
        if (parent instanceof UIForm) { form = (UIForm) parent; break; }
        parent = parent.getParent();
        }
        if (form == null) { log.warn("CommandLinkComponent not nested inside UIForm, ignored"); return; }
        String formClientId = form.getClientId(context);

        // Render the attributes of this hyperlink
        if (component.getId() != null) { writer.writeAttribute("id", component.getClientId(context), "id"); }
        writer.writeAttribute("href", "#", null);
        String styleClass = (String)
        component.getAttributes().get("styleClass");
        if (styleClass != null) { writer.writeAttribute("class", styleClass, "styleClass"); }
        renderPassThrough(context, component, writer, passThrough);

        // Render the JavaScript content of the "onclick" element
        StringBuffer sb = new StringBuffer();
        sb.append("document.forms['");
        sb.append(formClientId);
        sb.append("']['");
        sb.append(TOKEN);
        sb.append("'].value='");
        sb.append(component.getClientId(context));
        sb.append("';");
        Iterator kids = component.getChildren().iterator();
        while (kids.hasNext()) {
        UIComponent kid = (UIComponent) kids.next();
        if (!(kid instanceof UIParameter)) { continue; }
        sb.append("document.forms['");
        sb.append(formClientId);
        sb.append("']['");
        sb.append((String) kid.getAttributes().get("name"));
        sb.append("'].value='");
        sb.append((String) kid.getAttributes().get("value"));
        sb.append("';");
        }
        sb.append("document.forms['");
        sb.append(formClientId);
        sb.append("'].submit(); return false;");
        writer.writeAttribute("onclick", sb.toString(), null);

        // Render the component value as the hyperlink text
        Object value = component.getAttributes().get("value");
        if (value != null) {
        if (value instanceof String) { writer.write((String) value); } else { writer.write(value.toString()); }
        }

        }


        /**
        * <p>Render the end of a hyperlink to submit this form.</p>
        *
        * @param context FacesContext for the request we are processing
        * @param component UIComponent to be rendered
        * @param writer ResponseWriter we are rendering to
        *
        * @exception IOException if an input/output error occurs while rendering
        * @exception NullPointerException if <code>context</code>
        * or <code>component</code> is null
        */
        public void renderEnd(FacesContext context, UIComponent component,
        ResponseWriter writer)
        throws IOException {

        // Skip this component if it is not relevant
        if (!component.isRendered() || isDisabled(component) ||
        isReadOnly(component)) { return; }

        // Render the beginning of this hyperlink
        writer.endElement("a");

        }


        }
        /*
        * Copyright 2002,2004 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.struts.faces.renderer;


        import java.io.IOException;
        import java.util.Iterator;
        import java.util.Map;

        import javax.faces.application.FacesMessage;
        import javax.faces.component.EditableValueHolder;
        import javax.faces.component.UIComponent;
        import javax.faces.component.ValueHolder;
        import javax.faces.context.FacesContext;
        import javax.faces.context.ResponseWriter;
        import javax.faces.convert.Converter;
        import javax.faces.convert.ConverterException;
        import javax.faces.el.ValueBinding;
        import javax.faces.render.Renderer;

        import org.apache.commons.logging.Log;
        import org.apache.commons.logging.LogFactory;


        /**
        * <p>Abstract base class for concrete implementations of
        * <code>javax.faces.render.Renderer</code> for the
        * <em>Struts-Faces Integration Library</em>.</p>
        *
        * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $
        */

        public abstract class AbstractRenderer extends Renderer {


        // -------------------------------------------------------- Static Variables


        private static final Log log =
        LogFactory.getLog(AbstractRenderer.class);


        // -------------------------------------------------------- Renderer Methods


        /**
        * <p>Decode any new state of the specified <code>UIComponent</code>
        * from the request contained in the specified <code>FacesContext</code>,
        * and store that state on the <code>UIComponent</code>.</p>
        *
        * <p>The default implementation calls <code>setSubmittedValue()</code>
        * unless this component has a boolean <code>disabled</code> or
        * <code>readonly</code> attribute that is set to <code>true</code>.</p>
        *
        * @param context <code>FacesContext</code> for the current request
        * @param component <code>UIComponent</code> to be decoded
        *
        * @exception NullPointerException if <code>context</code> or
        * <code>component</code> is <code>null</code>
        */
        public void decode(FacesContext context, UIComponent component) {

        // Enforce NPE requirements in the Javadocs
        if ((context == null) || (component == null)) { throw new NullPointerException(); }

        // Disabled or readonly components are not decoded
        if (isDisabled(component) || isReadOnly(component))

        { return; }

        // Save submitted value on EditableValueHolder components
        if (component instanceof EditableValueHolder) { setSubmittedValue(context, component); }

        }


        /**
        * <p>Render the beginning of the specified <code>UIComponent</code>
        * to the output stream or writer associated with the response we are
        * creating.</p>
        *
        * <p>The default implementation calls <code>renderStart()</code> and
        * <code>renderAttributes()</code>.</p>
        *
        * @param context <code>FacesContext</code> for the current request
        * @param component <code>UIComponent</code> to be decoded
        *
        * @exception NullPointerException if <code>context</code> or
        * <code>component</code> is <code>null</code>
        *
        * @exception IOException if an input/output error occurs
        */
        public void encodeBegin(FacesContext context, UIComponent component)
        throws IOException {

        // Enforce NPE requirements in the Javadocs
        if ((context == null) || (component == null)) { throw new NullPointerException(); }

        if (log.isTraceEnabled()) { log.trace("encodeBegin(id=" + component.getId() + ", family=" + component.getFamily() + ", rendererType=" + component.getRendererType() + ")"); }

        // Render the element and attributes for this component
        ResponseWriter writer = context.getResponseWriter();
        renderStart(context, component, writer);
        renderAttributes(context, component, writer);

        }


        /**
        * <p>Render the children of the specified <code>UIComponent</code>
        * to the output stream or writer associated with the response we are
        * creating.</p>
        *
        * <p>The default implementation iterates through the children of
        * this component and renders them.</p>
        *
        * @param context <code>FacesContext</code> for the current request
        * @param component <code>UIComponent</code> to be decoded
        *
        * @exception NullPointerException if <code>context</code> or
        * <code>component</code> is <code>null</code>
        *
        * @exception IOException if an input/output error occurs
        */
        public void encodeChildren(FacesContext context, UIComponent component)
        throws IOException {

        if (context == null || component == null) { throw new NullPointerException(); }

        if (log.isTraceEnabled()) { log.trace("encodeChildren(id=" + component.getId() + ", family=" + component.getFamily() + ", rendererType=" + component.getRendererType() + ")"); }
        Iterator kids = component.getChildren().iterator();
        while (kids.hasNext()) {
        UIComponent kid = (UIComponent) kids.next();
        kid.encodeBegin(context);
        if (kid.getRendersChildren()) { kid.encodeChildren(context); }
        kid.encodeEnd(context);
        }
        if (log.isTraceEnabled()) { log.trace("encodeChildren(id=" + component.getId() + ") end"); }

        }


        /**
        * <p>Render the ending of the specified <code>UIComponent</code>
        * to the output stream or writer associated with the response we are
        * creating.</p>
        *
        * <p>The default implementation calls <code>renderEnd()</code>.</p>
        *
        * @param context <code>FacesContext</code> for the current request
        * @param component <code>UIComponent</code> to be decoded
        *
        * @exception NullPointerException if <code>context</code> or
        * <code>component</code> is <code>null</code>
        *
        * @exception IOException if an input/output error occurs
        */
        public void encodeEnd(FacesContext context, UIComponent component)
        throws IOException {

        // Enforce NPE requirements in the Javadocs
        if ((context == null) || (component == null)) { throw new NullPointerException(); }

        if (log.isTraceEnabled()) { log.trace("encodeEnd(id=" + component.getId() + ", family=" + component.getFamily() + ", rendererType=" + component.getRendererType() + ")"); }

        // Render the element closing for this component
        ResponseWriter writer = context.getResponseWriter();
        renderEnd(context, component, writer);

        }


        // --------------------------------------------------------- Package Methods


        // ------------------------------------------------------- Protected Methods


        /**
        * <p>Render nested child components by invoking the encode methods
        * on those components, but only when the <code>rendered</code>
        * property is <code>true</code>.</p>
        */
        protected void encodeRecursive(FacesContext context, UIComponent component)
        throws IOException {

        // suppress rendering if "rendered" property on the component is
        // false.
        if (!component.isRendered()) { return; }

        // Render this component and its children recursively
        if (log.isTraceEnabled())

        { log.trace("encodeRecursive(id=" + component.getId() + ", family=" + component.getFamily() + ", rendererType=" + component.getRendererType() + ") encodeBegin"); }

        component.encodeBegin(context);
        if (component.getRendersChildren()) {
        if (log.isTraceEnabled())

        { log.trace("encodeRecursive(id=" + component.getId() + ") delegating"); }

        component.encodeChildren(context);
        } else {
        if (log.isTraceEnabled())

        { log.trace("encodeRecursive(id=" + component.getId() + ") recursing"); }

        Iterator kids = component.getChildren().iterator();
        while (kids.hasNext())

        { UIComponent kid = (UIComponent) kids.next(); encodeRecursive(context, kid); }

        }
        if (log.isTraceEnabled())

        { log.trace("encodeRecursive(id=" + component.getId() + ") encodeEnd"); }

        component.encodeEnd(context);

        }

        /**

        • <p>Return <code>true</code> if the specified component is disabled.</p>
          *
        • @param component <code>UIComponent</code> to be checked
          */
          protected boolean isDisabled(UIComponent component) {

        Object disabled = component.getAttributes().get("disabled");
        if (disabled == null)

        { return (false); }
        if (disabled instanceof String) { return (Boolean.valueOf((String) disabled).booleanValue()); } else { return (disabled.equals(Boolean.TRUE)); }

        }


        /**
        * <p>Return <code>true</code> if the specified component is read only.</p>
        *
        * @param component <code>UIComponent</code> to be checked
        */
        protected boolean isReadOnly(UIComponent component) {

        Object readonly = component.getAttributes().get("readonly");
        if (readonly == null) { return (false); }

        if (readonly instanceof String)

        { return (Boolean.valueOf((String) readonly).booleanValue()); }

        else

        { return (readonly.equals(Boolean.TRUE)); }

        }

        /**

        • <p>Render the element attributes for the generated markup related to this
        • component. Simple renderers that create a single markup element
        • for this component should override this method and include calls to
        • to <code>writeAttribute()</code> and <code>writeURIAttribute</code>
        • on the specified <code>ResponseWriter</code>.</p>
          *
        • <p>The default implementation does nothing.</p>
          *
        • @param context <code>FacesContext</code> for the current request
        • @param component <code>EditableValueHolder</code> component whose
        • submitted value is to be stored
        • @param writer <code>ResponseWriter</code> to which the element
        • start should be rendered
          *
        • @exception IOException if an input/output error occurs
          */
          protected void renderAttributes(FacesContext context, UIComponent component,
          ResponseWriter writer) throws IOException {

        }

        /**

        • <p>Render the element end for the generated markup related to this
        • component. Simple renderers that create a single markup element
        • for this component should override this method and include a call
        • to <code>endElement()</code> on the specified
        • <code>ResponseWriter</code>.</p>
          *
        • <p>The default implementation does nothing.</p>
          *
        • @param context <code>FacesContext</code> for the current request
        • @param component <code>EditableValueHolder</code> component whose
        • submitted value is to be stored
        • @param writer <code>ResponseWriter</code> to which the element
        • start should be rendered
          *
        • @exception IOException if an input/output error occurs
          */
          protected void renderEnd(FacesContext context, UIComponent component,
          ResponseWriter writer) throws IOException {

        }

        /**

        • <p>Render any boolean attributes on the specified list that have
        • <code>true</code> values on the corresponding attribute of the
        • specified <code>UIComponent</code>.</p>
          *
        • @param context <code>FacesContext</code> for the current request
        • @param component <code>EditableValueHolder</code> component whose
        • submitted value is to be stored
        • @param writer <code>ResponseWriter</code> to which the element
        • start should be rendered
        • @param names List of attribute names to be passed through
          *
        • @exception IOException if an input/output error occurs
          */
          protected void renderBoolean(FacesContext context,
          UIComponent component,
          ResponseWriter writer,
          String names[]) throws IOException {

        if (names == null)

        { return; }
        Map attributes = component.getAttributes();
        boolean flag;
        Object value;
        for (int i = 0; i < names.length; i++) {
        value = attributes.get(names[i]);
        if (value != null) {
        if (value instanceof String) { flag = Boolean.valueOf((String) value).booleanValue(); } else { flag = Boolean.valueOf(value.toString()).booleanValue(); }
        if (flag) { writer.writeAttribute(names[i], names[i], names[i]); flag = false; }
        }
        }

        }


        /**
        * <p>Render any attributes on the specified list directly to the
        * specified <code>ResponseWriter</code> for which the specified
        * <code>UIComponent</code> has a non-<code>null</code> attribute value.
        * This method may be used to "pass through" commonly used attribute
        * name/value pairs with a minimum of code.</p>
        *
        * @param context <code>FacesContext</code> for the current request
        * @param component <code>EditableValueHolder</code> component whose
        * submitted value is to be stored
        * @param writer <code>ResponseWriter</code> to which the element
        * start should be rendered
        * @param names List of attribute names to be passed through
        *
        * @exception IOException if an input/output error occurs
        */
        protected void renderPassThrough(FacesContext context,
        UIComponent component,
        ResponseWriter writer,
        String names[]) throws IOException {

        if (names == null) { return; }

        Map attributes = component.getAttributes();
        Object value;
        for (int i = 0; i < names.length; i++) {
        value = attributes.get(names[i]);
        if (value != null) {
        if (value instanceof String)

        { writer.writeAttribute(names[i], (String) value, names[i]); }

        else

        { writer.writeAttribute(names[i], value.toString(), names[i]); }

        }
        }

        }

        /**

        • <p>Render the element start for the generated markup related to this
        • component. Simple renderers that create a single markup element
        • for this component should override this method and include a call
        • to <code>startElement()</code> on the specified
        • <code>ResponseWriter</code>.</p>
          *
        • <p>The default implementation does nothing.</p>
          *
        • @param context <code>FacesContext</code> for the current request
        • @param component <code>EditableValueHolder</code> component whose
        • submitted value is to be stored
        • @param writer <code>ResponseWriter</code> to which the element
        • start should be rendered
          *
        • @exception IOException if an input/output error occurs
          */
          protected void renderStart(FacesContext context, UIComponent component,
          ResponseWriter writer) throws IOException {

        }

        /**

        • <p>If a submitted value was included on this request, store it in the
        • component as appropriate.</p>
          *
        • <p>The default implementation determines whether this component
        • implements <code>EditableValueHolder</code>. If so, it checks for a
        • request parameter with the same name as the <code>clientId</code>
        • of this <code>UIComponent</code>. If there is such a parameter, its
        • value is passed (as a String) to the <code>setSubmittedValue()</code>
        • method on the <code>EditableValueHolder</code> component.</p>
          *
        • @param context <code>FacesContext</code> for the current request
        • @param component <code>EditableValueHolder</code> component whose
        • submitted value is to be stored
          */
          protected void setSubmittedValue
          (FacesContext context, UIComponent component) {

        if (!(component instanceof EditableValueHolder))

        { return; }
        String clientId = component.getClientId(context);
        Map parameters = context.getExternalContext().getRequestParameterMap();
        if (parameters.containsKey(clientId)) {
        if (log.isTraceEnabled()) { log.trace("setSubmittedValue(" + clientId + "," + (String) parameters.get(clientId)); }
        component.getAttributes().put("submittedValue",
        (String) parameters.get(clientId));
        }

        }


        // --------------------------------------------------------- Private Methods


        /**
        * <p>Decode the current state of the specified UIComponent from the
        * request contained in the specified <code>FacesContext</code>, and
        * attempt to convert this state information into an object of the
        * type equired for this component.</p>
        *
        * @param context FacesContext for the request we are processing
        * @param component UIComponent to be decoded
        *
        * @exception NullPointerException if context or component is null
        */
        /*
        public void decode(FacesContext context, UIComponent component) {

        // Enforce NPE requirements in the Javadocs
        if ((context == null) || (component == null)) { throw new NullPointerException(); }

        // Only input components need to be decoded
        if (!(component instanceof UIInput)) { return; }

        UIInput input = (UIInput) component;

        // Save the old value for use in generating ValueChangedEvents
        Object oldValue = input.getValue();
        if (oldValue instanceof String) {
        try

        { oldValue = getAsObject(context, component, (String) oldValue); }

        catch (ConverterException e)

        { ; }

        }
        input.setPrevious(oldValue);

        // Decode and convert (if needed) the new value
        String clientId = component.getClientId(context);
        Map map = context.getExternalContext().getRequestParameterMap();
        String newString = (String) map.get(clientId);
        Object newValue = null;
        try

        { newValue = getAsObject(context, component, newString); input.setValue(newValue); input.setValid(true); }

        catch (ConverterException e)

        { input.setValue(newValue); input.setValid(false); addConverterMessage(context, component, e.getMessage()); }

        }
        */

        // --------------------------------------------------------- Package Methods

        // ------------------------------------------------------- Protected Methods

        /**

        • <p>Add an error message denoting a conversion failure.</p>
          *
        • @param context The <code>FacesContext</code> for this request
        • @param component The <code>UIComponent</code> that experienced
        • the conversion failure
        • @param text The text of the error message
          */
          /*
          protected void addConverterMessage(FacesContext context,
          UIComponent component,
          String text) { String clientId = component.getClientId(context); FacesMessage message = new FacesMessage (text, "Conversion error on component '" + clientId + "'"); context.addMessage(clientId, message); }

          */

        /**

        • <p>Convert the String representation of this component's value
        • to the corresponding Object representation. The default
        • implementation utilizes the <code>getAsObject()</code> method of any
        • associated <code>Converter</code>.</p>
          *
        • @param context The <code>FacesContext</code> for this request
        • @param component The <code>UIComponent</code> whose value is
        • being converted
        • @param value The String representation to be converted
          *
        • @exception ConverterException if conversion fails
          */
          /*
          protected Object getAsObject(FacesContext context, UIComponent component,
          String value) throws ConverterException {

        // Identify any Converter associated with this component value
        ValueBinding vb = component.getValueBinding("value");
        Converter converter = null;
        if (component instanceof ValueHolder)

        { // Acquire explicitly assigned Converter (if any) converter = ((ValueHolder) component).getConverter(); }
        if ((converter == null) && (vb != null)) {
        Class type = vb.getType(context);
        if ((type == null) || (type == String.class)) { return (value); // No conversion required for Strings }
        // Acquire implicit by-type Converter (if any)
        converter = context.getApplication().createConverter(type);
        }

        // Convert the result if we identified a Converter
        if (converter != null) { return (converter.getAsObject(context, component, value)); } else { return (value); }

        }
        */


        /**
        * <p>Convert the Object representation of this component's value
        * to the corresponding String representation. The default implementation
        * utilizes the <code>getAsString()</code> method of any associated
        * <code>Converter</code>.</p>
        *
        * @param context The <code>FacesContext</code> for this request
        * @param component The <code>UIComponent</code> whose value is
        * being converted
        * @param value The Object representation to be converted
        *
        * @exception ConverterException if conversion fails
        */
        protected String getAsString(FacesContext context, UIComponent component,
        Object value) throws ConverterException {

        // Identify any Converter associated with this component value
        ValueBinding vb = component.getValueBinding("value");
        Converter converter = null;
        if (component instanceof ValueHolder) { // Acquire explicitly assigned Converter (if any) converter = ((ValueHolder) component).getConverter(); }

        if ((converter == null) && (vb != null)) {
        // Acquire implicit by-type Converter (if any)
        Class type = vb.getType(context);
        if (type != null)

        { converter = context.getApplication().createConverter(type); }

        }

        // Convert the result if we identified a Converter
        if (converter != null)

        { return (converter.getAsString(context, component, value)); }

        else if (value == null)

        { return (""); }

        else if (value instanceof String)

        { return ((String) value); }

        else

        { return (value.toString()); }

        }

        }

        Show
        Ed Burns added a comment - Craig McClanahan has a parallel implementation in struts faces: Craig wrote: FWIW, Struts-Faces had to create its own CommandLinkRenderer because the RI one was tied to the RI's FormRenderer (and Struts needed its own). Not only does it work with multiple forms, but it also decouples the two renderer implementations, and will work inside anyone's form renderer. You might want to consider doing something like that – and get rid of the unfortunate language requiring linkage (in standard-html-renderkit.html) in JSF 1.2. Craig /* Copyright 2002-2004 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.struts.faces.renderer; import java.io.IOException; import java.util.Iterator; import javax.faces.component.NamingContainer; import javax.faces.component.UICommand; import javax.faces.component.UIComponent; import javax.faces.component.UIForm; import javax.faces.component.UIParameter; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.event.ActionEvent; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.Globals; import org.apache.struts.config.ActionConfig; import org.apache.struts.config.ModuleConfig; /** <p><code>Renderer</code> implementation for the <code>commandLink</code> tag from the <em>Struts-Faces Integration Library</em>.</p> * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $ */ public class CommandLinkRenderer extends AbstractRenderer { // -------------------------------------------------------- Static Variables /** <p>Token for private names.</p> */ private static final String TOKEN = "org_apache_struts_faces_renderer_CommandLinkRenderer"; /** <p>The <code>Log</code> instance for this class.</p> */ private static Log log = LogFactory.getLog(CommandLinkRenderer.class); // ---------------------------------------------------------- Public Methods /** <p>Perform setup processing that will be required for decoding the incoming request.</p> * @param context FacesContext for the request we are processing @param component UIComponent to be processed * @exception NullPointerException if <code>context</code> or <code>component</code> is null */ public void decode(FacesContext context, UIComponent component) { // Implement spec requirements on NullPointerException if ((context == null) || (component == null)) { throw new NullPointerException(); } // Skip this component if it is not relevant if (!component.isRendered() || isDisabled(component) || isReadOnly(component)) { return; } // Set up variables we will need UIForm form = null; UIComponent parent = component.getParent(); while (parent != null) { if (parent instanceof UIForm) { form = (UIForm) parent; break; } parent = parent.getParent(); } if (form == null) { log.warn("CommandLinkComponent not nested inside UIForm, ignored"); return; } String formClientId = form.getClientId(context); // Was this the component that submitted this form? String paramId = TOKEN; String value = (String) context.getExternalContext().getRequestParameterMap().get(paramId); if ((value == null) || !value.equals(component.getClientId(context))) { if (log.isTraceEnabled()) { log.trace("decode(" + component.getId() + ") --> not active"); } return; } // Queue an ActionEvent from this component if (log.isTraceEnabled()) { log.trace("decode(" + component.getId() + ") --> queueEvent()"); } component.queueEvent(new ActionEvent(component)); } private static String passThrough[] = { "accesskey", "charset", "dir", "hreflang", "lang", "onblur", /* "onclick", */ "ondblclick", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "rel", "rev", "style", "tabindex", "target", "title", "type" }; /** * <p>Render the beginning of a hyperlink to submit this form.</p> * * @param context FacesContext for the request we are processing * @param component UIComponent to be rendered * @param writer ResponseWriter we are rendering to * * @exception IOException if an input/output error occurs while rendering * @exception NullPointerException if <code>context</code> * or <code>component</code> is null */ public void renderStart(FacesContext context, UIComponent component, ResponseWriter writer) throws IOException { // Skip this component if it is not relevant if (!component.isRendered() || isDisabled(component) || isReadOnly(component)) { return; } // Set up variables we will need UIForm form = null; UIComponent parent = component.getParent(); while (parent != null) { if (parent instanceof UIForm) { form = (UIForm) parent; break; } parent = parent.getParent(); } if (form == null) { log.warn("CommandLinkComponent not nested inside UIForm, ignored"); return; } String formClientId = form.getClientId(context); // If this is the first nested command link inside this form, // render a hidden variable to identify which link did the submit String key = formClientId + NamingContainer.SEPARATOR_CHAR + TOKEN; if (context.getExternalContext().getRequestMap().get(key) == null) { writer.startElement("input", null); writer.writeAttribute("name", TOKEN, null); writer.writeAttribute("type", "hidden", null); writer.writeAttribute("value", "", null); writer.endElement("input"); context.getExternalContext().getRequestMap().put (key, Boolean.TRUE); } // Render the beginning of this hyperlink writer.startElement("a", component); } /** * <p>Render the attributes of a hyperlink to submit this form.</p> * * @param context FacesContext for the request we are processing * @param component UIComponent to be rendered * @param writer ResponseWriter we are rendering to * * @exception IOException if an input/output error occurs while rendering * @exception NullPointerException if <code>context</code> * or <code>component</code> is null */ public void renderAttributes(FacesContext context, UIComponent component, ResponseWriter writer) throws IOException { // Skip this component if it is not relevant if (!component.isRendered() || isDisabled(component) || isReadOnly(component)) { return; } // Set up variables we will need UIForm form = null; UIComponent parent = component.getParent(); while (parent != null) { if (parent instanceof UIForm) { form = (UIForm) parent; break; } parent = parent.getParent(); } if (form == null) { log.warn("CommandLinkComponent not nested inside UIForm, ignored"); return; } String formClientId = form.getClientId(context); // Render the attributes of this hyperlink if (component.getId() != null) { writer.writeAttribute("id", component.getClientId(context), "id"); } writer.writeAttribute("href", "#", null); String styleClass = (String) component.getAttributes().get("styleClass"); if (styleClass != null) { writer.writeAttribute("class", styleClass, "styleClass"); } renderPassThrough(context, component, writer, passThrough); // Render the JavaScript content of the "onclick" element StringBuffer sb = new StringBuffer(); sb.append("document.forms['"); sb.append(formClientId); sb.append("']['"); sb.append(TOKEN); sb.append("'].value='"); sb.append(component.getClientId(context)); sb.append("';"); Iterator kids = component.getChildren().iterator(); while (kids.hasNext()) { UIComponent kid = (UIComponent) kids.next(); if (!(kid instanceof UIParameter)) { continue; } sb.append("document.forms['"); sb.append(formClientId); sb.append("']['"); sb.append((String) kid.getAttributes().get("name")); sb.append("'].value='"); sb.append((String) kid.getAttributes().get("value")); sb.append("';"); } sb.append("document.forms['"); sb.append(formClientId); sb.append("'].submit(); return false;"); writer.writeAttribute("onclick", sb.toString(), null); // Render the component value as the hyperlink text Object value = component.getAttributes().get("value"); if (value != null) { if (value instanceof String) { writer.write((String) value); } else { writer.write(value.toString()); } } } /** * <p>Render the end of a hyperlink to submit this form.</p> * * @param context FacesContext for the request we are processing * @param component UIComponent to be rendered * @param writer ResponseWriter we are rendering to * * @exception IOException if an input/output error occurs while rendering * @exception NullPointerException if <code>context</code> * or <code>component</code> is null */ public void renderEnd(FacesContext context, UIComponent component, ResponseWriter writer) throws IOException { // Skip this component if it is not relevant if (!component.isRendered() || isDisabled(component) || isReadOnly(component)) { return; } // Render the beginning of this hyperlink writer.endElement("a"); } } /* * Copyright 2002,2004 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.struts.faces.renderer; import java.io.IOException; import java.util.Iterator; import java.util.Map; import javax.faces.application.FacesMessage; import javax.faces.component.EditableValueHolder; import javax.faces.component.UIComponent; import javax.faces.component.ValueHolder; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.el.ValueBinding; import javax.faces.render.Renderer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * <p>Abstract base class for concrete implementations of * <code>javax.faces.render.Renderer</code> for the * <em>Struts-Faces Integration Library</em>.</p> * * @version $Rev: 54934 $ $Date: 2004-10-16 10:07:50 -0700 (Sat, 16 Oct 2004) $ */ public abstract class AbstractRenderer extends Renderer { // -------------------------------------------------------- Static Variables private static final Log log = LogFactory.getLog(AbstractRenderer.class); // -------------------------------------------------------- Renderer Methods /** * <p>Decode any new state of the specified <code>UIComponent</code> * from the request contained in the specified <code>FacesContext</code>, * and store that state on the <code>UIComponent</code>.</p> * * <p>The default implementation calls <code>setSubmittedValue()</code> * unless this component has a boolean <code>disabled</code> or * <code>readonly</code> attribute that is set to <code>true</code>.</p> * * @param context <code>FacesContext</code> for the current request * @param component <code>UIComponent</code> to be decoded * * @exception NullPointerException if <code>context</code> or * <code>component</code> is <code>null</code> */ public void decode(FacesContext context, UIComponent component) { // Enforce NPE requirements in the Javadocs if ((context == null) || (component == null)) { throw new NullPointerException(); } // Disabled or readonly components are not decoded if (isDisabled(component) || isReadOnly(component)) { return; } // Save submitted value on EditableValueHolder components if (component instanceof EditableValueHolder) { setSubmittedValue(context, component); } } /** * <p>Render the beginning of the specified <code>UIComponent</code> * to the output stream or writer associated with the response we are * creating.</p> * * <p>The default implementation calls <code>renderStart()</code> and * <code>renderAttributes()</code>.</p> * * @param context <code>FacesContext</code> for the current request * @param component <code>UIComponent</code> to be decoded * * @exception NullPointerException if <code>context</code> or * <code>component</code> is <code>null</code> * * @exception IOException if an input/output error occurs */ public void encodeBegin(FacesContext context, UIComponent component) throws IOException { // Enforce NPE requirements in the Javadocs if ((context == null) || (component == null)) { throw new NullPointerException(); } if (log.isTraceEnabled()) { log.trace("encodeBegin(id=" + component.getId() + ", family=" + component.getFamily() + ", rendererType=" + component.getRendererType() + ")"); } // Render the element and attributes for this component ResponseWriter writer = context.getResponseWriter(); renderStart(context, component, writer); renderAttributes(context, component, writer); } /** * <p>Render the children of the specified <code>UIComponent</code> * to the output stream or writer associated with the response we are * creating.</p> * * <p>The default implementation iterates through the children of * this component and renders them.</p> * * @param context <code>FacesContext</code> for the current request * @param component <code>UIComponent</code> to be decoded * * @exception NullPointerException if <code>context</code> or * <code>component</code> is <code>null</code> * * @exception IOException if an input/output error occurs */ public void encodeChildren(FacesContext context, UIComponent component) throws IOException { if (context == null || component == null) { throw new NullPointerException(); } if (log.isTraceEnabled()) { log.trace("encodeChildren(id=" + component.getId() + ", family=" + component.getFamily() + ", rendererType=" + component.getRendererType() + ")"); } Iterator kids = component.getChildren().iterator(); while (kids.hasNext()) { UIComponent kid = (UIComponent) kids.next(); kid.encodeBegin(context); if (kid.getRendersChildren()) { kid.encodeChildren(context); } kid.encodeEnd(context); } if (log.isTraceEnabled()) { log.trace("encodeChildren(id=" + component.getId() + ") end"); } } /** * <p>Render the ending of the specified <code>UIComponent</code> * to the output stream or writer associated with the response we are * creating.</p> * * <p>The default implementation calls <code>renderEnd()</code>.</p> * * @param context <code>FacesContext</code> for the current request * @param component <code>UIComponent</code> to be decoded * * @exception NullPointerException if <code>context</code> or * <code>component</code> is <code>null</code> * * @exception IOException if an input/output error occurs */ public void encodeEnd(FacesContext context, UIComponent component) throws IOException { // Enforce NPE requirements in the Javadocs if ((context == null) || (component == null)) { throw new NullPointerException(); } if (log.isTraceEnabled()) { log.trace("encodeEnd(id=" + component.getId() + ", family=" + component.getFamily() + ", rendererType=" + component.getRendererType() + ")"); } // Render the element closing for this component ResponseWriter writer = context.getResponseWriter(); renderEnd(context, component, writer); } // --------------------------------------------------------- Package Methods // ------------------------------------------------------- Protected Methods /** * <p>Render nested child components by invoking the encode methods * on those components, but only when the <code>rendered</code> * property is <code>true</code>.</p> */ protected void encodeRecursive(FacesContext context, UIComponent component) throws IOException { // suppress rendering if "rendered" property on the component is // false. if (!component.isRendered()) { return; } // Render this component and its children recursively if (log.isTraceEnabled()) { log.trace("encodeRecursive(id=" + component.getId() + ", family=" + component.getFamily() + ", rendererType=" + component.getRendererType() + ") encodeBegin"); } component.encodeBegin(context); if (component.getRendersChildren()) { if (log.isTraceEnabled()) { log.trace("encodeRecursive(id=" + component.getId() + ") delegating"); } component.encodeChildren(context); } else { if (log.isTraceEnabled()) { log.trace("encodeRecursive(id=" + component.getId() + ") recursing"); } Iterator kids = component.getChildren().iterator(); while (kids.hasNext()) { UIComponent kid = (UIComponent) kids.next(); encodeRecursive(context, kid); } } if (log.isTraceEnabled()) { log.trace("encodeRecursive(id=" + component.getId() + ") encodeEnd"); } component.encodeEnd(context); } /** <p>Return <code>true</code> if the specified component is disabled.</p> * @param component <code>UIComponent</code> to be checked */ protected boolean isDisabled(UIComponent component) { Object disabled = component.getAttributes().get("disabled"); if (disabled == null) { return (false); } if (disabled instanceof String) { return (Boolean.valueOf((String) disabled).booleanValue()); } else { return (disabled.equals(Boolean.TRUE)); } } /** * <p>Return <code>true</code> if the specified component is read only.</p> * * @param component <code>UIComponent</code> to be checked */ protected boolean isReadOnly(UIComponent component) { Object readonly = component.getAttributes().get("readonly"); if (readonly == null) { return (false); } if (readonly instanceof String) { return (Boolean.valueOf((String) readonly).booleanValue()); } else { return (readonly.equals(Boolean.TRUE)); } } /** <p>Render the element attributes for the generated markup related to this component. Simple renderers that create a single markup element for this component should override this method and include calls to to <code>writeAttribute()</code> and <code>writeURIAttribute</code> on the specified <code>ResponseWriter</code>.</p> * <p>The default implementation does nothing.</p> * @param context <code>FacesContext</code> for the current request @param component <code>EditableValueHolder</code> component whose submitted value is to be stored @param writer <code>ResponseWriter</code> to which the element start should be rendered * @exception IOException if an input/output error occurs */ protected void renderAttributes(FacesContext context, UIComponent component, ResponseWriter writer) throws IOException { } /** <p>Render the element end for the generated markup related to this component. Simple renderers that create a single markup element for this component should override this method and include a call to <code>endElement()</code> on the specified <code>ResponseWriter</code>.</p> * <p>The default implementation does nothing.</p> * @param context <code>FacesContext</code> for the current request @param component <code>EditableValueHolder</code> component whose submitted value is to be stored @param writer <code>ResponseWriter</code> to which the element start should be rendered * @exception IOException if an input/output error occurs */ protected void renderEnd(FacesContext context, UIComponent component, ResponseWriter writer) throws IOException { } /** <p>Render any boolean attributes on the specified list that have <code>true</code> values on the corresponding attribute of the specified <code>UIComponent</code>.</p> * @param context <code>FacesContext</code> for the current request @param component <code>EditableValueHolder</code> component whose submitted value is to be stored @param writer <code>ResponseWriter</code> to which the element start should be rendered @param names List of attribute names to be passed through * @exception IOException if an input/output error occurs */ protected void renderBoolean(FacesContext context, UIComponent component, ResponseWriter writer, String names[]) throws IOException { if (names == null) { return; } Map attributes = component.getAttributes(); boolean flag; Object value; for (int i = 0; i < names.length; i++) { value = attributes.get(names [i] ); if (value != null) { if (value instanceof String) { flag = Boolean.valueOf((String) value).booleanValue(); } else { flag = Boolean.valueOf(value.toString()).booleanValue(); } if (flag) { writer.writeAttribute(names[i], names[i], names[i]); flag = false; } } } } /** * <p>Render any attributes on the specified list directly to the * specified <code>ResponseWriter</code> for which the specified * <code>UIComponent</code> has a non-<code>null</code> attribute value. * This method may be used to "pass through" commonly used attribute * name/value pairs with a minimum of code.</p> * * @param context <code>FacesContext</code> for the current request * @param component <code>EditableValueHolder</code> component whose * submitted value is to be stored * @param writer <code>ResponseWriter</code> to which the element * start should be rendered * @param names List of attribute names to be passed through * * @exception IOException if an input/output error occurs */ protected void renderPassThrough(FacesContext context, UIComponent component, ResponseWriter writer, String names[]) throws IOException { if (names == null) { return; } Map attributes = component.getAttributes(); Object value; for (int i = 0; i < names.length; i++) { value = attributes.get(names [i] ); if (value != null) { if (value instanceof String) { writer.writeAttribute(names[i], (String) value, names[i]); } else { writer.writeAttribute(names[i], value.toString(), names[i]); } } } } /** <p>Render the element start for the generated markup related to this component. Simple renderers that create a single markup element for this component should override this method and include a call to <code>startElement()</code> on the specified <code>ResponseWriter</code>.</p> * <p>The default implementation does nothing.</p> * @param context <code>FacesContext</code> for the current request @param component <code>EditableValueHolder</code> component whose submitted value is to be stored @param writer <code>ResponseWriter</code> to which the element start should be rendered * @exception IOException if an input/output error occurs */ protected void renderStart(FacesContext context, UIComponent component, ResponseWriter writer) throws IOException { } /** <p>If a submitted value was included on this request, store it in the component as appropriate.</p> * <p>The default implementation determines whether this component implements <code>EditableValueHolder</code>. If so, it checks for a request parameter with the same name as the <code>clientId</code> of this <code>UIComponent</code>. If there is such a parameter, its value is passed (as a String) to the <code>setSubmittedValue()</code> method on the <code>EditableValueHolder</code> component.</p> * @param context <code>FacesContext</code> for the current request @param component <code>EditableValueHolder</code> component whose submitted value is to be stored */ protected void setSubmittedValue (FacesContext context, UIComponent component) { if (!(component instanceof EditableValueHolder)) { return; } String clientId = component.getClientId(context); Map parameters = context.getExternalContext().getRequestParameterMap(); if (parameters.containsKey(clientId)) { if (log.isTraceEnabled()) { log.trace("setSubmittedValue(" + clientId + "," + (String) parameters.get(clientId)); } component.getAttributes().put("submittedValue", (String) parameters.get(clientId)); } } // --------------------------------------------------------- Private Methods /** * <p>Decode the current state of the specified UIComponent from the * request contained in the specified <code>FacesContext</code>, and * attempt to convert this state information into an object of the * type equired for this component.</p> * * @param context FacesContext for the request we are processing * @param component UIComponent to be decoded * * @exception NullPointerException if context or component is null */ /* public void decode(FacesContext context, UIComponent component) { // Enforce NPE requirements in the Javadocs if ((context == null) || (component == null)) { throw new NullPointerException(); } // Only input components need to be decoded if (!(component instanceof UIInput)) { return; } UIInput input = (UIInput) component; // Save the old value for use in generating ValueChangedEvents Object oldValue = input.getValue(); if (oldValue instanceof String) { try { oldValue = getAsObject(context, component, (String) oldValue); } catch (ConverterException e) { ; } } input.setPrevious(oldValue); // Decode and convert (if needed) the new value String clientId = component.getClientId(context); Map map = context.getExternalContext().getRequestParameterMap(); String newString = (String) map.get(clientId); Object newValue = null; try { newValue = getAsObject(context, component, newString); input.setValue(newValue); input.setValid(true); } catch (ConverterException e) { input.setValue(newValue); input.setValid(false); addConverterMessage(context, component, e.getMessage()); } } */ // --------------------------------------------------------- Package Methods // ------------------------------------------------------- Protected Methods /** <p>Add an error message denoting a conversion failure.</p> * @param context The <code>FacesContext</code> for this request @param component The <code>UIComponent</code> that experienced the conversion failure @param text The text of the error message */ /* protected void addConverterMessage(FacesContext context, UIComponent component, String text) { String clientId = component.getClientId(context); FacesMessage message = new FacesMessage (text, "Conversion error on component '" + clientId + "'"); context.addMessage(clientId, message); } */ /** <p>Convert the String representation of this component's value to the corresponding Object representation. The default implementation utilizes the <code>getAsObject()</code> method of any associated <code>Converter</code>.</p> * @param context The <code>FacesContext</code> for this request @param component The <code>UIComponent</code> whose value is being converted @param value The String representation to be converted * @exception ConverterException if conversion fails */ /* protected Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException { // Identify any Converter associated with this component value ValueBinding vb = component.getValueBinding("value"); Converter converter = null; if (component instanceof ValueHolder) { // Acquire explicitly assigned Converter (if any) converter = ((ValueHolder) component).getConverter(); } if ((converter == null) && (vb != null)) { Class type = vb.getType(context); if ((type == null) || (type == String.class)) { return (value); // No conversion required for Strings } // Acquire implicit by-type Converter (if any) converter = context.getApplication().createConverter(type); } // Convert the result if we identified a Converter if (converter != null) { return (converter.getAsObject(context, component, value)); } else { return (value); } } */ /** * <p>Convert the Object representation of this component's value * to the corresponding String representation. The default implementation * utilizes the <code>getAsString()</code> method of any associated * <code>Converter</code>.</p> * * @param context The <code>FacesContext</code> for this request * @param component The <code>UIComponent</code> whose value is * being converted * @param value The Object representation to be converted * * @exception ConverterException if conversion fails */ protected String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException { // Identify any Converter associated with this component value ValueBinding vb = component.getValueBinding("value"); Converter converter = null; if (component instanceof ValueHolder) { // Acquire explicitly assigned Converter (if any) converter = ((ValueHolder) component).getConverter(); } if ((converter == null) && (vb != null)) { // Acquire implicit by-type Converter (if any) Class type = vb.getType(context); if (type != null) { converter = context.getApplication().createConverter(type); } } // Convert the result if we identified a Converter if (converter != null) { return (converter.getAsString(context, component, value)); } else if (value == null) { return (""); } else if (value instanceof String) { return ((String) value); } else { return (value.toString()); } } }
        Hide
        Ed Burns added a comment -

        enact

        Show
        Ed Burns added a comment - enact
        Hide
        Ed Burns added a comment -

        take ownership.

        Show
        Ed Burns added a comment - take ownership.
        Hide
        Ed Burns added a comment -

        Created an attachment (id=27)
        Prototype implementation

        Show
        Ed Burns added a comment - Created an attachment (id=27) Prototype implementation
        Hide
        Ed Burns added a comment -

        I have implemented a fix for this that doesn't require a spec change, so I'm
        closing the issue here. Work will continue on
        https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=26

        Ed

        Show
        Ed Burns added a comment - I have implemented a fix for this that doesn't require a spec change, so I'm closing the issue here. Work will continue on https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=26 Ed
        Hide
        Ed Burns added a comment -

        Prepare to delete "spec" subcomponent.

        Show
        Ed Burns added a comment - Prepare to delete "spec" subcomponent.
        Hide
        Ed Burns added a comment -

        Move all to 1.2

        Show
        Ed Burns added a comment - Move all to 1.2
        Hide
        Manfred Riem added a comment -

        Closing resolved issue out

        Show
        Manfred Riem added a comment - Closing resolved issue out

          People

          • Assignee:
            Ed Burns
            Reporter:
            rogerk
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: