001package org.gwtbootstrap3.client.ui.base;
002
003/*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2015 GwtBootstrap3
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 * http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import java.text.ParseException;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029
030import org.gwtbootstrap3.client.ui.Radio;
031import org.gwtbootstrap3.client.ui.base.mixin.BlankValidatorMixin;
032import org.gwtbootstrap3.client.ui.base.mixin.ErrorHandlerMixin;
033import org.gwtbootstrap3.client.ui.form.error.ErrorHandler;
034import org.gwtbootstrap3.client.ui.form.error.ErrorHandlerType;
035import org.gwtbootstrap3.client.ui.form.error.HasErrorHandler;
036import org.gwtbootstrap3.client.ui.form.validator.HasBlankValidator;
037import org.gwtbootstrap3.client.ui.form.validator.HasValidators;
038import org.gwtbootstrap3.client.ui.form.validator.RadioGroupBlankValidatorMixin;
039import org.gwtbootstrap3.client.ui.form.validator.ValidationChangedEvent.ValidationChangedHandler;
040import org.gwtbootstrap3.client.ui.form.validator.Validator;
041import org.gwtbootstrap3.client.ui.gwt.FlowPanel;
042
043import com.google.gwt.editor.client.EditorError;
044import com.google.gwt.editor.client.HasEditorErrors;
045import com.google.gwt.editor.client.LeafValueEditor;
046import com.google.gwt.event.logical.shared.ValueChangeEvent;
047import com.google.gwt.event.logical.shared.ValueChangeHandler;
048import com.google.gwt.text.shared.Parser;
049import com.google.gwt.user.client.ui.HasName;
050import com.google.gwt.user.client.ui.HasOneWidget;
051import com.google.gwt.user.client.ui.HasValue;
052import com.google.gwt.user.client.ui.HasWidgets;
053import com.google.gwt.user.client.ui.IsWidget;
054import com.google.gwt.user.client.ui.Widget;
055import com.google.web.bindery.event.shared.HandlerRegistration;
056
057/**
058 * Contains a group of radio input elements.
059 *
060 * @param <T> the return value type of the radio group.
061 * @author Steven Jardine
062 */
063public class RadioGroupBase<T> extends FlowPanel implements HasName, HasValue<T>, LeafValueEditor<T>,
064        HasEditorErrors<T>, HasErrorHandler, HasValidators<T>, HasBlankValidator<T> {
065
066    private final ErrorHandlerMixin<T> errorHandlerMixin = new ErrorHandlerMixin<T>(this);
067
068    private String name = null;
069
070    private final Parser<T> parser;
071
072    private final BlankValidatorMixin<RadioGroupBase<T>, T> validatorMixin = new RadioGroupBlankValidatorMixin<RadioGroupBase<T>, T>(
073            this, errorHandlerMixin.getErrorHandler());
074
075    private final Map<Radio, HandlerRegistration> valueChangedRegistrations = new HashMap<Radio, HandlerRegistration>();
076
077    /**
078     * Constructor.
079     *
080     * @param name the name
081     * @param parser the parser
082     */
083    public RadioGroupBase(final String name, final Parser<T> parser) {
084        super();
085        this.name = name;
086        this.parser = parser;
087    }
088
089    /** {@inheritDoc} */
090    @Override
091    public void add(final Widget w) {
092        super.add(w);
093        updateChildren();
094    }
095
096    @Override
097    public HandlerRegistration addValidationChangedHandler(final ValidationChangedHandler handler) {
098        return validatorMixin.addValidationChangedHandler(handler);
099    }
100
101    @Override
102    public void addValidator(final Validator<T> validator) {
103        validatorMixin.addValidator(validator);
104    }
105
106    /** {@inheritDoc} */
107    @Override
108    public com.google.gwt.event.shared.HandlerRegistration addValueChangeHandler(final ValueChangeHandler<T> handler) {
109        return addHandler(handler, ValueChangeEvent.getType());
110    }
111
112    /** {@inheritDoc} */
113    @Override
114    public void clear() {
115        super.clear();
116        updateChildren();
117    }
118
119    @Override
120    public boolean getAllowBlank() {
121        return validatorMixin.getAllowBlank();
122    }
123
124    /** {@inheritDoc} */
125    @Override
126    public ErrorHandler getErrorHandler() {
127        return errorHandlerMixin.getErrorHandler();
128    }
129
130    /** {@inheritDoc} */
131    @Override
132    public ErrorHandlerType getErrorHandlerType() {
133        return errorHandlerMixin.getErrorHandlerType();
134    }
135
136    /** {@inheritDoc} */
137    @Override
138    public String getName() {
139        return name;
140    }
141
142    /**
143     * @return the radio children
144     */
145    public Set<Radio> getRadioChildren() {
146        return getRadioChildren(this, null);
147    }
148
149    /**
150     * Gets the radio children.
151     *
152     * @param widget the widget
153     * @param c the current children
154     * @return the radio children
155     */
156    protected Set<Radio> getRadioChildren(final Widget widget, final Set<Radio> c) {
157        Set<Radio> children = c;
158        if (children == null) {
159            children = new HashSet<Radio>();
160        }
161        if (widget instanceof Radio) {
162            children.add((Radio) widget);
163        } else if (widget instanceof HasOneWidget) {
164            children = getRadioChildren(((HasOneWidget) widget).getWidget(), children);
165        } else if (widget instanceof HasWidgets) {
166            for (Widget w : (HasWidgets) widget) {
167                if (w instanceof Radio) {
168                    children.add((Radio) w);
169                } else {
170                    children = getRadioChildren(w, children);
171                }
172            }
173        }
174        return children;
175    }
176
177    /** {@inheritDoc} */
178    @Override
179    public boolean getValidateOnBlur() {
180        return validatorMixin.getValidateOnBlur();
181    }
182
183    /** {@inheritDoc} */
184    @Override
185    public T getValue() {
186        for (Radio child : getRadioChildren()) {
187            if (child.getValue()) {
188                try {
189                    return parser.parse(child.getFormValue());
190                } catch (ParseException e) {
191                }
192            }
193        }
194        return null;
195    }
196
197    /** {@inheritDoc} */
198    @Override
199    public void insert(final IsWidget w, final int beforeIndex) {
200        super.insert(w, beforeIndex);
201        updateChildren();
202    }
203
204    /** {@inheritDoc} */
205    @Override
206    public void insert(final Widget w, final int beforeIndex) {
207        super.insert(w, beforeIndex);
208        updateChildren();
209    }
210
211    /** {@inheritDoc} */
212    @Override
213    public boolean removeValidator(final Validator<T> validator) {
214        return validatorMixin.removeValidator(validator);
215    }
216
217    /** {@inheritDoc} */
218    @Override
219    public void reset() {
220        setValue(null);
221        validatorMixin.reset();
222    }
223
224    /** {@inheritDoc} */
225    @Override
226    public void setAllowBlank(final boolean allowBlank) {
227        validatorMixin.setAllowBlank(allowBlank);
228    }
229
230    /** {@inheritDoc} */
231    @Override
232    public void setErrorHandler(final ErrorHandler handler) {
233        errorHandlerMixin.setErrorHandler(handler);
234        validatorMixin.setErrorHandler(handler);
235    }
236
237    /** {@inheritDoc} */
238    @Override
239    public void setErrorHandlerType(final ErrorHandlerType type) {
240        errorHandlerMixin.setErrorHandlerType(type);
241    }
242
243    /** {@inheritDoc} */
244    @Override
245    public void setName(final String name) {
246        this.name = name;
247    }
248
249    @Override
250    public void setValidateOnBlur(final boolean validateOnBlur) {
251        validatorMixin.setValidateOnBlur(validateOnBlur);
252    }
253
254    @Override
255    public void setValidators(@SuppressWarnings("unchecked") final Validator<T>... validators) {
256        validatorMixin.setValidators(validators);
257    }
258
259    /** {@inheritDoc} */
260    @Override
261    public void setValue(final T value) {
262        setValue(value, false);
263    }
264
265    /** {@inheritDoc} */
266    @Override
267    public void setValue(final T value, final boolean fireEvents) {
268        for (Radio child : getRadioChildren()) {
269            try {
270                if (value != null && value.equals(parser.parse(child.getFormValue()))) {
271                    child.setValue(true, fireEvents);
272                }
273            } catch (ParseException e) {
274            }
275        }
276    }
277
278    /** {@inheritDoc} */
279    @Override
280    public void showErrors(final List<EditorError> errors) {
281        errorHandlerMixin.showErrors(errors);
282    }
283
284    private ValueChangeHandler<Boolean> changeHandler = new ValueChangeHandler<Boolean>() {
285        @Override
286        public void onValueChange(ValueChangeEvent<Boolean> event) {
287            ValueChangeEvent.fire(RadioGroupBase.this, getValue());
288        }
289    };
290
291    /**
292     * Update the radio children names.
293     */
294    protected void updateChildren() {
295        for (Radio child : getRadioChildren()) {
296            HandlerRegistration reg = valueChangedRegistrations.get(child);
297            if (reg == null) {
298                valueChangedRegistrations.put(child, child.addValueChangeHandler(changeHandler));
299            }
300            child.setName(name);
301        }
302    }
303
304    /** {@inheritDoc} */
305    @Override
306    public boolean validate() {
307        return validatorMixin.validate();
308    }
309
310    /** {@inheritDoc} */
311    @Override
312    public boolean validate(final boolean show) {
313        return validatorMixin.validate(show);
314    }
315
316    /** {@inheritDoc} */
317    @Override
318    public boolean remove(Widget w) {
319        return super.remove(w);
320    }
321
322}