001 // Copyright 2010 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 package org.apache.tapestry5.internal.beanvalidator; 015 016 import static java.lang.String.format; 017 018 import java.lang.annotation.Annotation; 019 import java.util.Iterator; 020 import java.util.Set; 021 022 import javax.validation.ConstraintViolation; 023 import javax.validation.MessageInterpolator; 024 import javax.validation.Validator; 025 import javax.validation.ValidatorFactory; 026 import javax.validation.MessageInterpolator.Context; 027 import javax.validation.metadata.BeanDescriptor; 028 import javax.validation.metadata.ConstraintDescriptor; 029 import javax.validation.metadata.PropertyDescriptor; 030 031 import org.apache.tapestry5.Field; 032 import org.apache.tapestry5.FieldValidator; 033 import org.apache.tapestry5.MarkupWriter; 034 import org.apache.tapestry5.ValidationException; 035 import org.apache.tapestry5.beanvalidator.BeanValidatorGroupSource; 036 import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptor; 037 import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptorSource; 038 import org.apache.tapestry5.internal.BeanValidationContext; 039 import org.apache.tapestry5.json.JSONObject; 040 import org.apache.tapestry5.services.Environment; 041 import org.apache.tapestry5.services.FormSupport; 042 043 044 public class BeanFieldValidator implements FieldValidator 045 { 046 private final Field field; 047 private final ValidatorFactory validatorFactory; 048 private final BeanValidatorGroupSource beanValidationGroupSource; 049 private final ClientConstraintDescriptorSource clientValidatorSource; 050 private final FormSupport formSupport; 051 private final Environment environment; 052 053 public BeanFieldValidator(Field field, 054 ValidatorFactory validatorFactory, 055 BeanValidatorGroupSource beanValidationGroupSource, 056 ClientConstraintDescriptorSource clientValidatorSource, 057 FormSupport formSupport, 058 Environment environment) 059 { 060 this.field = field; 061 this.validatorFactory = validatorFactory; 062 this.beanValidationGroupSource = beanValidationGroupSource; 063 this.clientValidatorSource = clientValidatorSource; 064 this.formSupport = formSupport; 065 this.environment = environment; 066 } 067 068 public boolean isRequired() 069 { 070 return false; 071 } 072 073 public void render(final MarkupWriter writer) 074 { 075 final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class); 076 077 if (beanValidationContext == null) 078 { 079 return; 080 } 081 082 final Validator validator = validatorFactory.getValidator(); 083 084 BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType()); 085 086 String currentProperty = beanValidationContext.getCurrentProperty(); 087 088 if(currentProperty == null) return; 089 090 PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty); 091 092 if(propertyDescriptor == null) return; 093 094 for (final ConstraintDescriptor<?> descriptor :propertyDescriptor.getConstraintDescriptors()) 095 { 096 Class<? extends Annotation> annotationType = descriptor.getAnnotation().annotationType(); 097 098 ClientConstraintDescriptor clientConstraintDescriptor = clientValidatorSource.getConstraintDescriptor(annotationType); 099 100 if(clientConstraintDescriptor != null) 101 { 102 String message = format("%s %s", field.getLabel(), interpolateMessage(descriptor)); 103 104 JSONObject specs = new JSONObject(); 105 106 for (String attribute : clientConstraintDescriptor.getAttributes()) 107 { 108 Object object = descriptor.getAttributes().get(attribute); 109 110 if (object == null) 111 { 112 throw new RuntimeException("Expected attribute is null"); 113 } 114 specs.put(attribute, object); 115 } 116 117 formSupport.addValidation(field, clientConstraintDescriptor.getValidatorName(), message, specs); 118 } 119 } 120 } 121 122 @SuppressWarnings("unchecked") 123 public void validate(final Object value) throws ValidationException 124 { 125 126 final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class); 127 128 if (beanValidationContext == null) 129 { 130 return; 131 } 132 133 final Validator validator = validatorFactory.getValidator(); 134 135 String currentProperty = beanValidationContext.getCurrentProperty(); 136 137 if(currentProperty == null) return; 138 139 BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType()); 140 141 PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty); 142 143 if(propertyDescriptor == null) return; 144 145 final Set<ConstraintViolation<Object>> violations = validator.validateValue( 146 (Class<Object>) beanValidationContext.getBeanType(), currentProperty, 147 value, beanValidationGroupSource.get()); 148 149 if (violations.isEmpty()) 150 { 151 return; 152 } 153 154 final StringBuilder builder = new StringBuilder(); 155 156 for (Iterator iterator = violations.iterator(); iterator.hasNext();) 157 { 158 ConstraintViolation<?> violation = (ConstraintViolation<Object>) iterator.next(); 159 160 builder.append(format("%s %s", field.getLabel(), violation.getMessage())); 161 162 if(iterator.hasNext()) 163 builder.append(", "); 164 165 } 166 167 throw new ValidationException(builder.toString()); 168 169 } 170 171 private String interpolateMessage(final ConstraintDescriptor<?> descriptor) 172 { 173 String messageTemplate = (String) descriptor.getAttributes().get("message"); 174 175 MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); 176 177 return messageInterpolator.interpolate(messageTemplate, new Context() 178 { 179 180 public ConstraintDescriptor<?> getConstraintDescriptor() 181 { 182 return descriptor; 183 } 184 185 public Object getValidatedValue() 186 { 187 return null; 188 } 189 }); 190 } 191 }