> violations,");
sw.println("Class>... groups) {");
sw.outdent();
// /// For each group
// TODO(nchalko) handle the sequence in the AbstractValidator
// See JSR 303 section 3.5
// all reachable fields
// all reachable getters (both) at once
// including all reachable and cascadable associations
sw.println("validateAllNonInheritedProperties(context, object, violations, groups);");
// validate super classes and super interfaces
writeValidateInheritance(sw, beanHelper.getClazz(), Stage.OBJECT, null, false, "groups");
writeClassLevelConstraintsValidation(sw, "groups");
// }
sw.outdent();
sw.println("}");
}
private void writeValidateConstraint(SourceWriter sw, PropertyDescriptor p,
Class> elementClass, ConstraintDescriptor> constraint,
String constraintDescriptorVar) throws UnableToCompleteException {
writeValidateConstraint(sw, p, elementClass, constraint,
constraintDescriptorVar, DEFAULT_VIOLATION_VAR);
}
/**
* Writes the call to actually validate a constraint, including its composite
* constraints.
*
* If the constraint is annotated as
* {@link javax.validation.ReportAsSingleViolation ReportAsSingleViolation},
* then is called recursively and the {@code violationsVar} is changed to
* match the {@code constraintDescriptorVar}.
*
* @param sw the Source Writer
* @param p the property
* @param elementClass The class of the Element
* @param constraint the constraint to validate.
* @param constraintDescriptorVar the name of the constraintDescriptor
* variable.
* @param violationsVar the name of the variable to hold violations
* @throws UnableToCompleteException
*/
private void writeValidateConstraint(SourceWriter sw, PropertyDescriptor p,
Class> elementClass, ConstraintDescriptor> constraint,
String constraintDescriptorVar, String violationsVar)
throws UnableToCompleteException {
boolean isComposite = !constraint.getComposingConstraints().isEmpty();
boolean firstReportAsSingleViolation =
constraint.isReportAsSingleViolation()
&& violationsVar.equals(DEFAULT_VIOLATION_VAR) && isComposite;
boolean reportAsSingleViolation = firstReportAsSingleViolation
|| !violationsVar.equals(DEFAULT_VIOLATION_VAR);
boolean hasValidator = !constraint.getConstraintValidatorClasses()
.isEmpty();
String compositeViolationsVar = constraintDescriptorVar + "_violations";
// Only do this the first time in a constraint composition.
if (firstReportAsSingleViolation) {
// Report myConstraint as Single Violation
sw.print("// Report ");
sw.print(constraint.getAnnotation().annotationType().getCanonicalName());
sw.println(" as Single Violation");
writeNewViolations(sw, compositeViolationsVar);
}
if (hasValidator) {
Class extends ConstraintValidator extends Annotation, ?>> validatorClass;
try {
validatorClass = getValidatorForType(constraint, elementClass);
} catch (UnexpectedTypeException e) {
throw error(logger, e);
}
if (firstReportAsSingleViolation) {
// if (!
sw.println("if (!");
sw.indent();
sw.indent();
}
// validate(myContext, violations object, value, new MyValidator(),
// constraintDescriptor, groups));
sw.print("validate(myContext, ");
sw.print(violationsVar);
sw.print(", object, value, ");
sw.print("new ");
sw.print(validatorClass.getCanonicalName());
sw.print("(), ");
sw.print(constraintDescriptorVar);
sw.print(", groups)");
if (firstReportAsSingleViolation) {
// ) {
sw.println(") {");
sw.outdent();
} else if (!reportAsSingleViolation) {
// ;
sw.println(";");
} else if (isComposite) {
// ||
sw.println(" ||");
}
} else if (!isComposite) {
// TODO(nchalko) What does the spec say to do here.
logger.log(TreeLogger.WARN, "No ConstraintValidator of " + constraint + " for "
+ p.getPropertyName() + " of type " + elementClass);
}
if (firstReportAsSingleViolation) {
// if (
sw.print("if (");
sw.indent();
sw.indent();
}
int count = 0;
for (ConstraintDescriptor> compositeConstraint : constraint
.getComposingConstraints()) {
String compositeVar = constraintDescriptorVar + "_" + count++;
writeValidateConstraint(sw, p, elementClass, compositeConstraint,
compositeVar, firstReportAsSingleViolation ? compositeViolationsVar
: violationsVar);
if (!reportAsSingleViolation) {
// ;
sw.println(";");
} else {
// ||
sw.println(" ||");
}
}
if (isComposite && reportAsSingleViolation) {
// false
sw.print("false");
}
if (firstReportAsSingleViolation) {
// ) {
sw.println(" ) {");
sw.outdent();
// addSingleViolation(myContext, violations, object, value,
// constraintDescriptor);
sw.print("addSingleViolation(myContext, violations, object, value, ");
sw.print(constraintDescriptorVar);
sw.println(");");
// }
sw.outdent();
sw.println("}");
if (hasValidator) {
// }
sw.outdent();
sw.println("}");
}
}
}
private void writeValidateFieldCall(SourceWriter sw, PropertyDescriptor p,
boolean useValue, boolean honorValid) {
String propertyName = p.getPropertyName();
// validateProperty_<>(context,
sw.print(validateMethodFieldName(p));
sw.print("(context, ");
sw.print("violations, ");
// null, (MyType) value,
// or
// object, object.getLastName(),
if (useValue) {
sw.print("null, ");
sw.print("(");
sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(p,
true)));
sw.print(") value");
} else {
sw.print("object, ");
JField field = beanType.getField(propertyName);
if (field.isPublic()) {
sw.print("object.");
sw.print(propertyName);
} else {
fieldsToWrap.add(field);
sw.print(toWrapperName(field) + "(object)");
}
}
sw.print(", ");
// honorValid, groups);
sw.print(Boolean.toString(honorValid));
sw.println(", groups);");
}
private void writeValidateGetterCall(SourceWriter sw, PropertyDescriptor p,
boolean useValue, boolean honorValid) {
// validateProperty_get<>(context, violations,
sw.print(validateMethodGetterName(p));
sw.print("(context, ");
sw.print("violations, ");
// object, object.getMyProp(),
// or
// null, (MyType) value,
if (useValue) {
sw.print("null, ");
sw.print("(");
sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(p,
false)));
sw.print(") value");
} else {
sw.print("object, ");
JMethod method = beanType.findMethod(asGetter(p), NO_ARGS);
if (method.isPublic()) {
sw.print("object.");
sw.print(asGetter(p));
sw.print("()");
} else {
gettersToWrap.add(method);
sw.print(toWrapperName(method) + "(object)");
}
}
sw.print(", ");
// honorValid, groups);
sw.print(Boolean.toString(honorValid));
sw.println(", groups);");
}
private void writeValidateInheritance(SourceWriter sw, Class> clazz, Stage stage,
PropertyDescriptor property) throws UnableToCompleteException {
writeValidateInheritance(sw, clazz, stage, property, false, "groups");
}
private void writeValidateInheritance(SourceWriter sw, Class> clazz, Stage stage,
PropertyDescriptor property, boolean expandDefaultGroupSequence, String groupsVarName)
throws UnableToCompleteException {
writeValidateInterfaces(sw, clazz, stage, property, expandDefaultGroupSequence, groupsVarName);
Class> superClass = clazz.getSuperclass();
if (superClass != null) {
writeValidatorCall(sw, superClass, stage, property, expandDefaultGroupSequence, groupsVarName);
}
}
private void writeValidateInterfaces(SourceWriter sw, Class> clazz,
Stage stage, PropertyDescriptor p, boolean expandDefaultGroupSequence, String groupsVarName)
throws UnableToCompleteException {
for (Class> type : clazz.getInterfaces()) {
writeValidatorCall(sw, type, stage, p, expandDefaultGroupSequence, groupsVarName);
writeValidateInterfaces(sw, type, stage, p, expandDefaultGroupSequence, groupsVarName);
}
}
private void writeValidateIterable(SourceWriter sw, PropertyDescriptor p) {
// int i = 0;
sw.println("int i = 0;");
// for (Object instance : value) {
sw.println("for(Object instance : value) {");
sw.indent();
// if(instance != null && !context.alreadyValidated(instance)) {
sw.println(" if(instance != null && !context.alreadyValidated(instance)) {");
sw.indent();
// violations.addAll(
sw.println("violations.addAll(");
sw.indent();
sw.indent();
// context.getValidator().validate(
sw.println("context.getValidator().validate(");
sw.indent();
sw.indent();
Class> elementClass = p.getElementClass();
if (elementClass.isArray() || List.class.isAssignableFrom(elementClass)) {
// context.appendIndex("myProperty",i++),
sw.print("context.appendIndex(\"");
sw.print(p.getPropertyName());
sw.println("\",i),");
} else {
// context.appendIterable("myProperty"),
sw.print("context.appendIterable(\"");
sw.print(p.getPropertyName());
sw.println("\"),");
}
// instance, groups));
sw.println("instance, groups));");
sw.outdent();
sw.outdent();
sw.outdent();
sw.outdent();
// }
sw.outdent();
sw.println("}");
// i++;
sw.println("i++;");
// }
sw.outdent();
sw.println("}");
}
private void writeValidateMap(SourceWriter sw, PropertyDescriptor p) {
// for (Entry, Type> entry : value.entrySet()) {
sw.print("for(");
sw.print(Entry.class.getCanonicalName());
sw.println(", ?> entry : value.entrySet()) {");
sw.indent();
// if(entry.getValue() != null &&
// !context.alreadyValidated(entry.getValue())) {
sw.println(" if(entry.getValue() != null && !context.alreadyValidated(entry.getValue())) {");
sw.indent();
// violations.addAll(
sw.println("violations.addAll(");
sw.indent();
sw.indent();
// context.getValidator().validate(
sw.println("context.getValidator().validate(");
sw.indent();
sw.indent();
// context.appendKey("myProperty",entry.getKey()),
sw.print("context.appendKey(\"");
sw.print(p.getPropertyName());
sw.println("\",entry.getKey()),");
// entry.getValue(), groups));
sw.println("entry.getValue(), groups));");
sw.outdent();
sw.outdent();
sw.outdent();
sw.outdent();
// }
sw.outdent();
sw.println("}");
// }
sw.outdent();
sw.println("}");
}
private void writeValidatePropertyCall(SourceWriter sw,
PropertyDescriptor property, boolean useValue, boolean honorValid) {
if (useValue) {
// boolean valueTypeMatches = false;
sw.println("boolean valueTypeMatches = false;");
}
if (beanHelper.hasGetter(property)) {
if (useValue) {
// if ( value == null || value instanceof propertyType) {
sw.print("if ( value == null || value instanceof ");
sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(
property, false)));
sw.println(") {");
sw.indent();
// valueTypeMatches = true;
sw.println("valueTypeMatches = true;");
}
// validate_getMyProperty
writeValidateGetterCall(sw, property, useValue, honorValid);
if (useValue) {
// }
sw.outdent();
sw.println("}");
}
}
if (beanHelper.hasField(property)) {
if (useValue) {
// if ( value == null || value instanceof propertyType) {
sw.print("if ( value == null || value instanceof ");
sw.print(getQualifiedSourceNonPrimitiveType(beanHelper.getElementType(
property, true)));
sw.println(") {");
sw.indent();
// valueTypeMatches = true;
sw.println("valueTypeMatches = true;");
}
// validate_myProperty
writeValidateFieldCall(sw, property, useValue, honorValid);
if (useValue) {
// } else
sw.outdent();
sw.println("}");
}
}
if (useValue & (beanHelper.hasGetter(property) || beanHelper.hasField(property))) {
// if(!valueTypeMatches) {
sw.println("if(!valueTypeMatches) {");
sw.indent();
// throw new ValidationException(value.getClass +
// " is not a valid type for " + propertyName);
sw.print("throw new ValidationException");
sw.println("(value.getClass() +\" is not a valid type for \"+ propertyName);");
// }
sw.outdent();
sw.println("}");
}
}
private void writeValidatePropertyGroups(SourceWriter sw)
throws UnableToCompleteException {
// public void validatePropertyGroups(
sw.println("public void validatePropertyGroups(");
// GwtValidationContext context, BeanType object, String propertyName,
// Set> violations, Class>... groups) throws ValidationException {
sw.indent();
sw.indent();
sw.println("GwtValidationContext context,");
sw.println(beanHelper.getTypeCanonicalName() + " object,");
sw.println("String propertyName,");
sw.println("Set> violations,");
sw.println("Class>... groups) throws ValidationException {");
sw.outdent();
for (PropertyDescriptor property : beanHelper.getBeanDescriptor().getConstrainedProperties()) {
// if (propertyName.equals(myPropety)) {
sw.print("if (propertyName.equals(\"");
sw.print(property.getPropertyName());
sw.println("\")) {");
sw.indent();
writeValidatePropertyCall(sw, property, false, false);
// validate all super classes and interfaces
writeValidateInheritance(sw, beanHelper.getClazz(), Stage.PROPERTY,
property);
// }
sw.outdent();
sw.print("} else ");
}
writeIfPropertyNameNotFound(sw);
// }
sw.outdent();
sw.println("}");
}
private void writeValidatePropertyMethod(SourceWriter sw,
PropertyDescriptor p, boolean useField) throws UnableToCompleteException {
Class> elementClass = p.getElementClass();
JType elementType = beanHelper.getElementType(p, useField);
// private final void validateProperty_{get}(
sw.print("private final void ");
if (useField) {
sw.print(validateMethodFieldName(p));
} else {
sw.print(validateMethodGetterName(p));
}
sw.println("(");
sw.indent();
sw.indent();
// final GwtValidationContext context,
sw.println("final GwtValidationContext context,");
// final Set> violations,
sw.println("final Set> violations,");
// BeanType object,
sw.println(beanHelper.getTypeCanonicalName() + " object,");
// final value,
sw.print("final ");
sw.print(elementType.getParameterizedQualifiedSourceName());
sw.println(" value,");
// boolean honorValid,
sw.println("boolean honorValid,");
// Class>... groups) {
sw.println("Class>... groups) {");
sw.outdent();
// only write the checks if the property is constrained in some way
if (isPropertyConstrained(p, useField)) {
// context = context.append("myProperty");
sw.print("final GwtValidationContext myContext = context.append(\"");
sw.print(p.getPropertyName());
sw.println("\");");
// only check this property if the TraversableResolver says we can
// Node leafNode = myContext.getPath().getLeafNode();
sw.println("Node leafNode = myContext.getPath().getLeafNode();");
// PathImpl path = myContext.getPath().getPathWithoutLeafNode();
sw.println("PathImpl path = myContext.getPath().getPathWithoutLeafNode();");
// boolean isReachable;
sw.println("boolean isReachable;");
// try {
sw.println("try {");
sw.indent();
// isReachable = myContext.getTraversableResolver().isReachable(object, leafNode,
// myContext.getRootBeanClass(), path, ElementType);
sw.println("isReachable = myContext.getTraversableResolver().isReachable(object, " +
"leafNode, myContext.getRootBeanClass(), path, " +
(useField ? asLiteral(ElementType.FIELD) : asLiteral(ElementType.METHOD)) + ");");
// } catch (Exception e) {
sw.outdent();
sw.println("} catch (Exception e) {");
sw.indent();
// throw new ValidationException("TraversableResolver isReachable caused an exception", e);
sw.println("throw new ValidationException(\"TraversableResolver isReachable caused an " +
"exception\", e);");
// }
sw.outdent();
sw.println("}");
// if (isReachable) {
sw.println("if (isReachable) {");
sw.indent();
// TODO(nchalko) move this out of here to the Validate method
if (p.isCascaded() && hasValid(p, useField)) {
// if (honorValid && value != null) {
sw.println("if (honorValid && value != null) {");
sw.indent();
// boolean isCascadable;
sw.println("boolean isCascadable;");
// try {
sw.println("try {");
sw.indent();
// isCascadable = myContext.getTraversableResolver().isCascadable(object, leafNode,
// myContext.getRootBeanClass(), path, ElementType)
sw.println("isCascadable = myContext.getTraversableResolver().isCascadable(object, " +
"leafNode, myContext.getRootBeanClass(), path, " +
(useField ? asLiteral(ElementType.FIELD) : asLiteral(ElementType.METHOD)) + ");");
// } catch (Exception e) {
sw.outdent();
sw.println("} catch (Exception e) {");
sw.indent();
// throw new ValidationException("TraversableResolver isReachable caused an exception", e);
sw.println("throw new ValidationException(\"TraversableResolver isCascadable caused an " +
"exception\", e);");
// }
sw.outdent();
sw.println("}");
// if (isCascadable) {
sw.println("if (isCascadable) {");
sw.indent();
if (isIterableOrMap(elementClass)) {
JClassType associationType = beanHelper.getAssociationType(p,
useField);
createBeanHelper(associationType);
if (Map.class.isAssignableFrom(elementClass)) {
writeValidateMap(sw, p);
} else {
writeValidateIterable(sw, p);
}
} else {
createBeanHelper(elementClass);
// if (!context.alreadyValidated(value)) {
sw.println(" if (!context.alreadyValidated(value)) {");
sw.indent();
// violations.addAll(myContext.getValidator().validate(context, value,
// groups));
sw.print("violations.addAll(");
sw.println("myContext.getValidator().validate(myContext, value, groups));");
// }
sw.outdent();
sw.println("}");
}
// }
sw.outdent();
sw.println("}");
// }
sw.outdent();
sw.println("}");
}
// It is possible for an annotation with the exact same values to be set on
// both the field and the getter.
// Keep track of the ones we have used to make sure we don't duplicate.
Set includedAnnotations = Sets.newHashSet();
int count = 0;
for (ConstraintDescriptor> constraint : p.getConstraintDescriptors()) {
if (areConstraintDescriptorGroupsValid(constraint)) {
Object annotation = constraint.getAnnotation();
if (hasMatchingAnnotation(p, useField, constraint)) {
String constraintDescriptorVar = constraintDescriptorVar(p.getPropertyName(), count);
if (!includedAnnotations.contains(annotation)) {
if (useField) {
writeValidateConstraint(sw, p, elementClass, constraint, constraintDescriptorVar);
} else {
// The annotation hasn't been looked at twice (yet) and we are validating a getter
// Write the call if only the getter has this constraint applied to it
boolean hasField = beanHelper.hasField(p);
if (!hasField ||
(hasField && !hasMatchingAnnotation(p, true, constraint))) {
writeValidateConstraint(sw, p, elementClass, constraint, constraintDescriptorVar);
}
}
} else {
// The annotation has been looked at once already during this validate property call
// so we know the field and the getter are both annotated with the same constraint.
if (!useField) {
writeValidateConstraint(sw, p, elementClass, constraint, constraintDescriptorVar);
}
}
includedAnnotations.add(annotation);
}
count++;
}
}
// }
sw.outdent();
sw.println("}");
}
sw.outdent();
sw.println("}");
}
private void writeValidateValueGroups(SourceWriter sw)
throws UnableToCompleteException {
// public void validateValueGroups(
sw.println("public void validateValueGroups(");
// GwtValidationContext context, Class beanType, String propertyName,
// Object value, Set> violations, Class>... groups) {
sw.indent();
sw.indent();
sw.println("GwtValidationContext context,");
sw.println("Class<" + beanHelper.getTypeCanonicalName() + "> beanType,");
sw.println("String propertyName,");
sw.println("Object value,");
sw.println("Set> violations,");
sw.println("Class>... groups) {");
sw.outdent();
for (PropertyDescriptor property :
beanHelper.getBeanDescriptor().getConstrainedProperties()) {
// if (propertyName.equals(myPropety)) {
sw.print("if (propertyName.equals(\"");
sw.print(property.getPropertyName());
sw.println("\")) {");
sw.indent();
if (!isIterableOrMap(property.getElementClass())) {
writeValidatePropertyCall(sw, property, true, false);
}
// validate all super classes and interfaces
writeValidateInheritance(sw, beanHelper.getClazz(),
Stage.VALUE, property);
// }
sw.outdent();
sw.print("} else ");
}
writeIfPropertyNameNotFound(sw);
sw.outdent();
sw.println("}");
}
/**
* @param p Only used if writing a call to validate a property - otherwise can be null.
* @param expandDefaultGroupSequence Only used if writing a call to validate a bean.
* @param groupsVarName The name of the variable containing the groups.
*/
private void writeValidatorCall(SourceWriter sw, Class> type, Stage stage,
PropertyDescriptor p, boolean expandDefaultGroupSequence, String groupsVarName)
throws UnableToCompleteException {
if (cache.isClassConstrained(type) && !isIterableOrMap(type)) {
BeanHelper helper = createBeanHelper(type);
beansToValidate.add(helper);
switch (stage) {
case OBJECT:
// myValidator
sw.print(helper.getValidatorInstanceName());
if (expandDefaultGroupSequence) {
// .expandDefaultAndValidateClassGroups(context,object,violations,groups);
sw.println(".expandDefaultAndValidateClassGroups(context, object, violations, " +
groupsVarName + ");");
} else {
// .validateClassGroups(context,object,violations,groups);
sw.println(".validateClassGroups(context, object, violations, " + groupsVarName + ");");
}
break;
case PROPERTY:
if (isPropertyConstrained(helper, p)) {
// myValidator.validatePropertyGroups(context,object
// ,propertyName, violations, groups);
sw.print(helper.getValidatorInstanceName());
sw.print(".validatePropertyGroups(context, object,");
sw.println(" propertyName, violations, " + groupsVarName + ");");
}
break;
case VALUE:
if (isPropertyConstrained(helper, p)) {
// myValidator.validateValueGroups(context,beanType
// ,propertyName, value, violations, groups);
sw.print(helper.getValidatorInstanceName());
sw.print(".validateValueGroups(context, ");
sw.print(helper.getTypeCanonicalName());
sw.println(".class, propertyName, value, violations, " + groupsVarName + ");");
}
break;
default:
throw new IllegalStateException();
}
}
}
private void writeWrappers(SourceWriter sw) {
sw.println("// Write the wrappers after we know which are needed");
for (JField field : fieldsToWrap) {
writeFieldWrapperMethod(sw, field);
sw.println();
}
for (JMethod method : gettersToWrap) {
writeGetterWrapperMethod(sw, method);
sw.println();
}
}
}