/*
 * Copyright 2014 Matti Tahvonen.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.vaadin.viritin;

import com.vaadin.data.Property;
import com.vaadin.data.fieldgroup.BeanFieldGroup;
import com.vaadin.event.FieldEvents;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Field;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.validation.constraints.NotNull;
import org.vaadin.viritin.fields.MTextField;

/**
 * Enhanced version of basic BeanFieldGroup in Vaadin. Supports "eager
 * validation" and some enhancements to bean validation support.
 *
 * @param <T> the type of the bean wrapped by this group
 */
public class MBeanFieldGroup<T> extends BeanFieldGroup<T> implements
        Property.ValueChangeListener, FieldEvents.TextChangeListener {

    protected final Class nonHiddenBeanType;

    /**
     * Configures fields for some better defaults, like property fields
     * annotated with NotNull to be "required" (kind of a special validator in
     * Vaadin)
     */
    public void configureMaddonDefaults() {
        for (Object property : getBoundPropertyIds()) {
            final Field<?> field = getField(property);

            // Make @NotNull annotated fields "required"
            try {
                java.lang.reflect.Field declaredField = findDeclaredField(
                        property, nonHiddenBeanType);
                final NotNull notNullAnnotation = declaredField.getAnnotation(
                        NotNull.class);
                if (notNullAnnotation != null) {
                    field.setRequired(true);
                    if (notNullAnnotation.message() != null) {
                        getField(property).setRequiredError(notNullAnnotation.
                                message());
                    }
                }
            } catch (NoSuchFieldException ex) {
                Logger.getLogger(MBeanFieldGroup.class.getName()).
                        log(Level.FINE, null, ex);
            } catch (SecurityException ex) {
                Logger.getLogger(MBeanFieldGroup.class.getName()).
                        log(Level.SEVERE, null, ex);
            }
        }
    }

    protected java.lang.reflect.Field findDeclaredField(Object property,
            Class clazz) throws NoSuchFieldException, SecurityException {
        try {
            java.lang.reflect.Field declaredField = clazz.
                    getDeclaredField(property.
                            toString());
            return declaredField;
        } catch (NoSuchFieldException e) {
            if (clazz.getSuperclass() == null) {
                throw e;
            } else {
                return findDeclaredField(property, clazz.getSuperclass());
            }
        }
    }

    public interface FieldGroupListener<T> {

        public void onFieldGroupChange(MBeanFieldGroup<T> beanFieldGroup);

    }

    @Override
    public void valueChange(Property.ValueChangeEvent event) {
        setBeanModified(true);
        if (listener != null) {
            listener.onFieldGroupChange(this);
        }
    }

    @Override
    public void textChange(FieldEvents.TextChangeEvent event) {
        valueChange(null);
    }

    private boolean beanModified = false;
    private FieldGroupListener<T> listener;

    public void setBeanModified(boolean beanModified) {
        this.beanModified = beanModified;
    }

    public boolean isBeanModified() {
        return beanModified;
    }

    @Override
    public boolean isModified() {
        return super.isModified();
    }

    public MBeanFieldGroup(Class beanType) {
        super(beanType);
        this.nonHiddenBeanType = beanType;
    }

    public MBeanFieldGroup<T> withEagerValidation() {
        return withEagerValidation(new FieldGroupListener() {
            @Override
            public void onFieldGroupChange(MBeanFieldGroup beanFieldGroup) {
            }
        });
    }

    /**
     * Makes all fields "immediate" to trigger eager validation
     *
     * @param listener a listener that will be notified when a field in the
     * group has been modified
     * @return the MBeanFieldGroup that can be used for further modifications or
     * e.g. commit if buffered
     */
    public MBeanFieldGroup<T> withEagerValidation(FieldGroupListener<T> listener) {
        this.listener = listener;
        for (Field<?> field : getFields()) {
            ((AbstractComponent) field).setImmediate(true);
            field.addValueChangeListener(this);
            if (field instanceof MTextField) {
                final MTextField abstractTextField = (MTextField) field;
                abstractTextField.setEagerValidation(true);
                abstractTextField.addTextChangeListener(this);
            }
        }
        return this;
    }

    /**
     * Removes all listeners from the bound fields and unbinds properties.
     */
    public void unbind() {
        // wrap in array list to avoid CME
        for (Field<?> field : new ArrayList<Field<?>>(getFields())) {
            field.removeValueChangeListener(this);
            if (field instanceof MTextField) {
                final MTextField abstractTextField = (MTextField) field;
                abstractTextField.setEagerValidation(true);
                abstractTextField.removeTextChangeListener(this);
            }
            unbind(field);
        }

    }

}

@@ -729,27 +729,73 @@
 n.event.
-FieldEvents
+*;%0Aimport com.vaadin.event.FieldEvents.TextChangeNotifier
 ;%0Aimport
@@ -856,16 +856,17 @@
 .Field;%0A
+%0A
 import j
@@ -946,24 +946,25 @@
 ing.Logger;%0A
+%0A
 import javax
@@ -996,16 +996,17 @@
 otNull;%0A
+%0A
 import o
@@ -5281,32 +5281,190 @@
 lidation(true);%0A
+            %7D%0A            if (field instanceof TextChangeNotifier) %7B%0A                final TextChangeNotifier abstractTextField = (TextChangeNotifier) field;%0A
                 

Add TextChangeListener to all fields which support it, instead of only MTextField. This makes the reset button of the AbstractFrom also work with TextArea.