/*
 * Copyright (c) 2008 Haulmont Technology Ltd. All Rights Reserved.
 * Haulmont Technology proprietary and confidential.
 * Use is subject to license terms.
 */
package com.haulmont.cuba.web.gui.components;

import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.datatypes.Datatypes;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Formatter;
import com.haulmont.cuba.gui.components.TextInputField;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.web.toolkit.ui.converters.StringToDatatypeConverter;
import com.haulmont.cuba.web.toolkit.ui.converters.StringToEntityConverter;
import com.haulmont.cuba.web.toolkit.ui.converters.StringToEnumConverter;
import com.vaadin.ui.AbstractTextField;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.text.ParseException;
import java.util.Locale;

/**
 * @param <T>
 * @author abramov
 * @version $Id$
 */
public abstract class WebAbstractTextField<T extends AbstractTextField>
        extends
            WebAbstractField<T>
        implements
            TextInputField, Component.Wrapper {

    private static Log log = LogFactory.getLog(WebAbstractTextField.class);

    protected Locale locale = AppBeans.get(UserSessionSource.class).getLocale();

    public WebAbstractTextField() {
        this.component = createTextFieldImpl();
        this.component.setValidationVisible(false);

        component.setConverter(new StringToDatatypeConverter(Datatypes.getNN(String.class)));

        attachListener(component);
        component.setImmediate(true);
        component.setNullRepresentation("");
        component.setInvalidAllowed(false);
        component.setInvalidCommitted(true);
        component.addValidator(
                new com.vaadin.data.Validator() {
                    @Override
                    public void validate(Object value) throws InvalidValueException {
                        if (!isValid(value)) {
                            component.markAsDirty();
                            throw new InvalidValueException("Unable to parse value: " + value);
                        }
                    }

                    public boolean isValid(Object value) {
                        Datatype datatype = getActualDatatype();
                        if (value instanceof String && datatype != null) {
                            try {
                                datatype.parse((String) value, locale);
                            } catch (ParseException e) {
                                return false;
                            }
                        }
                        return true;
                    }
                }
        );
    }

    protected abstract T createTextFieldImpl();

    @Override
    public <T> T getValue() {
        Object value = super.getValue();
        Datatype datatype = getActualDatatype();
        if (value instanceof String && datatype != null) {
            try {
                return (T) datatype.parse((String) value, locale);
            } catch (ParseException e) {
                log.debug("Unable to parse value of component " + getId() + "\n" + e.getMessage());
                return null;
            }
        } else {
            return (T) value;
        }
    }

    @Override
    public void setValue(Object value) {
        if (!isEditable())
            return;

        Datatype datatype = getActualDatatype();
        if (!(value instanceof String) && datatype != null) {
            String str = datatype.format(value, locale);
            super.setValue(str);
        } else {
            super.setValue(value);
        }
    }

    protected Datatype getActualDatatype() {
        if (metaProperty != null) {
            return metaProperty.getRange().isDatatype() ? metaProperty.getRange().asDatatype() : null;
        } else {
            return Datatypes.getNN(String.class);
        }
    }

    @Override
    public void setDatasource(Datasource datasource, String property) {
        super.setDatasource(datasource, property);

        Integer len = (Integer) metaProperty.getAnnotations().get("length");
        if (len != null) {
            component.setMaxLength(len);
        }
    }

    protected Formatter getFormatter() {
        return null;
    }

    protected boolean isTrimming() {
        return false;
    }

    @Override
    protected void initFieldConverter() {
        if (metaProperty != null) {
            switch (metaProperty.getType()) {
                case ASSOCIATION:
                    component.setConverter(new StringToEntityConverter() {
                        @Override
                        public Formatter getFormatter() {
                            return WebAbstractTextField.this.getFormatter();
                        }
                    });
                    break;

                case DATATYPE:
                    Datatype<?> datatype = Datatypes.get(metaProperty.getJavaType());
                    if (datatype == null) {
                        datatype = Datatypes.getNN(String.class);
                    }

                    component.setConverter(new TextFieldStringToDatatypeConverter(datatype));
                    break;

                case ENUM:
                    //noinspection unchecked
                    component.setConverter(new StringToEnumConverter((Class<Enum>) metaProperty.getJavaType()){
                        @Override
                        public Formatter getFormatter() {
                            return WebAbstractTextField.this.getFormatter();
                        }

                        @Override
                        public boolean isTrimming() {
                            return WebAbstractTextField.this.isTrimming();
                        }
                    });
                    break;

                default:
                    component.setConverter(new TextFieldStringToDatatypeConverter(Datatypes.getNN(String.class)));
                    break;
            }
        } else {
            component.setConverter(new TextFieldStringToDatatypeConverter(Datatypes.getNN(String.class)));
        }
    }

    @Override
    protected boolean isEmpty(Object value) {
        if (value instanceof String) {
            return StringUtils.isBlank((String) value);
        } else {
            return value == null;
        }
    }

    protected class TextFieldStringToDatatypeConverter extends StringToDatatypeConverter {
        public TextFieldStringToDatatypeConverter(Datatype datatype) {
            super(datatype);
        }

        @Override
        public Formatter getFormatter() {
            return WebAbstractTextField.this.getFormatter();
        }

        @Override
        public boolean isTrimming() {
            return WebAbstractTextField.this.isTrimming();
        }
    }
}
@@ -3099,22 +3099,22 @@
         
-Object
+String
  value =
@@ -3129,24 +3129,112 @@
 etValue();%0D%0A
+        if (isTrimming()) %7B%0D%0A            value = StringUtils.trim(value);%0D%0A        %7D%0D%0A%0D%0A
         Data
@@ -3289,33 +3289,23 @@
  (value 
-instanceof String
+!= null
  && data
@@ -3378,33 +3378,24 @@
 atype.parse(
-(String) 
 value, local

Fix trim for TextField in getValue() #PL-2481