Save This Page
Home » hibernate-validator-src-20081106 » org.hibernate » validator » [javadoc | source]
    1   //$Id: ClassValidator.java 15133 2008-08-20 10:05:57Z hardy.ferentschik $
    2   package org.hibernate.validator;
    3   
    4   import java.beans.Introspector;
    5   import java.io.IOException;
    6   import java.io.ObjectInputStream;
    7   import java.io.ObjectOutputStream;
    8   import java.io.Serializable;
    9   import java.lang.annotation.Annotation;
   10   import java.lang.reflect.Method;
   11   import java.lang.reflect.Modifier;
   12   import java.text.MessageFormat;
   13   import java.util.ArrayList;
   14   import java.util.Collection;
   15   import java.util.HashMap;
   16   import java.util.HashSet;
   17   import java.util.Iterator;
   18   import java.util.List;
   19   import java.util.Locale;
   20   import java.util.Map;
   21   import java.util.MissingResourceException;
   22   import java.util.ResourceBundle;
   23   import java.util.Set;
   24   import java.util.StringTokenizer;
   25   
   26   import org.hibernate.AssertionFailure;
   27   import org.hibernate.Hibernate;
   28   import org.hibernate.MappingException;
   29   import org.hibernate.mapping.PersistentClass;
   30   import org.hibernate.mapping.Property;
   31   import org.hibernate.mapping.Component;
   32   import org.hibernate.annotations.common.reflection.Filter;
   33   import org.hibernate.annotations.common.reflection.ReflectionManager;
   34   import org.hibernate.annotations.common.reflection.XClass;
   35   import org.hibernate.annotations.common.reflection.XMember;
   36   import org.hibernate.annotations.common.reflection.XMethod;
   37   import org.hibernate.annotations.common.reflection.XProperty;
   38   import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
   39   import org.hibernate.util.IdentitySet;
   40   import org.hibernate.validator.interpolator.DefaultMessageInterpolatorAggregator;
   41   import org.slf4j.Logger;
   42   import org.slf4j.LoggerFactory;
   43   
   44   
   45   /**
   46    * Engine that take a bean and check every expressed annotation restrictions
   47    *
   48    * @author Gavin King
   49    * @author Emmanuel Bernard
   50    */
   51   public class ClassValidator<T> implements Serializable {
   52   	//TODO Define magic number
   53   	private static final Logger log = LoggerFactory.getLogger( ClassValidator.class );
   54   	private static final InvalidValue[] EMPTY_INVALID_VALUE_ARRAY = new InvalidValue[]{};
   55   	private static final String DEFAULT_VALIDATOR_MESSAGE = "org.hibernate.validator.resources.DefaultValidatorMessages";
   56   	private static final String VALIDATOR_MESSAGE = "ValidatorMessages";
   57   	private static final Set<Class> INDEXABLE_CLASS = new HashSet<Class>();
   58   
   59   	static {
   60   		INDEXABLE_CLASS.add( Integer.class );
   61   		INDEXABLE_CLASS.add( Long.class );
   62   		INDEXABLE_CLASS.add( String.class );
   63   	}
   64   
   65   	static {
   66   		Version.touch(); //touch version
   67   	}
   68   
   69   	private final Class<T> beanClass;
   70   	private transient ResourceBundle messageBundle;
   71   	private transient ResourceBundle defaultMessageBundle;
   72   	private transient boolean isUserProvidedResourceBundle;
   73   	private transient ReflectionManager reflectionManager;
   74   
   75   	private final transient Map<XClass, ClassValidator> childClassValidators;
   76   	private transient List<Validator> beanValidators;
   77   	private transient List<Validator> memberValidators;
   78   	private transient List<XMember> memberGetters;
   79   	private transient List<XMember> childGetters;
   80   	private transient DefaultMessageInterpolatorAggregator defaultInterpolator;
   81   	private transient MessageInterpolator userInterpolator;
   82   	private static final Filter GET_ALL_FILTER = new Filter() {
   83   		public boolean returnStatic() {
   84   		return true;
   85   		}
   86   
   87   		public boolean returnTransient() {
   88   		return true;
   89   		}
   90   	};
   91   
   92   	/**
   93   	 * create the validator engine for this bean type
   94   	 */
   95   	public ClassValidator(Class<T> beanClass) {
   96   		this( beanClass, (ResourceBundle) null );
   97   	}
   98   
   99   	/**
  100   	 * create the validator engine for a particular bean class, using a resource bundle
  101   	 * for message rendering on violation
  102   	 */
  103   	public ClassValidator(Class<T> beanClass, ResourceBundle resourceBundle) {
  104   		this( beanClass, resourceBundle, null, new HashMap<XClass, ClassValidator>(), null );
  105   	}
  106   
  107   	/**
  108   	 * create the validator engine for a particular bean class, using a custom message interpolator
  109   	 * for message rendering on violation
  110   	 */
  111   	public ClassValidator(Class<T> beanClass, MessageInterpolator interpolator) {
  112   		this( beanClass, null, interpolator, new HashMap<XClass, ClassValidator>(), null );
  113   	}
  114   
  115       /**
  116        * Not a public API
  117        */
  118       public ClassValidator(
  119   			Class<T> beanClass, ResourceBundle resourceBundle, MessageInterpolator interpolator,
  120               Map<XClass, ClassValidator> childClassValidators, ReflectionManager reflectionManager
  121       ) {
  122           this.reflectionManager = reflectionManager != null ? reflectionManager : new JavaReflectionManager();
  123           XClass beanXClass = this.reflectionManager.toXClass( beanClass );
  124   		this.beanClass = beanClass;
  125   		this.messageBundle = resourceBundle == null ?
  126   				getDefaultResourceBundle() :
  127   				resourceBundle;
  128   		this.defaultMessageBundle = ResourceBundle.getBundle( DEFAULT_VALIDATOR_MESSAGE );
  129   		this.userInterpolator = interpolator;
  130   		this.childClassValidators = childClassValidators != null ?
  131                   childClassValidators :
  132                   new HashMap<XClass, ClassValidator>();
  133   		initValidator( beanXClass, this.childClassValidators );
  134   	}
  135   
  136   	@SuppressWarnings("unchecked")
  137   	protected ClassValidator(
  138   			XClass beanXClass, ResourceBundle resourceBundle, MessageInterpolator userInterpolator,
  139   			Map<XClass, ClassValidator> childClassValidators, ReflectionManager reflectionManager
  140   	) {
  141   		this.reflectionManager = reflectionManager;
  142   		this.beanClass = reflectionManager.toClass( beanXClass );
  143   		this.messageBundle = resourceBundle == null ?
  144   				getDefaultResourceBundle() :
  145   				resourceBundle;
  146   		this.defaultMessageBundle = ResourceBundle.getBundle( DEFAULT_VALIDATOR_MESSAGE );
  147   		this.userInterpolator = userInterpolator;
  148   		this.childClassValidators = childClassValidators;
  149   		initValidator( beanXClass, childClassValidators );
  150   	}
  151   
  152   	private ResourceBundle getDefaultResourceBundle() {
  153   		ResourceBundle rb;
  154   		try {
  155   			//use context class loader as a first citizen
  156   			ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
  157   			if ( contextClassLoader == null ) {
  158   				throw new MissingResourceException( "No context classloader", null, VALIDATOR_MESSAGE );
  159   			}
  160   			rb = ResourceBundle.getBundle(
  161   					VALIDATOR_MESSAGE,
  162   					Locale.getDefault(),
  163   					contextClassLoader
  164   			);
  165   		}
  166   		catch (MissingResourceException e) {
  167   			log.trace( "ResourceBundle {} not found in thread context classloader", VALIDATOR_MESSAGE );
  168   			//then use the Validator Framework classloader
  169   			try {
  170   				rb = ResourceBundle.getBundle(
  171   						VALIDATOR_MESSAGE,
  172   						Locale.getDefault(),
  173   						this.getClass().getClassLoader()
  174   				);
  175   			}
  176   			catch (MissingResourceException ee) {
  177   				log.debug(
  178   						"ResourceBundle ValidatorMessages not found in Validator classloader. Delegate to {}",
  179   						DEFAULT_VALIDATOR_MESSAGE
  180   				);
  181   				//the user did not override the default ValidatorMessages
  182   				rb = null;
  183   			}
  184   		}
  185   		isUserProvidedResourceBundle = true;
  186   		return rb;
  187   	}
  188   
  189   	private void initValidator(
  190   			XClass xClass, Map<XClass, ClassValidator> childClassValidators
  191   	) {
  192   		beanValidators = new ArrayList<Validator>();
  193   		memberValidators = new ArrayList<Validator>();
  194   		memberGetters = new ArrayList<XMember>();
  195   		childGetters = new ArrayList<XMember>();
  196   		defaultInterpolator = new DefaultMessageInterpolatorAggregator();
  197   		defaultInterpolator.initialize( messageBundle, defaultMessageBundle );
  198   
  199   		//build the class hierarchy to look for members in
  200   		childClassValidators.put( xClass, this );
  201   		Collection<XClass> classes = new HashSet<XClass>();
  202   		addSuperClassesAndInterfaces( xClass, classes );
  203   		for ( XClass currentClass : classes ) {
  204   			Annotation[] classAnnotations = currentClass.getAnnotations();
  205   			for ( int i = 0; i < classAnnotations.length ; i++ ) {
  206   				Annotation classAnnotation = classAnnotations[i];
  207   				Validator beanValidator = createValidator( classAnnotation );
  208   				if ( beanValidator != null ) beanValidators.add( beanValidator );
  209   				handleAggregateAnnotations(classAnnotation, null);
  210   			}
  211   		}
  212   
  213   		//Check on all selected classes
  214   		for ( XClass currClass : classes ) {
  215   			List<XMethod> methods = currClass.getDeclaredMethods();
  216   			for ( XMethod method : methods ) {
  217   				createMemberValidator( method );
  218   				createChildValidator( method );
  219   			}
  220   
  221   			List<XProperty> fields = currClass.getDeclaredProperties(
  222   					"field", GET_ALL_FILTER
  223   			);
  224   			for ( XProperty field : fields ) {
  225   				createMemberValidator( field );
  226   				createChildValidator( field );
  227   			}
  228   		}
  229   	}
  230   
  231   	private void addSuperClassesAndInterfaces(XClass clazz, Collection<XClass> classes) {
  232   		for ( XClass currClass = clazz; currClass != null ; currClass = currClass.getSuperclass() ) {
  233   			if ( ! classes.add( currClass ) ) return;
  234   			XClass[] interfaces = currClass.getInterfaces();
  235   			for ( XClass interf : interfaces ) {
  236   				addSuperClassesAndInterfaces( interf, classes );
  237   			}
  238   		}
  239   	}
  240   
  241   	private boolean handleAggregateAnnotations(Annotation annotation, XMember member) {
  242   		Object[] values;
  243   		try {
  244   			Method valueMethod = annotation.getClass().getMethod( "value" );
  245   			if ( valueMethod.getReturnType().isArray() ) {
  246   				values = (Object[]) valueMethod.invoke( annotation );
  247   			}
  248   			else {
  249   				return false;
  250   			}
  251   		}
  252   		catch (NoSuchMethodException e) {
  253   			return false;
  254   		}
  255   		catch (Exception e) {
  256   			throw new IllegalStateException( e );
  257   		}
  258   
  259   		boolean validatorPresent = false;
  260   		for ( Object value : values ) {
  261   			if ( value instanceof Annotation ) {
  262   				annotation = (Annotation) value;
  263   				Validator validator = createValidator( annotation );
  264   				if ( validator != null ) {
  265   					if ( member != null ) {
  266   						//member
  267   						memberValidators.add( validator );
  268   						setAccessible( member );
  269   						memberGetters.add( member );
  270   					}
  271   					else {
  272   						//bean
  273   						beanValidators.add( validator );
  274   					}
  275   					validatorPresent = true;
  276   				}
  277   			}
  278   		}
  279   		return validatorPresent;
  280   	}
  281   
  282   	@SuppressWarnings("unchecked")
  283   	private void createChildValidator( XMember member) {
  284   		if ( member.isAnnotationPresent( Valid.class ) ) {
  285   			setAccessible( member );
  286   			childGetters.add( member );
  287   			XClass clazz;
  288   			if ( member.isCollection() || member.isArray() ) {
  289   				clazz = member.getElementClass();
  290   			}
  291   			else {
  292   				clazz = member.getType();
  293   			}
  294   			if ( !childClassValidators.containsKey( clazz ) ) {
  295   				//ClassValidator added by side effect (added to childClassValidators during CV construction)
  296   				new ClassValidator( clazz, messageBundle, userInterpolator, childClassValidators, reflectionManager );
  297   			}
  298   		}
  299   	}
  300   
  301   	private void createMemberValidator(XMember member) {
  302   		boolean validatorPresent = false;
  303   		Annotation[] memberAnnotations = member.getAnnotations();
  304   		for ( Annotation methodAnnotation : memberAnnotations ) {
  305   			Validator propertyValidator = createValidator( methodAnnotation );
  306   			if ( propertyValidator != null ) {
  307   				memberValidators.add( propertyValidator );
  308   				setAccessible( member );
  309   				memberGetters.add( member );
  310   				validatorPresent = true;
  311   			}
  312   			boolean agrValidPresent = handleAggregateAnnotations( methodAnnotation, member );
  313   			validatorPresent = validatorPresent || agrValidPresent;
  314   		}
  315   		if ( validatorPresent && !member.isTypeResolved() ) {
  316   			log.warn( "Original type of property {} is unbound and has been approximated.", member );
  317   		}
  318   	}
  319   
  320   	private static void setAccessible(XMember member) {
  321   		if ( !Modifier.isPublic( member.getModifiers() ) ) {
  322   			member.setAccessible( true );
  323   		}
  324   	}
  325   
  326   	@SuppressWarnings("unchecked")
  327   	private Validator createValidator(Annotation annotation) {
  328   		try {
  329   			ValidatorClass validatorClass = annotation.annotationType().getAnnotation( ValidatorClass.class );
  330   			if ( validatorClass == null ) {
  331   				return null;
  332   			}
  333   			Validator beanValidator = validatorClass.value().newInstance();
  334   			beanValidator.initialize( annotation );
  335   			defaultInterpolator.addInterpolator( annotation, beanValidator );
  336   			return beanValidator;
  337   		}
  338   		catch (Exception e) {
  339   			throw new IllegalArgumentException( "could not instantiate ClassValidator", e );
  340   		}
  341   	}
  342   
  343   	public boolean hasValidationRules() {
  344   		return beanValidators.size() != 0 || memberValidators.size() != 0;
  345   	}
  346   
  347   	/**
  348   	 * apply constraints on a bean instance and return all the failures.
  349   	 * if <code>bean</code> is null, an empty array is returned
  350   	 */
  351   	public InvalidValue[] getInvalidValues(T bean) {
  352   		return this.getInvalidValues( bean, new IdentitySet() );
  353   	}
  354   
  355   	/**
  356   	 * apply constraints on a bean instance and return all the failures.
  357   	 * if <code>bean</code> is null, an empty array is returned
  358   	 */
  359   	@SuppressWarnings("unchecked")
  360   	protected InvalidValue[] getInvalidValues(T bean, Set<Object> circularityState) {
  361   		if ( bean == null || circularityState.contains( bean ) ) {
  362   			return EMPTY_INVALID_VALUE_ARRAY; //Avoid circularity
  363   		}
  364   		else {
  365   			circularityState.add( bean );
  366   		}
  367   
  368   		if ( !beanClass.isInstance( bean ) ) {
  369   			throw new IllegalArgumentException( "not an instance of: " + bean.getClass() );
  370   		}
  371   
  372   		List<InvalidValue> results = new ArrayList<InvalidValue>();
  373   
  374   		for ( int i = 0; i < beanValidators.size() ; i++ ) {
  375   			Validator validator = beanValidators.get( i );
  376   			if ( !validator.isValid( bean ) ) {
  377   				results.add( new InvalidValue( interpolate(validator), beanClass, null, bean, bean ) );
  378   			}
  379   		}
  380   
  381   		for ( int i = 0; i < memberValidators.size() ; i++ ) {
  382   			XMember getter = memberGetters.get( i );
  383   			if ( Hibernate.isPropertyInitialized( bean, getPropertyName( getter ) ) ) {
  384   				Object value = getMemberValue( bean, getter );
  385   				Validator validator = memberValidators.get( i );
  386   				if ( !validator.isValid( value ) ) {
  387   					String propertyName = getPropertyName( getter );
  388   					results.add( new InvalidValue( interpolate(validator), beanClass, propertyName, value, bean ) );
  389   				}
  390   			}
  391   		}
  392   
  393   		for ( int i = 0; i < childGetters.size() ; i++ ) {
  394   			XMember getter = childGetters.get( i );
  395   			if ( Hibernate.isPropertyInitialized( bean, getPropertyName( getter ) ) ) {
  396   				Object value = getMemberValue( bean, getter );
  397   				if ( value != null && Hibernate.isInitialized( value ) ) {
  398   					String propertyName = getPropertyName( getter );
  399   					if ( getter.isCollection() ) {
  400   						int index = 0;
  401   						boolean isIterable = value instanceof Iterable;
  402   						Map map = ! isIterable ? (Map) value : null;
  403   						Iterable elements = isIterable ?
  404   								(Iterable) value :
  405   								map.keySet();
  406   						for ( Object element : elements ) {
  407   							Object actualElement = isIterable ? element : map.get( element );
  408   							if ( actualElement == null ) {
  409   								index++;
  410   								continue;
  411   							}
  412   							InvalidValue[] invalidValues = getClassValidator( actualElement )
  413   									.getInvalidValues( actualElement, circularityState );
  414   
  415   							String indexedPropName = MessageFormat.format(
  416   									"{0}[{1}]",
  417   									propertyName,
  418   									INDEXABLE_CLASS.contains( element.getClass() ) ?
  419   											( "'" + element + "'" ) :
  420   											index
  421   							);
  422   							index++;
  423   
  424   							for ( InvalidValue invalidValue : invalidValues ) {
  425   								invalidValue.addParentBean( bean, indexedPropName );
  426   								results.add( invalidValue );
  427   							}
  428   						}
  429   					}
  430   					if ( getter.isArray() ) {
  431   						int index = 0;
  432   						for ( Object element : (Object[]) value ) {
  433   							if ( element == null ) {
  434   								index++;
  435   								continue;
  436   							}
  437   							InvalidValue[] invalidValues = getClassValidator( element )
  438   									.getInvalidValues( element, circularityState );
  439   
  440   							String indexedPropName = MessageFormat.format(
  441   									"{0}[{1}]",
  442   									propertyName,
  443   									index
  444   							);
  445   							index++;
  446   
  447   							for ( InvalidValue invalidValue : invalidValues ) {
  448   								invalidValue.addParentBean( bean, indexedPropName );
  449   								results.add( invalidValue );
  450   							}
  451   						}
  452   					}
  453   					else {
  454   						InvalidValue[] invalidValues = getClassValidator( value )
  455   								.getInvalidValues( value, circularityState );
  456   						for ( InvalidValue invalidValue : invalidValues ) {
  457   							invalidValue.addParentBean( bean, propertyName );
  458   							results.add( invalidValue );
  459   						}
  460   					}
  461   				}
  462   			}
  463   		}
  464   
  465   		return results.toArray( new InvalidValue[results.size()] );
  466   	}
  467   
  468   	private String interpolate(Validator validator) {
  469   		String message = defaultInterpolator.getAnnotationMessage( validator );
  470   		if (userInterpolator != null) {
  471   			return userInterpolator.interpolate( message, validator, defaultInterpolator );
  472   		}
  473   		else {
  474   			return defaultInterpolator.interpolate( message, validator, null);
  475   		}
  476   	}
  477   
  478   	@SuppressWarnings("unchecked")
  479   	private ClassValidator getClassValidator(Object value) {
  480   		Class clazz = value.getClass();
  481   		ClassValidator validator = childClassValidators.get( reflectionManager.toXClass( clazz ) );
  482   		if ( validator == null ) { //handles polymorphism
  483   			//TODO cache this thing. in a second queue (reflectionManager being sealed)? beware of concurrency
  484   			validator = new ClassValidator( clazz );
  485   		}
  486   		return validator;
  487   	}
  488   
  489   	/**
  490   	 * Apply constraints of a particular property on a bean instance and return all the failures.
  491   	 * Note this is not recursive.
  492   	 */
  493   	//TODO should it be recursive?
  494   	public InvalidValue[] getInvalidValues(T bean, String propertyName) {
  495   		List<InvalidValue> results = new ArrayList<InvalidValue>();
  496   
  497   		for ( int i = 0; i < memberValidators.size() ; i++ ) {
  498   			XMember getter = memberGetters.get( i );
  499   			if ( getPropertyName( getter ).equals( propertyName ) ) {
  500   				Object value = getMemberValue( bean, getter );
  501   				Validator validator = memberValidators.get( i );
  502   				if ( !validator.isValid( value ) ) {
  503   					results.add( new InvalidValue( interpolate(validator), beanClass, propertyName, value, bean ) );
  504   				}
  505   			}
  506   		}
  507   
  508   		return results.toArray( new InvalidValue[results.size()] );
  509   	}
  510   
  511   	/**
  512   	 * Apply constraints of a particular property value of a bean type and return all the failures.
  513   	 * The InvalidValue objects returns return null for InvalidValue#getBean() and InvalidValue#getRootBean()
  514   	 * Note this is not recursive.
  515   	 */
  516   	//TODO should it be recursive?
  517   	public InvalidValue[] getPotentialInvalidValues(String propertyName, Object value) {
  518   		List<InvalidValue> results = new ArrayList<InvalidValue>();
  519   
  520   		for ( int i = 0; i < memberValidators.size() ; i++ ) {
  521   			XMember getter = memberGetters.get( i );
  522   			if ( getPropertyName( getter ).equals( propertyName ) ) {
  523   				Validator validator = memberValidators.get( i );
  524   				if ( !validator.isValid( value ) ) {
  525   					results.add( new InvalidValue( interpolate(validator), beanClass, propertyName, value, null ) );
  526   				}
  527   			}
  528   		}
  529   
  530   		return results.toArray( new InvalidValue[results.size()] );
  531   	}
  532   
  533   	private Object getMemberValue(T bean, XMember getter) {
  534   		Object value;
  535   		try {
  536   			value = getter.invoke( bean );
  537   		}
  538   		catch (Exception e) {
  539   			throw new IllegalStateException( "Could not get property value", e );
  540   		}
  541   		return value;
  542   	}
  543   
  544   	private String getPropertyName(XMember member) {
  545   		//Do no try to cache the result in a map, it's actually much slower (2.x time)
  546   		String propertyName;
  547   		if ( XProperty.class.isAssignableFrom( member.getClass() ) ) {
  548   			propertyName = member.getName();
  549   		}
  550   		else if ( XMethod.class.isAssignableFrom( member.getClass() ) ) {
  551   			propertyName = member.getName();
  552   			if ( propertyName.startsWith( "is" ) ) {
  553   				propertyName = Introspector.decapitalize( propertyName.substring( 2 ) );
  554   			}
  555   			else if ( propertyName.startsWith( "get" ) ) {
  556   				propertyName = Introspector.decapitalize( propertyName.substring( 3 ) );
  557   			}
  558   			//do nothing for non getter method, in case someone want to validate a PO Method
  559   		}
  560   		else {
  561   			throw new AssertionFailure( "Unexpected member: " + member.getClass().getName() );
  562   		}
  563   		return propertyName;
  564   	}
  565   
  566   	/** @deprecated */
  567   	private String replace(String message, Annotation parameters) {
  568   		StringTokenizer tokens = new StringTokenizer( message, "#{}", true );
  569   		StringBuilder buf = new StringBuilder( 30 );
  570   		boolean escaped = false;
  571   		boolean el = false;
  572   		while ( tokens.hasMoreTokens() ) {
  573   			String token = tokens.nextToken();
  574   			if ( !escaped && "#".equals( token ) ) {
  575   				el = true;
  576   			}
  577   			if ( !el && "{".equals( token ) ) {
  578   				escaped = true;
  579   			}
  580   			else if ( escaped && "}".equals( token ) ) {
  581   				escaped = false;
  582   			}
  583   			else if ( !escaped ) {
  584   				if ( "{".equals( token ) ) el = false;
  585   				buf.append( token );
  586   			}
  587   			else {
  588   				Method member;
  589   				try {
  590   					member = parameters.getClass().getMethod( token, (Class[]) null );
  591   				}
  592   				catch (NoSuchMethodException nsfme) {
  593   					member = null;
  594   				}
  595   				if ( member != null ) {
  596   					try {
  597   						buf.append( member.invoke( parameters ) );
  598   					}
  599   					catch (Exception e) {
  600   						throw new IllegalArgumentException( "could not render message", e );
  601   					}
  602   				}
  603   				else {
  604   					String string = null;
  605   					try {
  606   						string = messageBundle != null ? messageBundle.getString( token ) : null;
  607   					}
  608   					catch( MissingResourceException e ) {
  609   						//give a second chance with the default resource bundle
  610   					}
  611   					if (string == null) {
  612   						try {
  613   							string = defaultMessageBundle.getString( token );
  614   						}
  615   						catch( MissingResourceException e) {
  616   							throw new MissingResourceException(
  617   									"Can't find resource in validator bundles, key " + token,
  618                                       defaultMessageBundle.getClass().getName(),
  619                                       token
  620   							);
  621   						}
  622   					}
  623   					if ( string != null ) buf.append( replace( string, parameters ) );
  624   				}
  625   			}
  626   		}
  627   		return buf.toString();
  628   	}
  629   
  630   	/**
  631   	 * apply the registred constraints rules on the hibernate metadata (to be applied on DB schema...)
  632   	 *
  633   	 * @param persistentClass hibernate metadata
  634   	 */
  635   	public void apply(PersistentClass persistentClass) {
  636   
  637   		for ( Validator validator : beanValidators ) {
  638   			if ( validator instanceof PersistentClassConstraint ) {
  639   				( (PersistentClassConstraint) validator ).apply( persistentClass );
  640   			}
  641   		}
  642   
  643   		Iterator<Validator> validators = memberValidators.iterator();
  644   		Iterator<XMember> getters = memberGetters.iterator();
  645   		while ( validators.hasNext() ) {
  646   			Validator validator = validators.next();
  647   			String propertyName = getPropertyName( getters.next() );
  648   			if ( validator instanceof PropertyConstraint ) {
  649   				try {
  650   					Property property = findPropertyByName(persistentClass, propertyName);
  651   					if (property != null) {
  652   						( (PropertyConstraint) validator ).apply( property );
  653   					}
  654   				}
  655   				catch (MappingException pnfe) {
  656   					//do nothing
  657   				}
  658   			}
  659   		}
  660   
  661   	}
  662   
  663   	public void assertValid(T bean) {
  664   		InvalidValue[] values = getInvalidValues( bean );
  665   		if ( values.length > 0 ) {
  666   			throw new InvalidStateException( values );
  667   		}
  668   	}
  669   
  670   	private void writeObject(ObjectOutputStream oos) throws IOException {
  671   		ResourceBundle rb = messageBundle;
  672   		MessageInterpolator interpolator = this.userInterpolator;
  673   		if ( rb != null && ! ( rb instanceof Serializable ) ) {
  674   			messageBundle = null;
  675   			if ( ! isUserProvidedResourceBundle ) {
  676   				log.warn(
  677   						"Serializing a ClassValidator with a non serializable ResourceBundle: ResourceBundle ignored"
  678   				);
  679   			}
  680   		}
  681   		if (interpolator != null && ! (interpolator instanceof Serializable) ) {
  682   			userInterpolator = null;
  683   			log.warn( "Serializing a non serializable MessageInterpolator" );
  684   		}
  685   		oos.defaultWriteObject();
  686   		oos.writeObject( messageBundle );
  687   		oos.writeObject( userInterpolator );
  688   		messageBundle = rb;
  689   		userInterpolator = interpolator;
  690   	}
  691   
  692   	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  693   		ois.defaultReadObject();
  694   		ResourceBundle rb = (ResourceBundle) ois.readObject();
  695   		if ( rb == null ) rb = getDefaultResourceBundle();
  696   		this.messageBundle = rb;
  697   		this.userInterpolator = (MessageInterpolator) ois.readObject();
  698   		this.defaultMessageBundle = ResourceBundle.getBundle( DEFAULT_VALIDATOR_MESSAGE );
  699   		reflectionManager = new JavaReflectionManager();
  700   		initValidator( reflectionManager.toXClass( beanClass ), new HashMap<XClass, ClassValidator>() );
  701   	}
  702   
  703   	/**
  704   	 * Retrieve the property by path in a recursive way, including IndetifierProperty in the loop
  705   	 * If propertyName is null or empty, the IdentifierProperty is returned
  706   	 */
  707   	public static Property findPropertyByName(PersistentClass associatedClass, String propertyName) {
  708   		Property property = null;
  709   		Property idProperty = associatedClass.getIdentifierProperty();
  710   		String idName = idProperty != null ? idProperty.getName() : null;
  711   		try {
  712   			if ( propertyName == null
  713   					|| propertyName.length() == 0
  714   					|| propertyName.equals( idName ) ) {
  715   				//default to id
  716   				property = idProperty;
  717   			}
  718   			else {
  719   				if ( propertyName.indexOf( idName + "." ) == 0 ) {
  720   					property = idProperty;
  721   					propertyName = propertyName.substring( idName.length() + 1 );
  722   				}
  723   				StringTokenizer st = new StringTokenizer( propertyName, ".", false );
  724   				while ( st.hasMoreElements() ) {
  725   					String element = (String) st.nextElement();
  726   					if ( property == null ) {
  727   						property = associatedClass.getProperty( element );
  728   					}
  729   					else {
  730   						if ( ! property.isComposite() ) return null;
  731   						property = ( (Component) property.getValue() ).getProperty( element );
  732   					}
  733   				}
  734   			}
  735   		}
  736   		catch (MappingException e) {
  737   			try {
  738   				//if we do not find it try to check the identifier mapper
  739   				if ( associatedClass.getIdentifierMapper() == null ) return null;
  740   				StringTokenizer st = new StringTokenizer( propertyName, ".", false );
  741   				while ( st.hasMoreElements() ) {
  742   					String element = (String) st.nextElement();
  743   					if ( property == null ) {
  744   						property = associatedClass.getIdentifierMapper().getProperty( element );
  745   					}
  746   					else {
  747   						if ( ! property.isComposite() ) return null;
  748   						property = ( (Component) property.getValue() ).getProperty( element );
  749   					}
  750   				}
  751   			}
  752   			catch (MappingException ee) {
  753   				return null;
  754   			}
  755   		}
  756   		return property;
  757   	}
  758   }

Save This Page
Home » hibernate-validator-src-20081106 » org.hibernate » validator » [javadoc | source]