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 }