/* * Copyright 2004-2005 the original author or authors. * * Licensed 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 grails.validation; import grails.util.GrailsNameUtils; import grails.util.GrailsStringUtils; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Locale; import java.util.Set; import org.springframework.beans.BeanWrapperImpl; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.validation.FieldError; /** * Abstract class for constraints to extend. * * @author Graeme Rocher * @deprecated Use {@link org.grails.datastore.gorm.validation.constraints.AbstractConstraint} instead */ @Deprecated public abstract class AbstractConstraint implements Constraint { protected String constraintPropertyName; protected Class<?> constraintOwningClass; protected Object constraintParameter; protected String classShortName; protected MessageSource messageSource; /* (non-Javadoc) * @see grails.validation.Constraint#setMessageSource(org.springframework.context.MessageSource) */ public void setMessageSource(MessageSource source) { messageSource = source; } public String getPropertyName() { return constraintPropertyName; } /** * {@inheritDoc} * @see grails.validation.Constraint#setOwningClass(java.lang.Class) */ @SuppressWarnings("rawtypes") public void setOwningClass(Class constraintOwningClass) { this.constraintOwningClass = constraintOwningClass; classShortName = GrailsNameUtils.getPropertyNameRepresentation(constraintOwningClass); } /** * @param constraintPropertyName The constraintPropertyName to set. */ public void setPropertyName(String constraintPropertyName) { this.constraintPropertyName = constraintPropertyName; } /** * @param constraintParameter The constraintParameter to set. */ public void setParameter(Object constraintParameter) { this.constraintParameter = constraintParameter; } public Object getParameter() { return constraintParameter; } protected void checkState() { Assert.hasLength(constraintPropertyName, "Property 'propertyName' must be set on the constraint"); Assert.notNull(constraintOwningClass, "Property 'owningClass' must be set on the constraint"); Assert.notNull(constraintParameter, "Property 'constraintParameter' must be set on the constraint"); } public void validate(Object target, Object propertyValue, Errors errors) { checkState(); // Skip null values if desired. if (propertyValue == null && skipNullValues()) { return; } // Skip blank values if desired. if (skipBlankValues() && propertyValue instanceof String && GrailsStringUtils.isBlank((String) propertyValue)) { return; } // Do the validation for this constraint. processValidate(target, propertyValue, errors); } protected boolean skipNullValues() { // a null is not a value we should even check in most cases return true; } protected boolean skipBlankValues() { // Most constraints ignore blank values, leaving it to the explicit "blank" constraint. return true; } public void rejectValue(Object target, Errors errors, String defaultMessageCode, Object[] args) { rejectValue(target, errors, defaultMessageCode, new String[] {}, args); } public void rejectValue(Object target,Errors errors, String defaultMessageCode, String code, Object[] args) { rejectValue(target,errors, defaultMessageCode, new String[] {code}, args); } public void rejectValue(Object target,Errors errors, String defaultMessageCode, String[] codes, Object[] args) { rejectValueWithDefaultMessage(target, errors, getDefaultMessage(defaultMessageCode), codes, args); } public void rejectValueWithDefaultMessage(Object target, Errors errors, String defaultMessage, String[] codes, Object[] args) { BindingResult result = (BindingResult) errors; Set<String> newCodes = new LinkedHashSet<String>(); if (args.length > 1 && messageSource != null) { if ((args[0] instanceof String) && (args[1] instanceof Class<?>)) { final Locale locale = LocaleContextHolder.getLocale(); final Class<?> constrainedClass = (Class<?>) args[1]; final String fullClassName = constrainedClass.getName(); String classNameCode = fullClassName + ".label"; String resolvedClassName = messageSource.getMessage(classNameCode, null, fullClassName, locale); final String classAsPropertyName = GrailsNameUtils.getPropertyName(constrainedClass); if (resolvedClassName.equals(fullClassName)) { // try short version classNameCode = classAsPropertyName+".label"; resolvedClassName = messageSource.getMessage(classNameCode, null, fullClassName, locale); } // update passed version if (!resolvedClassName.equals(fullClassName)) { args[1] = resolvedClassName; } String propertyName = (String)args[0]; String propertyNameCode = fullClassName + '.' + propertyName + ".label"; String resolvedPropertyName = messageSource.getMessage(propertyNameCode, null, propertyName, locale); if (resolvedPropertyName.equals(propertyName)) { propertyNameCode = classAsPropertyName + '.' + propertyName + ".label"; resolvedPropertyName = messageSource.getMessage(propertyNameCode, null, propertyName, locale); } // update passed version if (!resolvedPropertyName.equals(propertyName)) { args[0] = resolvedPropertyName; } } } //Qualified class name is added first to match before unqualified class (which is still resolved for backwards compatibility) newCodes.addAll(Arrays.asList(result.resolveMessageCodes(constraintOwningClass.getName() + '.' + constraintPropertyName + '.' + getName() + ".error", constraintPropertyName))); newCodes.addAll(Arrays.asList(result.resolveMessageCodes(classShortName + '.' + constraintPropertyName + '.' + getName() + ".error", constraintPropertyName))); for (String code : codes) { newCodes.addAll(Arrays.asList(result.resolveMessageCodes(constraintOwningClass.getName() + '.' + constraintPropertyName + '.' + code, constraintPropertyName))); newCodes.addAll(Arrays.asList(result.resolveMessageCodes(classShortName + '.' + constraintPropertyName + '.' + code, constraintPropertyName))); //We resolve the error code on it's own last so that a global code doesn't override a class/field specific error newCodes.addAll(Arrays.asList(result.resolveMessageCodes(code, constraintPropertyName))); } FieldError error = new FieldError( errors.getObjectName(), errors.getNestedPath() + constraintPropertyName, getPropertyValue(errors, target), false, newCodes.toArray(new String[newCodes.size()]), args, defaultMessage); ((BindingResult)errors).addError(error); } private Object getPropertyValue(Errors errors, Object target) { try { return errors.getFieldValue(constraintPropertyName); } catch (Exception nre) { int i = constraintPropertyName.lastIndexOf("."); String propertyName; if (i > -1) { propertyName = constraintPropertyName.substring(i, constraintPropertyName.length()); } else { propertyName = constraintPropertyName; } return new BeanWrapperImpl(target).getPropertyValue(propertyName); } } // For backward compatibility public void rejectValue(Object target, Errors errors, String code, String defaultMessage) { rejectValueWithDefaultMessage(target, errors, defaultMessage, new String[] {code}, null); } // For backward compatibility public void rejectValue(Object target, Errors errors, String code, Object[] args, String defaultMessage) { rejectValueWithDefaultMessage(target, errors, defaultMessage, new String[] {code}, args); } /** * Returns the default message for the given message code in the * current locale. Note that the string returned includes any * placeholders that the required message has - these must be * expanded by the caller if required. * @param code The i18n message code to look up. * @return The message corresponding to the given code in the * current locale. */ protected String getDefaultMessage(String code) { try { if (messageSource != null) { return messageSource.getMessage(code, null, LocaleContextHolder.getLocale()); } return ConstrainedProperty.DEFAULT_MESSAGES.get(code); } catch (Exception e) { return ConstrainedProperty.DEFAULT_MESSAGES.get(code); } } protected abstract void processValidate(Object target, Object propertyValue, Errors errors); @Override public String toString() { return new ToStringCreator(this).append(constraintParameter).toString(); } /** * Return whether the constraint is valid for the owning class * * @return true if it is */ public boolean isValid() { return true; } }