I am in the process of writing some validation code based on these assumptions:
I had this same problem and I found the visitor pattern to be really effective at decoupling the validation logic from the data object. You'll need to instrument your data class hierarchy with accept( visitor ) methods, but if you're building everything that's simple enough. Even if you're using a third-party hierarchy without visitor support, you can create wrappers that provide the accept traversal tree and that's pretty close to having the method inside the class.
To perform the different validation you implement a different validator class and pass it to the accept method on the root object. I was also able to create other utility visitors around the model which allowed me to create a generator visitor that filled in all the fields with sample/random data. I went a little visitor crazy on it because I was so excited. You can probably tell I'm still excited about it, especially having the change to tell someone else about it.
One size does not fit all! Make it simple!
Provide validators with common methods/interface to output data, categorize warnings, filter/process warnings raised more than once. Do not create any sophisticated way of validation itself, at least not before writing a few real life validators.
Move out of the way and let the validators do what they are supposed to do:
for validator in all_validators:
validator.validate(model)
If you're doing any sort of of GUI work, you should take a look at JGoodies Validation: http://www.jgoodies.com/downloads/libraries.html (also some articles here: www.jgoodies.com/articles/).
I would create a validator for any class that needs validation. You can actually create more than one validator if you need different ways of validating, e.g. strict or not. You can group common functionality and methods into classes like AbstractValidator and ValidationResult (which may have a list of errors, severity, etc.).
Be wary of over-design. Try starting with something simple like:
new UserValidator().validate(user)
or to validate a view:
new UserPanelValidator().validate(userPanel)
It does depend on your architecture though. For example, if you automatically propagate input from the view to the domain, then you don't need to do as much validation at the view level.
I think I am doing the same thing right now.
The pattern that applies here is the Filter pattern and the Filter Chain.
Each filter validates against one "way" (as you call them).
First for syntax, second for Db lookups etc (from your second bullet).