blob: 7918fa30c1fc8c672d414e673787c543ae27b7a2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.myfaces.extensions.validator.beanval.interceptor;
import org.apache.myfaces.extensions.validator.core.interceptor.ValidationInterceptor;
import org.apache.myfaces.extensions.validator.core.property.PropertyInformation;
import org.apache.myfaces.extensions.validator.core.property.PropertyDetails;
import org.apache.myfaces.extensions.validator.core.property.PropertyInformationKeys;
import org.apache.myfaces.extensions.validator.core.metadata.MetaDataEntry;
import org.apache.myfaces.extensions.validator.core.el.ELHelper;
import org.apache.myfaces.extensions.validator.core.el.ValueBindingExpression;
import org.apache.myfaces.extensions.validator.util.ExtValUtils;
import org.apache.myfaces.extensions.validator.util.ReflectionUtils;
import org.apache.myfaces.extensions.validator.beanval.validation.ModelValidationEntry;
import org.apache.myfaces.extensions.validator.beanval.ExtValBeanValidationContext;
import org.apache.myfaces.extensions.validator.beanval.annotation.extractor.DefaultGroupControllerScanningExtractor;
import org.apache.myfaces.extensions.validator.beanval.annotation.BeanValidation;
import org.apache.myfaces.extensions.validator.beanval.annotation.ModelValidation;
import org.apache.myfaces.extensions.validator.beanval.annotation.NoRestrictionGroup;
import org.apache.myfaces.extensions.validator.internal.ToDo;
import org.apache.myfaces.extensions.validator.internal.Priority;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.faces.context.FacesContext;
import javax.faces.component.UIComponent;
import javax.validation.groups.Default;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.lang.reflect.Method;
/**
* @author Gerhard Petracek
* @since 1.x.3
*/
public class ValidationGroupProvider implements ValidationInterceptor
{
protected final Log logger = LogFactory.getLog(getClass());
public boolean beforeValidation(FacesContext facesContext,
UIComponent uiComponent,
Object convertedObject,
Map<String, Object> properties)
{
if(properties.containsKey(PropertyInformation.class.getName()))
{
addMetaDataToContext(
(PropertyInformation)properties.get(PropertyInformation.class.getName()), uiComponent);
}
return true;
}
public void afterValidation(FacesContext facesContext,
UIComponent uiComponent,
Object convertedObject,
Map<String, Object> properties)
{
//not used
}
protected void addMetaDataToContext(PropertyInformation propertyInformation, UIComponent component)
{
PropertyDetails propertyDetails = propertyInformation
.getInformation(PropertyInformationKeys.PROPERTY_DETAILS, PropertyDetails.class);
String[] key = propertyDetails.getKey().split("\\.");
Object firstBean = ExtValUtils.getELHelper().getBean(key[0]);
List<Class> foundGroupsForPropertyValidation = new ArrayList<Class>();
List<Class> restrictedGroupsForPropertyValidation = new ArrayList<Class>();
List<ModelValidationEntry> modelValidationEntryList = new ArrayList<ModelValidationEntry>();
List<Class> restrictedGroupsForModelValidation = new ArrayList<Class>();
//extract bv-controller-annotation of
//first bean
processClass(firstBean,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
//first property
processFieldsAndProperties(key[0] + "." + key[1],
firstBean,
key[1],
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
//base object (of target property)
processClass(propertyDetails.getBaseObject(),
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
//last property
processFieldsAndProperties(
propertyDetails.getKey(),
propertyDetails.getBaseObject(),
propertyDetails.getProperty(),
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
ExtValBeanValidationContext extValBeanValidationContext = ExtValBeanValidationContext.getCurrentInstance();
String currentViewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();
String clientId = component.getClientId(FacesContext.getCurrentInstance());
processFoundGroups(extValBeanValidationContext, currentViewId, clientId,
foundGroupsForPropertyValidation);
processRestrictedGroups(extValBeanValidationContext, currentViewId, clientId,
restrictedGroupsForPropertyValidation);
initModelValidation(extValBeanValidationContext, currentViewId, component, propertyDetails,
modelValidationEntryList, restrictedGroupsForModelValidation);
}
private void processClass(Object objectToInspect,
List<Class> foundGroupsForPropertyValidation,
List<Class> restrictedGroupsForPropertyValidation,
List<ModelValidationEntry> modelValidationEntryList,
List<Class> restrictedGroupsForModelValidation)
{
Class classToInspect = objectToInspect.getClass();
while (!Object.class.getName().equals(classToInspect.getName()))
{
transferGroupValidationInformationToFoundGroups(objectToInspect,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
processInterfaces(objectToInspect.getClass(), objectToInspect,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
classToInspect = classToInspect.getSuperclass();
}
}
private void processFieldsAndProperties(String key,
Object base,
String property, List<Class> foundGroupsForPropertyValidation,
List<Class> restrictedGroupsForPropertyValidation,
List<ModelValidationEntry> modelValidationEntryList,
List<Class> restrictedGroupsForModelValidation)
{
PropertyInformation propertyInformation = new DefaultGroupControllerScanningExtractor()
.extract(FacesContext.getCurrentInstance(), new PropertyDetails(key, base, property));
for (MetaDataEntry metaDataEntry : propertyInformation.getMetaDataEntries())
{
if (metaDataEntry.getValue() instanceof BeanValidation)
{
processMetaData((BeanValidation) metaDataEntry.getValue(),
base,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
}
else if(metaDataEntry.getValue() instanceof BeanValidation.List)
{
for(BeanValidation currentBeanValidation : ((BeanValidation.List)metaDataEntry.getValue()).value())
{
processMetaData(currentBeanValidation,
base,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
}
}
}
}
protected void processFoundGroups(ExtValBeanValidationContext extValBeanValidationContext,
String currentViewId,
String clientId,
List<Class> foundGroupsForPropertyValidation)
{
/*
* add found groups to context
*/
for (Class currentGroupClass : foundGroupsForPropertyValidation)
{
extValBeanValidationContext.addGroup(currentGroupClass, currentViewId, clientId);
}
}
protected void processRestrictedGroups(ExtValBeanValidationContext extValBeanValidationContext,
String currentViewId,
String clientId,
List<Class> restrictedGroupsForPropertyValidation)
{
/*
* add restricted groups
*/
for (Class currentGroupClass : restrictedGroupsForPropertyValidation)
{
extValBeanValidationContext.restrictGroup(currentGroupClass, currentViewId, clientId);
}
}
protected void initModelValidation(ExtValBeanValidationContext extValBeanValidationContext,
String currentViewId,
UIComponent component,
PropertyDetails propertyDetails,
List<ModelValidationEntry> modelValidationEntryList,
List<Class> restrictedGroupsForModelValidation)
{
/*
* add model validation entry list
*/
for(ModelValidationEntry modelValidationEntry : modelValidationEntryList)
{
if(!"true".equalsIgnoreCase(org.apache.myfaces.extensions.validator.beanval.WebXmlParameter
.DEACTIVATE_IMPLICIT_DEFAULT_GROUP_VALIDATION))
{
modelValidationEntry.addGroup(Default.class);
}
for(Class restrictedGroup : restrictedGroupsForModelValidation)
{
modelValidationEntry.removeGroup(restrictedGroup);
}
if(modelValidationEntry.getGroups().length > 0)
{
addTargetsForModelValidation(modelValidationEntry, propertyDetails.getBaseObject());
extValBeanValidationContext.addModelValidationEntry(modelValidationEntry, currentViewId, component);
}
}
}
private void transferGroupValidationInformationToFoundGroups(Object objectToInspect,
List<Class> foundGroupsForPropertyValidation,
List<Class> restrictedGroupsForPropertyValidation,
List<ModelValidationEntry> modelValidationEntryList,
List<Class> restrictedGroupsForModelValidation)
{
if (objectToInspect.getClass().isAnnotationPresent(BeanValidation.class))
{
processMetaData(objectToInspect.getClass().getAnnotation(BeanValidation.class),
objectToInspect,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
}
else if (objectToInspect.getClass().isAnnotationPresent(BeanValidation.List.class))
{
for(BeanValidation currentBeanValidation :
(objectToInspect.getClass().getAnnotation(BeanValidation.List.class)).value())
{
processMetaData(currentBeanValidation,
objectToInspect,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
}
}
}
private void processInterfaces(Class currentClass,
Object metaDataSourceObject,
List<Class> foundGroupsForPropertyValidation,
List<Class> restrictedGroupsForPropertyValidation,
List<ModelValidationEntry> modelValidationEntryList,
List<Class> restrictedGroupsForModelValidation)
{
for (Class currentInterface : currentClass.getInterfaces())
{
transferGroupValidationInformationToFoundGroups(metaDataSourceObject,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
processInterfaces(currentInterface, metaDataSourceObject,
foundGroupsForPropertyValidation,
restrictedGroupsForPropertyValidation,
modelValidationEntryList,
restrictedGroupsForModelValidation);
}
}
protected void processMetaData(BeanValidation beanValidation,
Object metaDataSourceObject,
List<Class> foundGroupsForPropertyValidation,
List<Class> restrictedGroupsForPropertyValidation,
List<ModelValidationEntry> modelValidationEntryList,
List<Class> restrictedGroupsForModelValidation)
{
for (String currentViewId : beanValidation.viewIds())
{
if ((currentViewId.equals(FacesContext.getCurrentInstance().getViewRoot().getViewId()) ||
currentViewId.equals("*")) && isValidationPermitted(beanValidation))
{
if(isModelValidation(beanValidation))
{
addModelValidationEntry(
beanValidation, metaDataSourceObject,
modelValidationEntryList, restrictedGroupsForModelValidation);
}
else
{
processGroups(
beanValidation, foundGroupsForPropertyValidation, restrictedGroupsForPropertyValidation);
}
return;
}
}
}
private void addTargetsForModelValidation(ModelValidationEntry modelValidationEntry, Object defaultTarget)
{
if(modelValidationEntry.getMetaData().validationTargets().length == 1 &&
modelValidationEntry.getMetaData().validationTargets()[0].equals(ModelValidation.DEFAULT_TARGET))
{
modelValidationEntry.addValidationTarget(defaultTarget);
}
else
{
Object target;
for(String modelValidationTarget : modelValidationEntry.getMetaData().validationTargets())
{
target = resolveTarget(modelValidationEntry.getMetaDataSourceObject(), modelValidationTarget);
if(target == null && this.logger.isErrorEnabled())
{
this.logger.error("target unreachable - source class: " +
modelValidationEntry.getMetaDataSourceObject().getClass().getName() +
" target to resolve: " + modelValidationTarget);
}
modelValidationEntry.addValidationTarget(target);
}
}
}
private boolean isValidationPermitted(BeanValidation beanValidation)
{
ELHelper elHelper = ExtValUtils.getELHelper();
for(String condition : beanValidation.conditions())
{
if(elHelper.isELTermWellFormed(condition) &&
elHelper.isELTermValid(FacesContext.getCurrentInstance(), condition))
{
if(Boolean.TRUE.equals(
elHelper.getValueOfExpression(
FacesContext.getCurrentInstance(), new ValueBindingExpression(condition))))
{
return true;
}
}
else
{
if(this.logger.isErrorEnabled())
{
this.logger.error("an invalid condition is used: " + condition);
}
}
}
return false;
}
private boolean isModelValidation(BeanValidation beanValidation)
{
return beanValidation.modelValidation().isActive();
}
private void addModelValidationEntry(BeanValidation beanValidation,
Object metaDataSourceObject,
List<ModelValidationEntry> modelValidationEntryList,
List<Class> restrictedGroupsForModelValidation)
{
ModelValidationEntry modelValidationEntry = new ModelValidationEntry();
modelValidationEntry.setGroups(Arrays.asList(beanValidation.useGroups()));
modelValidationEntry.setMetaData(beanValidation.modelValidation());
modelValidationEntry.setMetaDataSourceObject(metaDataSourceObject);
if(!(beanValidation.restrictGroups().length == 1 &&
beanValidation.restrictGroups()[0].equals(NoRestrictionGroup.class)))
{
restrictedGroupsForModelValidation.addAll(Arrays.asList(beanValidation.restrictGroups()));
}
modelValidationEntryList.add(modelValidationEntry);
}
private void processGroups(BeanValidation beanValidation,
List<Class> foundGroupsForPropertyValidation,
List<Class> restrictedGroupsForPropertyValidation)
{
foundGroupsForPropertyValidation.addAll(Arrays.asList(beanValidation.useGroups()));
if(!(beanValidation.restrictGroups().length == 1 &&
beanValidation.restrictGroups()[0].equals(NoRestrictionGroup.class)))
{
restrictedGroupsForPropertyValidation.addAll(Arrays.asList(beanValidation.restrictGroups()));
}
}
private Object resolveTarget(Object metaDataSourceObject, String modelValidationTarget)
{
ELHelper elHelper = ExtValUtils.getELHelper();
if(elHelper.isELTermWellFormed(modelValidationTarget))
{
if(elHelper.isELTermValid(FacesContext.getCurrentInstance(), modelValidationTarget))
{
return elHelper.getValueOfExpression(
FacesContext.getCurrentInstance(), new ValueBindingExpression(modelValidationTarget));
}
else
{
if(this.logger.isErrorEnabled())
{
this.logger.error("an invalid binding is used: " + modelValidationTarget);
}
}
}
String[] properties = modelValidationTarget.split("\\.");
Object result = metaDataSourceObject;
for(String property : properties)
{
result = getValueOfProperty(result, property);
if(result == null)
{
return null;
}
}
return result;
}
@ToDo(value = Priority.HIGH, description = "move to util class - the original method is in LocalCompareStrategy")
protected Object getValueOfProperty(Object base, String property)
{
property = property.substring(0,1).toUpperCase() + property.substring(1, property.length());
Method targetMethod = ReflectionUtils.tryToGetMethod(base.getClass(), "get" + property);
if(targetMethod == null)
{
targetMethod = ReflectionUtils.tryToGetMethod(base.getClass(), "is" + property);
}
if(targetMethod == null)
{
throw new IllegalStateException(
"class " + base.getClass() + " has no public get/is " + property.toLowerCase());
}
return ReflectionUtils.tryToInvokeMethod(base, targetMethod);
}
}