zkbind/src/main/java/org/zkoss/bind/impl/ValidationHelper.java

Summary

Maintainability
D
2 days
Test Coverage
/* ValidationHelper.java

    Purpose:
        
    Description:
        
    History:
        2011/10/1 Created by Dennis Chen

Copyright (C) 2011 Potix Corporation. All Rights Reserved.
*/
package org.zkoss.bind.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.zkoss.bind.BindContext;
import org.zkoss.bind.Binder;
import org.zkoss.bind.Form;
import org.zkoss.bind.Property;
import org.zkoss.bind.proxy.FormProxyObject;
import org.zkoss.bind.sys.BinderCtrl;
import org.zkoss.bind.sys.Binding;
import org.zkoss.bind.sys.SaveBinding;
import org.zkoss.bind.sys.SaveFormBinding;
import org.zkoss.bind.sys.SavePropertyBinding;
import org.zkoss.bind.sys.ValidationMessages;
import org.zkoss.util.Pair;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;

/**
 * a internal stateless helper to helps BindImpl to the validation.
 * 
 * @author dennis
 * @since 6.0.0
 */
/*public*/ class ValidationHelper {

    private final Binder _binder;
    private final InfoProvider _infoProvider;

    //    private Map<Binding,Set<Property>> _collectedPropertyCache;
    private Map<Binding, Property> _mainPropertyCache;

    public ValidationHelper(Binder binder, InfoProvider infoProvider) {
        this._binder = binder;
        this._infoProvider = infoProvider;
        this._mainPropertyCache = new HashMap<Binding, Property>(2);
        //        this._collectedPropertyCache = new HashMap<Binding,Set<Property>>(2);
    }

    // a binder validation information provider, it is related to implementation of BindImpl 
    interface InfoProvider {
        Map<String, List<SavePropertyBinding>> getSaveBeforeBindings();

        Map<String, List<SavePropertyBinding>> getSaveAfterBindings();

        Map<String, List<SaveFormBinding>> getSaveFormBeforeBindings();

        Map<String, List<SaveFormBinding>> getSaveFormAfterBindings();

        BindingKey getBindingKey(Component comp, String attr);
    }

    //doCommand -> doValidate ->
    public void collectSaveBefore(Component comp, String command, Event evt, Set<Property> validates) {
        collectSavePropertyBefore(comp, command, evt, validates);
        collectSaveFormBefore(comp, command, evt, validates);
    }

    //doCommand -> doValidate -> collectSaveBefore ->
    private void collectSavePropertyBefore(Component comp, String command, Event evt, Set<Property> validates) {
        final List<SavePropertyBinding> bindings = _infoProvider.getSaveBeforeBindings().get(command); //_saveBeforeBindings.get(command);
        if (bindings != null) {
            for (SavePropertyBinding binding : bindings) {
                collectSavePropertyBinding(comp, binding, command, evt, validates);
            }
        }
    }

    //doCommand -> doValidate -> collectSaveBefore ->
    private void collectSaveFormBefore(Component comp, String command, Event evt, Set<Property> validates) {
        final List<SaveFormBinding> bindings = _infoProvider.getSaveFormBeforeBindings().get(command); //_saveFormBeforeBindings.get(command);
        if (bindings != null) {
            for (SaveFormBinding binding : bindings) {
                collectSaveFormBinding(comp, binding, command, evt, validates);
            }
        }
    }

    //doValidate -> 
    public void collectSaveAfter(Component comp, String command, Event evt, Set<Property> validates) {
        collectSavePropertyAfter(comp, command, evt, validates);
        collectSaveFormAfter(comp, command, evt, validates);
    }

    //doValidate -> collectSaveAfter ->
    private void collectSavePropertyAfter(Component comp, String command, Event evt, Set<Property> validates) {
        final List<SavePropertyBinding> bindings = _infoProvider.getSaveAfterBindings().get(command); //_saveAfterBindings.get(command);
        if (bindings != null) {
            for (SavePropertyBinding binding : bindings) {
                collectSavePropertyBinding(comp, binding, command, evt, validates);
            }
        }
    }

    //doValidate -> collectSaveAfter ->
    private void collectSaveFormAfter(Component comp, String command, Event evt, Set<Property> validates) {
        final List<SaveFormBinding> bindings = _infoProvider.getSaveFormAfterBindings().get(command); //_saveFormAfterBindings.get(command);
        if (bindings != null) {
            for (SaveFormBinding binding : bindings) {
                collectSaveFormBinding(comp, binding, command, evt, validates);
            }
        }
    }

    //validations

    public boolean validateSaveBefore(Component comp, String command, Map<String, Property[]> validates, boolean valid,
            Set<Property> notifys) {
        boolean r = valid;
        r &= validateSavePropertyBefore(comp, command, validates, r, notifys);
        r &= validateSaveFormBefore(comp, command, validates, r, notifys);
        return r;
    }

    //doCommand -> doValidate -> validateSaveBefore ->
    private boolean validateSavePropertyBefore(Component comp, String command, Map<String, Property[]> validates,
            boolean valid, Set<Property> notifys) {
        final List<SavePropertyBinding> bindings = _infoProvider.getSaveBeforeBindings().get(command); //_saveBeforeBindings.get(command);
        boolean r = valid;
        if (bindings != null) {
            for (SavePropertyBinding binding : bindings) {
                r &= validateSavePropertyBinding(comp, binding, command, validates, r, notifys);
            }
        }
        return r;
    }

    //doCommand -> doValidate -> validateSaveBefore ->
    private boolean validateSaveFormBefore(Component comp, String command, Map<String, Property[]> validates,
            boolean valid, Set<Property> notifys) {
        final List<SaveFormBinding> bindings = _infoProvider.getSaveFormBeforeBindings().get(command); //_saveFormBeforeBindings.get(command);
        boolean r = valid;
        if (bindings != null) {
            for (SaveFormBinding binding : bindings) {
                r &= validateSaveFormBinding(comp, binding, command, validates, r, notifys);
            }
        }
        return r;
    }

    public boolean validateSaveAfter(Component comp, String command, Map<String, Property[]> validates, boolean valid,
            Set<Property> notifys) {
        boolean r = valid;
        r &= validateSavePropertyAfter(comp, command, validates, r, notifys);
        r &= validateSaveFormAfter(comp, command, validates, r, notifys);
        return r;
    }

    //doCommand -> doValidate -> validateSaveBefore ->
    private boolean validateSavePropertyAfter(Component comp, String command, Map<String, Property[]> validates,
            boolean valid, Set<Property> notifys) {
        final List<SavePropertyBinding> bindings = _infoProvider.getSaveAfterBindings().get(command); //_saveBeforeBindings.get(command);
        boolean r = true;
        if (bindings != null) {
            for (SavePropertyBinding binding : bindings) {
                r &= validateSavePropertyBinding(comp, binding, command, validates, valid, notifys);
            }
        }
        return r;
    }

    //doCommand -> doValidate -> validateSaveBefore ->
    private boolean validateSaveFormAfter(Component comp, String command, Map<String, Property[]> validates,
            boolean valid, Set<Property> notifys) {
        final List<SaveFormBinding> bindings = _infoProvider.getSaveFormAfterBindings().get(command); //_saveFormBeforeBindings.get(command);
        boolean r = valid;
        if (bindings != null) {
            for (SaveFormBinding binding : bindings) {
                r &= validateSaveFormBinding(comp, binding, command, validates, r, notifys);
            }
        }
        return r;
    }

    //collect properties from a save-binding
    private void collectSavePropertyBinding(Component comp, SavePropertyBinding binding, String command, Event evt,
            Set<Property> validates, boolean checkFormFieldFromCollection) {
        final BindContext ctx = BindContextUtil.newBindContext(_binder, binding, true, command, binding.getComponent(),
                evt);
        BindContextUtil.setConverterArgs(_binder, binding.getComponent(), ctx, binding);
        Set<Property> cp = new HashSet<Property>();
        Property p = binding.getValidate(ctx);
        _mainPropertyCache.put(binding, p);
        cp.add(p); //main property
        if (checkFormFieldFromCollection) { //ZK-3185: Enable form validation with reference and collection binding
            Property pc = ((SavePropertyBindingImpl) binding).getBasePropertyIfFromCollection(); //Check if field from collection
            if (pc != null) cp.add(pc);
        }
        validates.addAll(cp); //collect properties to be validated
    }

    //collect properties from a save-binding
    private void collectSavePropertyBinding(Component comp, SavePropertyBinding binding, String command, Event evt,
                                            Set<Property> validates) {
        collectSavePropertyBinding(comp, binding, command, evt, validates, false);
    }

    //collect properties form a save-form-binding
    private void collectSaveFormBinding(Component comp, SaveFormBinding binding, String command, Event evt,
            Set<Property> validates) {
        //ZK-3185: Enable form validation with reference and collection binding
        String formId = binding.getFormId();
        Component formComp = binding.getComponent();
        Binder binder = binding.getBinder();
        Form form = ((BinderCtrl) binding.getBinder()).getForm(formComp, formId);
        if (form instanceof FormProxyObject) {
            Set<Pair<String, SavePropertyBinding>> cachedSavePropertyBindings = ((FormProxyObject) form).collectCachedSavePropertyBinding();
            for (Pair<String, SavePropertyBinding> p : cachedSavePropertyBindings) {
                SavePropertyBinding spbinding = p.getY();
                String fieldName = p.getX();
                Component cachedComp = spbinding.getComponent();
                if (cachedComp.getDesktop() != null) {
                    ((BinderCtrl) binder).addFormAssociatedSaveBinding(spbinding.getComponent(), formId, spbinding, fieldName);
                    ((BinderCtrl) binder).addSaveFormFieldName(form, fieldName);
                }
            }
        }

        Set<SaveBinding> savebindings = ((BinderCtrl) binder).getFormAssociatedSaveBindings(formComp);
        for (SaveBinding sbinding : savebindings) {
            if (sbinding instanceof SavePropertyBinding) {
                collectSavePropertyBinding(comp, ((SavePropertyBinding) sbinding), command, evt, validates, true);
            } else {
                // any other possible to go here?
            }
        }

        final BindContext ctx = BindContextUtil.newBindContext(_binder, binding, true, command, binding.getComponent(),
                evt);

        Set<Property> cp = new HashSet<Property>();
        Property p = binding.getValidate(ctx);
        _mainPropertyCache.put(binding, p);

        cp.add(p); // the main property
        cp.addAll(binding.getValidates(ctx)); // the field properties in form
        validates.addAll(cp); //collect properties to be validated
    }

    //validate a save-binding
    private boolean validateSavePropertyBinding(Component comp, SavePropertyBinding binding, String command,
            Map<String, Property[]> validates, boolean valid, Set<Property> notifys) {
        if (!binding.hasValidator())
            return true;

        //clear previous message before validation
        if (((BinderImpl) binding.getBinder()).hasValidator(binding.getComponent(), binding.getFieldName())) {
            clearValidationMessage(binding.getBinder(), binding.getComponent(), binding.getFieldName());
        }

        final BindContext ctx = BindContextUtil.newBindContext(_binder, binding, true, command, binding.getComponent(),
                null);
        BindContextUtil.setValidatorArgs(binding.getBinder(), binding.getComponent(), ctx, binding);

        Property p = _mainPropertyCache.get(binding);
        ValidationContextImpl vContext = new ValidationContextImpl(command, p, validates, ctx, valid);
        binding.validate(vContext);
        final Set<Property> xnotifys = getNotifys(ctx);
        if (xnotifys != null) {
            notifys.addAll(xnotifys);
        }
        return vContext.isValid();
    }

    private void clearValidationMessage(Binder binder, Component component, String attr) {
        ValidationMessages vmsgs = ((BinderCtrl) binder).getValidationMessages();
        if (vmsgs != null) {
            vmsgs.clearMessages(component, attr);
        }
    }

    //validate a save-form-binding
    private boolean validateSaveFormBinding(Component comp, SaveFormBinding binding, String command,
            Map<String, Property[]> validates, boolean valid, Set<Property> notifys) {
        //validate tracked savebinding
        Set<SaveBinding> savebindings = ((BinderCtrl) binding.getBinder())
                .getFormAssociatedSaveBindings(binding.getComponent());
        boolean svalid = true;
        for (SaveBinding sbinding : savebindings) {
            if (sbinding instanceof SavePropertyBinding) {
                svalid &= validateSavePropertyBinding(comp, ((SavePropertyBinding) sbinding), command, validates,
                        svalid & valid, notifys);
            } else {
                // any other possible to go here?
            }
        }
        if (!binding.hasValidator())
            return svalid;

        //clear previous message before validation
        if (((BinderImpl) binding.getBinder()).hasValidator(binding.getComponent(), binding.getFormId())) {
            clearValidationMessage(binding.getBinder(), binding.getComponent(), binding.getFormId());
        }

        final BindContext ctx = BindContextUtil.newBindContext(_binder, binding, true, command, binding.getComponent(),
                null);
        BindContextUtil.setValidatorArgs(binding.getBinder(), binding.getComponent(), ctx, binding);

        Property p = _mainPropertyCache.get(binding);
        ValidationContextImpl vContext = new ValidationContextImpl(command, p, validates, ctx, svalid & valid);
        binding.validate(vContext);
        final Set<Property> xnotifys = getNotifys(ctx);
        if (xnotifys != null) {
            notifys.addAll(xnotifys);
        }
        return svalid && vContext.isValid();

    }

    @SuppressWarnings("unchecked")
    private static Set<Property> getNotifys(BindContext ctx) {
        return (Set<Property>) ctx.getAttribute(BinderImpl.NOTIFYS);
    }

}