package gueei.binding.labs.validation; import gueei.binding.IObservable; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import android.content.Context; /** * ModelValidator changes to be non-static * [Enhancement]Model results now can retrieved by observable name * [Plan]Implement resource string retrieval (problem due to non-final id in new adt) * [Plan]Implement deferred (and probably Async) model validation * @author andy * */ public class ModelValidator { private final Context mContext; private final Object mModel; private final Class<?> mResClass; /** * Constructor * @param context Calling Context * @param model Model to validate * @param resClass Class of the resource, e.g. R.string */ public ModelValidator(Context context, Object model, Class<?> resClass){ mContext = context; mModel = model; mResClass = resClass; } @SuppressWarnings({ "unchecked", "rawtypes" }) public ValidationResult ValidateModel(){ ValidationResult result = new ValidationResult(); IAnnoatedPropertyContainer modelContainer; if (mModel instanceof IAnnoatedPropertyContainer){ modelContainer = (IAnnoatedPropertyContainer)mModel; }else{ modelContainer = new ReflectionAnnotatedPropertyContainer(mModel); } String[] observableNames = modelContainer.getAllObservableNames(); for(String obsName: observableNames){ try{ IObservable observable = modelContainer.getObservable(obsName); if (observable==null) continue; Annotation[] annotations = modelContainer.getAnnotations(obsName); for(Annotation annotation : annotations){ Method m = annotation.getClass().getMethod("Validator"); Class<?> validation = (Class<?>)m.invoke(annotation); ValidatorBase validator = (ValidatorBase)validation.getConstructor().newInstance(); if (!validator.Validate( mContext, observable.get(), validator.getAcceptedAnnotation().cast(annotation), mModel)){ result.putValidationError( obsName, validator.formatErrorMessage( mContext, validator.getAcceptedAnnotation().cast(annotation), obsName, getErrorMessage(annotation))); } validator.recycle(); } }catch(Exception e){ // ignore all errors continue; } } return result; } /** * Utility method to read error message res / message from validator annotation class * Since annotation does not allow inheritance, we have to do in a dirty way * * @param annotation The validation annotation object, * should contain ErrorMessageRes() and/or ErrorMessage() to work, otherwise, nothing will be returned * @return */ protected String getErrorMessage(Annotation annotation){ if (mResClass!=null){ try { // Resource name has highest priority String resName = annotation.getClass() .getMethod("ErrorMessageRes").invoke(annotation).toString(); if (resName.length()>0){ // We come a long way to achieve this.... since res id is no longer final... int resId = (Integer)mResClass.getField(resName).get(null); if (resId>0){ return mContext.getResources().getString(resId); } } } catch (Exception e) { e.printStackTrace(); } } try{ return annotation.getClass() .getMethod("ErrorMessage") .invoke(annotation) .toString(); }catch(Exception e){ return null; } } }