Skip to content

Commit f9e4ad1

Browse files
committed
TAP5-895: Tracking issue for Tapestry/JSR-303 integration
git-svn-id: https://svn.apache.org/repos/asf/tapestry/tapestry5/trunk@896767 13f79535-47bb-0310-9956-ffa450edef68
1 parent 1fc7f0e commit f9e4ad1

File tree

16 files changed

+595
-81
lines changed

16 files changed

+595
-81
lines changed

tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorModule.java

+45-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2009 The Apache Software Foundation
1+
// Copyright 2009, 2010 The Apache Software Foundation
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -13,16 +13,19 @@
1313
// limitations under the License.
1414
package org.apache.tapestry5.beanvalidator;
1515

16-
import java.util.Locale;
17-
1816
import javax.validation.MessageInterpolator;
1917
import javax.validation.Validator;
2018
import javax.validation.ValidatorFactory;
19+
import javax.validation.constraints.NotNull;
2120
import javax.validation.groups.Default;
2221

22+
import org.apache.tapestry5.Asset;
23+
import org.apache.tapestry5.MarkupWriter;
24+
import org.apache.tapestry5.RenderSupport;
2325
import org.apache.tapestry5.internal.beanvalidator.BeanFieldValidatorDefaultSource;
2426
import org.apache.tapestry5.internal.beanvalidator.BeanValidationGroupSourceImpl;
2527
import org.apache.tapestry5.internal.beanvalidator.BeanValidatorSourceImpl;
28+
import org.apache.tapestry5.internal.beanvalidator.ClientConstraintDescriptorImpl;
2629
import org.apache.tapestry5.internal.beanvalidator.MessageInterpolatorImpl;
2730
import org.apache.tapestry5.ioc.Configuration;
2831
import org.apache.tapestry5.ioc.MappedConfiguration;
@@ -31,7 +34,11 @@
3134
import org.apache.tapestry5.ioc.annotations.Local;
3235
import org.apache.tapestry5.ioc.services.PropertyShadowBuilder;
3336
import org.apache.tapestry5.ioc.services.ThreadLocale;
37+
import org.apache.tapestry5.services.AssetSource;
38+
import org.apache.tapestry5.services.Environment;
3439
import org.apache.tapestry5.services.FieldValidatorDefaultSource;
40+
import org.apache.tapestry5.services.MarkupRenderer;
41+
import org.apache.tapestry5.services.MarkupRendererFilter;
3542

3643
/**
3744
* Module for JSR-303 services.
@@ -46,6 +53,7 @@ public static void bind(final ServiceBinder binder)
4653
.withId("BeanFieldValidatorDefaultSource");
4754
binder.bind(BeanValidatorGroupSource.class, BeanValidationGroupSourceImpl.class);
4855
binder.bind(BeanValidatorSource.class, BeanValidatorSourceImpl.class);
56+
binder.bind(ClientConstraintDescriptorSource.class, ClientConstraintDescriptorImpl.class);
4957
}
5058

5159
public static void contributeServiceOverride(
@@ -85,5 +93,39 @@ public void configure(javax.validation.Configuration<?> configuration)
8593
}
8694
});
8795
}
96+
97+
public static void contributeClientConstraintDescriptorSource(
98+
final Configuration<ClientConstraintDescriptor> configuration)
99+
{
100+
configuration.add(new ClientConstraintDescriptor(NotNull.class, "notnull"));
101+
}
102+
103+
public void contributeMarkupRenderer(
104+
OrderedConfiguration<MarkupRendererFilter> configuration,
105+
106+
final AssetSource assetSource,
107+
108+
final ThreadLocale threadLocale,
109+
110+
final Environment environment)
111+
{
112+
MarkupRendererFilter injectBeanValidatorScript = new MarkupRendererFilter()
113+
{
114+
public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
115+
{
116+
RenderSupport renderSupport = environment.peek(RenderSupport.class);
117+
118+
Asset validators = assetSource.getAsset(null, "org/apache/tapestry5/beanvalidator/tapestry-beanvalidator.js",
119+
threadLocale.getLocale());
120+
121+
renderSupport.addScriptLink(validators);
122+
123+
renderer.renderMarkup(writer);
124+
}
125+
};
126+
127+
128+
configuration.add("BeanValidatorScript", injectBeanValidatorScript, "after:*");
129+
}
88130

89131
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2009 The Apache Software Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package org.apache.tapestry5.beanvalidator;
15+
16+
import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newSet;
17+
18+
import java.util.Set;
19+
20+
import org.apache.tapestry5.json.JSONObject;
21+
22+
/**
23+
* Describes a single client-side constraint.
24+
*
25+
*/
26+
public final class ClientConstraintDescriptor
27+
{
28+
private final Class annotationClass;
29+
private final String validatorName;
30+
private final Set<String> attributes;
31+
32+
public ClientConstraintDescriptor(final Class annotationClass,
33+
final String validatorName, final String... attributes)
34+
{
35+
this.annotationClass = annotationClass;
36+
this.validatorName = validatorName;
37+
this.attributes = newSet(attributes);
38+
}
39+
40+
/**
41+
* Returns the annotation describing the constraint declaration.
42+
*/
43+
public Class getAnnotationClass()
44+
{
45+
return this.annotationClass;
46+
}
47+
48+
/**
49+
* Returns the name of the client-side validator.
50+
*/
51+
public String getValidatorName()
52+
{
53+
return this.validatorName;
54+
}
55+
56+
/**
57+
* Returns a map containing the annotation attribute names as keys and the annotation attribute values as value.
58+
* This map is passed to the client-side validator as a {@link JSONObject}.
59+
*/
60+
public Set<String> getAttributes()
61+
{
62+
return this.attributes;
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2010 The Apache Software Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package org.apache.tapestry5.beanvalidator;
15+
16+
import org.apache.tapestry5.ioc.annotations.UsesConfiguration;
17+
18+
/**
19+
* Source for {@link ClientConstraintDescriptor}.
20+
*
21+
*/
22+
@UsesConfiguration(ClientConstraintDescriptor.class)
23+
public interface ClientConstraintDescriptorSource
24+
{
25+
/**
26+
* Return a {@link ClientConstraintDescriptor} for a constraint annotation or null.
27+
*
28+
* @param annotationClass type of the constraint annotation
29+
*/
30+
ClientConstraintDescriptor getConstraintDescriptor(Class annotationClass);
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright 2010 The Apache Software Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package org.apache.tapestry5.internal.beanvalidator;
15+
16+
import static java.lang.String.format;
17+
18+
import java.lang.annotation.Annotation;
19+
import java.util.Iterator;
20+
import java.util.Set;
21+
22+
import javax.validation.ConstraintViolation;
23+
import javax.validation.MessageInterpolator;
24+
import javax.validation.Validator;
25+
import javax.validation.ValidatorFactory;
26+
import javax.validation.MessageInterpolator.Context;
27+
import javax.validation.metadata.BeanDescriptor;
28+
import javax.validation.metadata.ConstraintDescriptor;
29+
import javax.validation.metadata.PropertyDescriptor;
30+
31+
import org.apache.tapestry5.Field;
32+
import org.apache.tapestry5.FieldValidator;
33+
import org.apache.tapestry5.MarkupWriter;
34+
import org.apache.tapestry5.ValidationException;
35+
import org.apache.tapestry5.beanvalidator.BeanValidatorGroupSource;
36+
import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptor;
37+
import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptorSource;
38+
import org.apache.tapestry5.json.JSONObject;
39+
import org.apache.tapestry5.services.BeanValidationContext;
40+
import org.apache.tapestry5.services.Environment;
41+
import org.apache.tapestry5.services.FormSupport;
42+
43+
44+
public class BeanFieldValidator implements FieldValidator
45+
{
46+
private final Field field;
47+
private final String propertyName;
48+
private final ValidatorFactory validatorFactory;
49+
private final BeanValidatorGroupSource beanValidationGroupSource;
50+
private final ClientConstraintDescriptorSource clientValidatorSource;
51+
private final FormSupport formSupport;
52+
private final Environment environment;
53+
54+
public BeanFieldValidator(Field field, String propertyName,
55+
ValidatorFactory validatorFactory,
56+
BeanValidatorGroupSource beanValidationGroupSource,
57+
ClientConstraintDescriptorSource clientValidatorSource,
58+
FormSupport formSupport,
59+
Environment environment)
60+
{
61+
this.field = field;
62+
this.propertyName = propertyName;
63+
this.validatorFactory = validatorFactory;
64+
this.beanValidationGroupSource = beanValidationGroupSource;
65+
this.clientValidatorSource = clientValidatorSource;
66+
this.formSupport = formSupport;
67+
this.environment = environment;
68+
}
69+
70+
public boolean isRequired()
71+
{
72+
return false;
73+
}
74+
75+
public void render(final MarkupWriter writer)
76+
{
77+
final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
78+
79+
if (beanValidationContext == null)
80+
{
81+
return;
82+
}
83+
84+
final Validator validator = validatorFactory.getValidator();
85+
86+
BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType());
87+
88+
PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(propertyName);
89+
90+
for (final ConstraintDescriptor<?> descriptor :propertyDescriptor.getConstraintDescriptors())
91+
{
92+
Class<? extends Annotation> annotationType = descriptor.getAnnotation().annotationType();
93+
94+
ClientConstraintDescriptor clientConstraintDescriptor = clientValidatorSource.getConstraintDescriptor(annotationType);
95+
96+
if(clientConstraintDescriptor != null)
97+
{
98+
String message = interpolateMessage(descriptor);
99+
100+
JSONObject specs = new JSONObject();
101+
102+
for (String attribute : clientConstraintDescriptor.getAttributes())
103+
{
104+
Object object = descriptor.getAttributes().get(attribute);
105+
106+
if (object == null)
107+
{
108+
throw new RuntimeException("Expected attribute is null");
109+
}
110+
specs.put(attribute, object);
111+
}
112+
113+
formSupport.addValidation(field, clientConstraintDescriptor.getValidatorName(), message, specs);
114+
}
115+
}
116+
}
117+
118+
@SuppressWarnings("unchecked")
119+
public void validate(final Object value) throws ValidationException
120+
{
121+
122+
final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
123+
124+
if (beanValidationContext == null)
125+
{
126+
return;
127+
}
128+
129+
final Validator validator = validatorFactory.getValidator();
130+
131+
final Set<ConstraintViolation<Object>> violations = validator.validateValue(
132+
(Class<Object>) beanValidationContext.getBeanType(), propertyName,
133+
value, beanValidationGroupSource.get());
134+
135+
if (violations.isEmpty())
136+
{
137+
return;
138+
}
139+
140+
final StringBuilder builder = new StringBuilder();
141+
142+
for (Iterator iterator = violations.iterator(); iterator.hasNext();)
143+
{
144+
ConstraintViolation<?> violation = (ConstraintViolation<Object>) iterator.next();
145+
146+
builder.append(format("%s %s", field.getLabel(), violation.getMessage()));
147+
148+
if(iterator.hasNext())
149+
builder.append(", ");
150+
151+
}
152+
153+
throw new ValidationException(builder.toString());
154+
155+
}
156+
157+
private String interpolateMessage(final ConstraintDescriptor<?> descriptor)
158+
{
159+
String messageTemplate = (String) descriptor.getAttributes().get("message");
160+
161+
MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator();
162+
163+
return messageInterpolator.interpolate(messageTemplate, new Context()
164+
{
165+
166+
public ConstraintDescriptor<?> getConstraintDescriptor()
167+
{
168+
return descriptor;
169+
}
170+
171+
public Object getValidatedValue()
172+
{
173+
return null;
174+
}
175+
});
176+
}
177+
}

0 commit comments

Comments
 (0)