/*
 * Copyright (c) 2008 Haulmont Technology Ltd. All Rights Reserved.
 * Haulmont Technology proprietary and confidential.
 * Use is subject to license terms.

 * Author: Dmitry Abramov
 * Created: 30.12.2008 16:27:03
 * $Id$
 */
package com.haulmont.cuba.web.gui.components;

import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.ValueListener;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.ValidationException;
import com.haulmont.cuba.gui.MetadataHelper;
import com.haulmont.cuba.web.gui.data.ItemWrapper;
import com.haulmont.cuba.core.entity.Entity;
import com.itmill.toolkit.data.Property;
import com.itmill.toolkit.data.Validator;

import java.util.*;

public abstract class AbstractField<T extends com.itmill.toolkit.ui.Field>
    extends
        AbstractComponent<T>
    implements
        Component.Field
{
    protected Datasource<Entity> datasource;
    protected MetaProperty metaProperty;

    protected List<ValueListener> listeners = new ArrayList<ValueListener>();
    protected Map<com.haulmont.cuba.gui.components.Field.Validator, Validator> validators =
            new HashMap<com.haulmont.cuba.gui.components.Field.Validator, Validator>();

    public Datasource getDatasource() {
        return datasource;
    }

    public MetaProperty getMetaProperty() {
        return metaProperty;
    }

    public void setDatasource(Datasource datasource, String property) {
        this.datasource = datasource;

        final MetaClass metaClass = datasource.getMetaClass();
        this.metaProperty = metaClass.getProperty(property);

        final MetaPropertyPath propertyPath = new MetaPropertyPath(metaProperty.getDomain(), metaProperty);
        final ItemWrapper wrapper = createDatasourceWrapper(datasource, Collections.singleton(propertyPath));
        component.setPropertyDataSource(wrapper.getItemProperty(propertyPath));

        setRequired(metaProperty.isMandatory());
    }

    protected ItemWrapper createDatasourceWrapper(Datasource datasource, Collection<MetaPropertyPath> propertyPaths) {
        return new ItemWrapper(datasource, propertyPaths);
    }

    public boolean isRequired() {
        return component.isRequired();
    }

    public void setRequired(boolean required) {
        component.setRequired(required);
    }

    public <T> T getValue() {
        return (T) component.getValue();
    }

    public void setValue(Object value) {
        component.setValue(value);
    }

    public String getCaption() {
        return component.getCaption();
    }

    public void setCaption(String caption) {
        component.setCaption(caption);
    }

    public boolean isEditable() {
        return !component.isReadOnly();
    }

    public void setEditable(boolean editable) {
        component.setReadOnly(!editable);
    }

    public void addListener(ValueListener listener) {
        if (!listeners.contains(listener)) listeners.add(listener);
    }

    public void removeListener(ValueListener listener) {
        listeners.remove(listener);
    }

    protected Object prevValue;

    protected void attachListener(T component) {
        component.addListener(new Property.ValueChangeListener() {
            public void valueChange(Property.ValueChangeEvent event) {
                final Object value = getValue();
                fireValueChanged(prevValue, value);
                prevValue = value;
            }
        });
    }

    protected void fireValueChanged(Object prevValue, Object value) {
        for (ValueListener listener : listeners) {
            listener.valueChanged(this, "value", prevValue, value);
        }
    }

    public void addValidator(final com.haulmont.cuba.gui.components.Field.Validator validator) {
        if (!validators.containsKey(validator)) {
            final Validator componentValidator = new Validator() {
                public void validate(Object value) throws InvalidValueException {
                    try {
                        validator.validate(value);
                    } catch (ValidationException e) {
                        throw new InvalidValueException(e.getMessage());
                    }
                }

                public boolean isValid(Object value) {
                    return validator.isValid(value);
                }
            };

            component.addValidator(componentValidator);
            validators.put(validator, componentValidator);
        }
    }

    public void removeValidator(com.haulmont.cuba.gui.components.Field.Validator validator) {
        validators.remove(validators.get(validator));
    }

    public boolean isValid() {
        return component.isValid();
    }

    public void validate() throws ValidationException {
        try {
            component.validate();
        } catch (Validator.InvalidValueException e) {
            throw new ValidationException(e.getMessage());
        }
    }
}

@@ -4234,32 +4234,121 @@
 lueException %7B%0D%0A
+                    if ((!isRequired() && value == null)) return;%0D%0A                    %0D%0A
                 
@@ -4665,16 +4665,52 @@
   return
+ (!isRequired() && value == null) %7C%7C
  validat

fix validator