All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.richfaces.component.behavior.ClientValidatorImpl Maven / Gradle / Ivy

There is a newer version: 4.3.7.Final
Show newest version
/*
 * $Id$
 * JBoss, Home of Professional Open Source
 * Copyright 2010, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.richfaces.component.behavior;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.ActionSource;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIMessage;
import javax.faces.component.UIMessages;
import javax.faces.component.behavior.ClientBehaviorContext;
import javax.faces.context.FacesContext;
import javax.faces.context.PartialViewContext;
import javax.faces.convert.Converter;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.BehaviorEvent;
import javax.faces.render.ClientBehaviorRenderer;
import javax.faces.render.RenderKit;
import javax.faces.validator.BeanValidator;
import javax.faces.validator.Validator;

import org.ajax4jsf.component.behavior.AjaxBehavior;
import org.ajax4jsf.javascript.ScriptUtils;
import org.richfaces.application.ServiceTracker;
import org.richfaces.cdk.annotations.Attribute;
import org.richfaces.cdk.annotations.JsfBehavior;
import org.richfaces.cdk.annotations.Tag;
import org.richfaces.cdk.annotations.TagType;
import org.richfaces.component.ClientSideMessage;
import org.richfaces.javascript.JavaScriptService;
import org.richfaces.javascript.Message;
import org.richfaces.log.Logger;
import org.richfaces.log.RichfacesLogger;
import org.richfaces.renderkit.html.ClientValidatorRenderer;
import org.richfaces.renderkit.html.FormClientValidatorRenderer;
import org.richfaces.validator.BeanValidatorService;
import org.richfaces.validator.ConverterDescriptor;
import org.richfaces.validator.FacesBeanValidator;
import org.richfaces.validator.FacesConverterService;
import org.richfaces.validator.FacesValidatorService;
import org.richfaces.validator.ValidatorDescriptor;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;

/**
 * 

The <rich:validator> behavior adds client-side validation to a form input control based on registered server-side validators. It provides this validation without the need to reproduce the server-side annotations.

* *

The <rich:validator> behavior triggers all client validator annotations listed in the relevant managed bean.

* * @author [email protected] */ @JsfBehavior(id = "org.richfaces.behavior.ClientValidator", tag = @Tag(name = "validator", handler = "org.richfaces.view.facelets.html.ClientValidatorHandler", type = TagType.Facelets), attributes = { "validator-props.xml", "immediate-prop.xml" }) public class ClientValidatorImpl extends AjaxBehavior implements ClientValidatorBehavior { private static final Set NONE = Collections.emptySet(); private static final Set THIS = Collections.singleton("@this"); private static final Class[] EMPTY_GROUPS = new Class[0]; private static final String VALUE = "value"; private static final Logger LOG = RichfacesLogger.COMPONENTS.getLogger(); private Class[] groups; private static final Function MESSAGES_TRANSFORMER = new Function() { public Message apply(FacesMessage msg) { return new Message(msg); } }; protected enum Properties { onvalid, oninvalid } @Override public String getScript(ClientBehaviorContext behaviorContext) { if (behaviorContext.getComponent() instanceof EditableValueHolder) { return super.getScript(behaviorContext); } else if (behaviorContext.getComponent() instanceof ActionSource) { ClientBehaviorRenderer renderer = getRenderer(behaviorContext.getFacesContext(), FormClientValidatorRenderer.RENDERER_TYPE); return renderer.getScript(behaviorContext, this); } else { throw new FacesException("Invalid target for client-side validator behavior"); } } @Override public String getRendererType() { return ClientValidatorRenderer.RENDERER_TYPE; } @Override public void broadcast(BehaviorEvent event) throws AbortProcessingException { // Add message components to re-render list ( if any ) FacesContext facesContext = FacesContext.getCurrentInstance(); PartialViewContext partialViewContext = facesContext.getPartialViewContext(); if (partialViewContext.isAjaxRequest()) { UIComponent component = event.getComponent(); if (component instanceof EditableValueHolder) { String clientId = component.getClientId(facesContext); final ImmutableList messages = ImmutableList.copyOf(Iterators.transform(facesContext.getMessages(clientId), MESSAGES_TRANSFORMER)); JavaScriptService javaScriptService = ServiceTracker.getService(JavaScriptService.class); javaScriptService.addPageReadyScript(facesContext, new MessageUpdateScript(clientId, messages)); if (messages.isEmpty()) { final String onvalid = getOnvalid(); if (onvalid != null && !"".equals(onvalid.trim())) { javaScriptService.addPageReadyScript(facesContext, new AnonymousFunctionCall().addToBody(onvalid)); } } else { final String oninvalid = getOninvalid(); if (oninvalid != null && !"".equals(oninvalid.trim())) { javaScriptService.addPageReadyScript(facesContext, new AnonymousFunctionCall("messages").addParameterValue(ScriptUtils.toScript(messages)).addToBody(oninvalid)); } } } } super.broadcast(event); } @Override public void setLiteralAttribute(String name, Object value) { super.setLiteralAttribute(name, value); if (compare(Properties.oninvalid, name)) { setOninvalid((String) value); } else if (compare(Properties.onvalid, name)) { setOnvalid((String) value); } } public Set getMessages(FacesContext context, UIComponent component) { Set messages = new HashSet(); findMessages(component.getParent(), component, messages, false, component.getId()); // TODO - enable then UIRichMessages will be done // findRichMessages(context, context.getViewRoot(), messages); return messages; } /** * Find all instances of the {@link org.richfaces.component.UIRichMessages} and update list of the rendered messages. * * @param context * @param component * @param messages */ protected void findRichMessages(FacesContext context, UIComponent component, String id, Set messages) { Iterator facetsAndChildren = component.getFacetsAndChildren(); while (facetsAndChildren.hasNext()) { UIComponent child = (UIComponent) facetsAndChildren.next(); if (child instanceof ClientSideMessage) { ClientSideMessage richMessage = (ClientSideMessage) child; if (null == richMessage.getFor()) { richMessage.updateMessages(context, id); messages.add(child); } } else { findRichMessages(context, child, id, messages); } } } /** * Recursive search messages for the parent component. * * @param parent * @param component * @param messages * @param id */ protected boolean findMessages(UIComponent parent, UIComponent component, Set messages, boolean found, Object id) { Iterator facetsAndChildren = parent.getFacetsAndChildren(); while (facetsAndChildren.hasNext()) { UIComponent child = (UIComponent) facetsAndChildren.next(); if (child != component) { if (child instanceof UIMessage || child instanceof UIMessages) { UIComponent message = (UIComponent) child; Object targetId = message.getAttributes().get("for"); if (null != targetId && targetId.equals(id)) { messages.add(message); found = true; } } else { found |= findMessages(child, null, messages, found, id); } } } if (!(found && parent instanceof NamingContainer) && component != null) { UIComponent newParent = parent.getParent(); if (null != newParent) { found = findMessages(newParent, parent, messages, found, id); } } return found; } /** *

* Look up for {@link ClientBehaviorRenderer} instence *

* * @param context current JSF context * @param rendererType desired renderer type * @return renderer instance * @throws {@link FacesException} if renderer can not be found */ protected ClientBehaviorRenderer getRenderer(FacesContext context, String rendererType) { if (null == context || null == rendererType) { throw new NullPointerException(); } ClientBehaviorRenderer renderer = null; RenderKit renderKit = context.getRenderKit(); if (null != renderKit) { renderer = renderKit.getClientBehaviorRenderer(rendererType); if (null == renderer) { throw new FacesException("No ClientBehaviorRenderer found for type " + rendererType); } } else { throw new FacesException("No renderkit available"); } return renderer; } /* * (non-Javadoc) * * @see org.richfaces.component.behavior.ClientValidatorBehavior#getConverter(javax.faces.component.behavior. * ClientBehaviorContext) */ public ConverterDescriptor getConverter(ClientBehaviorContext context) throws ConverterNotFoundException { UIComponent component = context.getComponent(); if (component instanceof EditableValueHolder) { EditableValueHolder input = (EditableValueHolder) component; FacesContext facesContext = context.getFacesContext(); Converter converter = input.getConverter(); if (null == converter) { Class valueType; ValueExpression valueExpression = component.getValueExpression(VALUE); if (null != valueExpression) { valueType = valueExpression.getType(facesContext.getELContext()); converter = createConverterByType(facesContext, valueType); } } if (null != converter) { FacesConverterService converterService = ServiceTracker.getService(facesContext, FacesConverterService.class); String converterMessage = (String) component.getAttributes().get("converterMessage"); return converterService.getConverterDescription(facesContext, input, converter, converterMessage); } else { return null; } } else { throw new ConverterNotFoundException("Component does not implement EditableValueHolder" + component); } } Converter createConverterByType(FacesContext facesContext, Class valueType) throws ConverterNotFoundException { Converter converter = null; if (valueType != null && valueType != Object.class) { Application application = facesContext.getApplication(); converter = application.createConverter(valueType); if (null == converter && valueType != String.class) { throw new ConverterNotFoundException("No converter registered for type " + valueType.getName()); } } return converter; } /* * (non-Javadoc) * * @see org.richfaces.component.behavior.ClientValidatorBehavior#getValidators(javax.faces.component.behavior. * ClientBehaviorContext) */ public Collection getValidators(ClientBehaviorContext context) { UIComponent component = context.getComponent(); if (component instanceof EditableValueHolder) { Collection validators = Lists.newArrayList(); EditableValueHolder input = (EditableValueHolder) component; Validator[] facesValidators = input.getValidators(); FacesContext facesContext = context.getFacesContext(); if (facesValidators.length > 0) { String validatorMessage = (String) component.getAttributes().get("validatorMessage"); boolean beanValidatorsProcessed = false; FacesValidatorService facesValidatorService = ServiceTracker.getService(facesContext, FacesValidatorService.class); for (Validator validator : facesValidators) { if (validator instanceof BeanValidator || validator instanceof FacesBeanValidator) { ValueExpression valueExpression = component.getValueExpression(VALUE); if (null != valueExpression && !beanValidatorsProcessed) { BeanValidatorService beanValidatorService = ServiceTracker.getService(facesContext, BeanValidatorService.class); validators.addAll(beanValidatorService.getConstrains(facesContext, valueExpression, validatorMessage, getGroups())); beanValidatorsProcessed = true; } } else { validators.add(facesValidatorService.getValidatorDescription(facesContext, input, validator, validatorMessage)); } } } return validators; } else { throw new FacesException("Component " + component.getClass().getName() + " does not implement EditableValueHolder interface"); } } public Class[] getGroups() { if (groups != null) { return groups; } ValueExpression expression = getValueExpression("groups"); if (expression != null) { FacesContext ctx = FacesContext.getCurrentInstance(); return (Class[]) expression.getValue(ctx.getELContext()); } return EMPTY_GROUPS; } public void setGroups(Class... groups) { this.groups = groups; clearInitialState(); } public String getAjaxScript(ClientBehaviorContext context) { return getRenderer(context.getFacesContext(), BEHAVIOR_ID).getScript(context, this); } @Override public Object saveState(FacesContext context) { if (context == null) { throw new NullPointerException(); } Object[] values; Object superState = super.saveState(context); if (initialStateMarked()) { if (superState == null) { values = null; } else { values = new Object[] { superState }; } } else { values = new Object[2]; values[0] = superState; values[1] = groups; } return values; } @Override public void restoreState(FacesContext context, Object state) { if (context == null) { throw new NullPointerException(); } if (state != null) { Object[] values = (Object[]) state; super.restoreState(context, values[0]); if (values.length != 1) { groups = (Class[]) values[1]; // If we saved state last time, save state again next time. clearInitialState(); } } } /* * (non-Javadoc) * * @see org.richfaces.component.behavior.ClientValidatorBehavior#isImmediateSet() */ public boolean isImmediateSet() { // TODO - implement this method in RichFaces AjaxBehavior return false; } // Disable processing for any component except validated input. @Override public boolean isLimitRender() { return true; } @Override public boolean isBypassUpdates() { return true; } @Override public Collection getExecute() { return THIS; } @Override public Collection getRender() { return NONE; } @Attribute public String getOnvalid() { return (String) getStateHelper().eval(Properties.onvalid); } public void setOnvalid(String value) { getStateHelper().put(Properties.onvalid, value); } @Attribute public String getOninvalid() { return (String) getStateHelper().eval(Properties.oninvalid); } public void setOninvalid(String value) { getStateHelper().put(Properties.oninvalid, value); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy