001/*
002 * #%L
003 * GwtBootstrap3
004 * %%
005 * Copyright (C) 2015 GwtBootstrap3
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 * 
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 * 
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package gwt.material.design.client.base.validator;
021
022import com.google.gwt.event.shared.GwtEvent;
023import com.google.gwt.user.client.ui.Widget;
024import com.google.web.bindery.event.shared.EventBus;
025import com.google.web.bindery.event.shared.HandlerRegistration;
026import com.google.web.bindery.event.shared.SimpleEventBus;
027import gwt.material.design.client.base.validator.ValidationChangedEvent.ValidationChangedHandler;
028
029import java.util.LinkedHashMap;
030import java.util.Map;
031
032/**
033 * Useful for validating a group of fields that implement the {@link HasValidators} interface.
034 *
035 * @author Steven Jardine
036 */
037public class GroupValidator implements ValidationChangedEvent.HasValidationChangedHandlers {
038
039    private final SimpleEventBus eventBus;
040
041    private boolean fireEvents = false;
042
043    private final Map<HasValidators<?>, Boolean> fields;
044
045    private final Map<HasValidators<?>, HandlerRegistration> registrations;
046
047    private Boolean groupValid = null;
048
049    /**
050     * Constructor.
051     */
052    public GroupValidator() {
053        fields = new LinkedHashMap<>();
054        registrations = new LinkedHashMap<>();
055        eventBus = new SimpleEventBus();
056    }
057
058    /**
059     * Adds a field to the group.
060     *
061     * @param <T>   the generic type
062     * @param field the field
063     */
064    public <T extends Widget & HasValidators<?>> void add(final T field) {
065        fields.put(field, field.validate(false));
066        if (field.isAttached()) {
067            updateStateAndNotify();
068        }
069        registrations.put(field, field.addValidationChangedHandler(event -> {
070            fields.put(field, event.isValid());
071            if (fireEvents) {
072                updateStateAndNotify();
073            }
074        }));
075    }
076
077    @Override
078    public HandlerRegistration addValidationChangedHandler(ValidationChangedHandler handler) {
079        return eventBus.addHandler(ValidationChangedEvent.getType(), handler);
080    }
081
082    @Override
083    public void fireEvent(GwtEvent<?> event) {
084        eventBus.fireEvent(event);
085    }
086
087    /**
088     * Removes a field from the validation group.
089     *
090     * @param <T>   the generic type
091     * @param field the field
092     * @return true, if successful
093     */
094    public <T extends Widget & HasValidators<?>> boolean remove(final T field) {
095        fields.remove(field);
096        HandlerRegistration reg = registrations.remove(field);
097        if (reg != null) {
098            reg.removeHandler();
099            return true;
100        }
101        return false;
102    }
103
104    /**
105     * Update the state of the validator and notify via {@link EventBus} any changed handlers.
106     */
107    protected void updateStateAndNotify() {
108        Boolean oldGroupValid = groupValid;
109        groupValid = true;
110        for (Boolean valid : fields.values()) {
111            groupValid &= valid;
112        }
113        if (groupValid != oldGroupValid) {
114            eventBus.fireEvent(new ValidationChangedEvent(groupValid));
115        }
116    }
117
118    /**
119     * Validate the group. This calls {@link Validator #validate(Editor, Object)} on each field in the group.
120     *
121     * @return true, if successful
122     */
123    public boolean validate() {
124        return validate(true);
125    }
126
127    /**
128     * Validate the group. This calls {@link Validator #validate(Editor, Object)} on each field in the group.
129     *
130     * @param show do we want to show the user the result of the validate via ui marks?
131     * @return true, if successful
132     */
133    public boolean validate(boolean show) {
134        fireEvents = false;
135        for (HasValidators<?> field : fields.keySet()) {
136            field.validate(show);
137        }
138        fireEvents = true;
139        updateStateAndNotify();
140        return groupValid;
141    }
142}