Package pl.net.bluesoft.rnd.processtool.ui.basewidgets

Source Code of pl.net.bluesoft.rnd.processtool.ui.basewidgets.ProcessDataBlockWidget$ComponentEvaluator

package pl.net.bluesoft.rnd.processtool.ui.basewidgets;

import com.vaadin.Application;
import com.vaadin.data.Container;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.Validatable;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.data.validator.AbstractValidator;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.data.validator.StringLengthValidator;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.terminal.Sizeable;
import com.vaadin.ui.*;
import com.vaadin.ui.Layout.AlignmentHandler;
import com.vaadin.ui.Layout.SpacingHandler;
import org.apache.commons.beanutils.NestedNullException;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.expression.DefaultResolver;
import org.apache.commons.beanutils.expression.Resolver;
import org.aperteworkflow.scripting.ScriptProcessor;
import org.aperteworkflow.scripting.ScriptProcessorRegistry;
import org.aperteworkflow.util.vaadin.VaadinUtility;
import pl.net.bluesoft.rnd.processtool.ProcessToolContext;
import pl.net.bluesoft.rnd.processtool.bpm.ProcessToolBpmSession;
import pl.net.bluesoft.rnd.processtool.dict.ProcessDictionaryRegistry;
import pl.net.bluesoft.rnd.processtool.model.*;
import pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration;
import pl.net.bluesoft.rnd.processtool.model.config.ProcessStateWidget;
import pl.net.bluesoft.rnd.processtool.model.dict.ProcessDictionary;
import pl.net.bluesoft.rnd.processtool.model.dict.ProcessDictionaryItem;
import pl.net.bluesoft.rnd.processtool.model.dict.ProcessDictionaryItemValue;
import pl.net.bluesoft.rnd.processtool.ui.basewidgets.editor.ProcessDataWidgetsDefinitionEditor;
import pl.net.bluesoft.rnd.processtool.ui.basewidgets.editor.ScriptCodeEditor;
import pl.net.bluesoft.rnd.processtool.ui.basewidgets.editor.ScriptUrlEditor;
import pl.net.bluesoft.rnd.processtool.ui.basewidgets.editor.ScriptingEnginesComboBox;
import pl.net.bluesoft.rnd.processtool.ui.basewidgets.xml.WidgetDefinitionLoader;
import pl.net.bluesoft.rnd.processtool.ui.basewidgets.xml.XmlConstants;
import pl.net.bluesoft.rnd.processtool.ui.basewidgets.xml.jaxb.*;
import pl.net.bluesoft.rnd.processtool.ui.widgets.ProcessToolDataWidget;
import pl.net.bluesoft.rnd.processtool.ui.widgets.ProcessToolVaadinRenderable;
import pl.net.bluesoft.rnd.processtool.ui.widgets.ProcessToolWidget;
import pl.net.bluesoft.rnd.processtool.ui.widgets.annotations.*;
import pl.net.bluesoft.rnd.processtool.ui.widgets.impl.BaseProcessToolVaadinWidget;
import pl.net.bluesoft.rnd.pt.utils.lang.Lang2;
import pl.net.bluesoft.rnd.util.i18n.I18NSource;
import pl.net.bluesoft.util.lang.Strings;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.vaadin.ui.Alignment.*;
import static pl.net.bluesoft.util.lang.FormatUtil.nvl;
import static pl.net.bluesoft.util.lang.StringUtil.hasText;

@AliasName(name = "ProcessData")
@AperteDoc(humanNameKey = "widget.process_data_block.name", descriptionKey = "widget.process_data_block.description")
@ChildrenAllowed(false)
@WidgetGroup("base-widgets")
public class ProcessDataBlockWidget extends BaseProcessToolVaadinWidget implements ProcessToolDataWidget, ProcessToolVaadinRenderable {
    private static final Logger logger = Logger.getLogger(ProcessDataBlockWidget.class.getName());
    private static final Resolver resolver = new DefaultResolver();
    private static Boolean allowFileldsListener = true;

    private WidgetDefinitionLoader definitionLoader = WidgetDefinitionLoader.getInstance();

    private ProcessDictionaryRegistry processDictionaryRegistry;

    //during re-rendering widget, this map could be modified while is iterated - it must be thread safe
    private Map<Property, WidgetElement> boundProperties = new ConcurrentHashMap<Property, WidgetElement>();
    private Map<AbstractSelect, WidgetElement> dictContainers = new HashMap<AbstractSelect, WidgetElement>();
    private Map<AbstractSelect, WidgetElement> instanceDictContainers = new HashMap<AbstractSelect, WidgetElement>();
    private Map<String, ProcessInstanceAttribute> processAttributes = new HashMap<String, ProcessInstanceAttribute>();
    protected WidgetsDefinitionElement widgetsDefinitionElement;
    private ProcessInstance processInstance;
    private Map<WidgetElement, Property> widgetDataSources = new HashMap<WidgetElement, Property>();

    @AutoWiredProperty
    @AutoWiredPropertyConfigurator(fieldClass = ScriptingEnginesComboBox.class)
    @AperteDoc(
            humanNameKey = "widget.process_data_block.property.scriptEngineType.name",
            descriptionKey = "widget.process_data_block.property.scriptEngineType.description"
    )
    private String scriptEngineType;

    @AutoWiredProperty
    @AutoWiredPropertyConfigurator(fieldClass = ScriptUrlEditor.class)
    @AperteDoc(
            humanNameKey = "widget.process_data_block.property.scriptExternalUrl.name",
            descriptionKey = "widget.process_data_block.property.scriptExternalUrl.description"
    )
    private String scriptExternalUrl;

    @AutoWiredProperty
    @AutoWiredPropertyConfigurator(fieldClass = ScriptCodeEditor.class)
    @AperteDoc(
            humanNameKey = "widget.process_data_block.property.scriptSourceCode.name",
            descriptionKey = "widget.process_data_block.property.scriptSourceCode.description"
    )
    private String scriptSourceCode;

    @AutoWiredProperty(required = true)
    @AutoWiredPropertyConfigurator(fieldClass = ProcessDataWidgetsDefinitionEditor.class)
    @AperteDoc(
            humanNameKey = "widget.process_data_block.property.widgetsDefinition.name",
            descriptionKey = "widget.process_data_block.property.widgetsDefinition.description"
    )
    private String widgetsDefinition;
    protected ComponentContainer mainPanel = null;

    public void setDefinitionLoader(WidgetDefinitionLoader definitionLoader) {
        this.definitionLoader = definitionLoader;
    }

    public void setProcessDictionaryRegistry(ProcessDictionaryRegistry processDictionaryRegistry) {
        this.processDictionaryRegistry = processDictionaryRegistry;
    }

    public String getScriptEngineType() {
        return scriptEngineType;
    }

    public void setScriptEngineType(String scriptEngineType) {
        this.scriptEngineType = scriptEngineType;
    }

    public String getScriptExternalUrl() {
        return scriptExternalUrl;
    }

    public void setScriptExternalUrl(String scriptExternalUrl) {
        this.scriptExternalUrl = scriptExternalUrl;
    }

    public String getScriptSourceCode() {
        return scriptSourceCode;
    }

    public void setScriptSourceCode(String scriptSourceCode) {
        this.scriptSourceCode = scriptSourceCode;
    }

    @Override
    public void setContext(ProcessStateConfiguration state, ProcessStateWidget configuration, I18NSource i18NSource,
                           ProcessToolBpmSession bpmSession, Application application, Set<String> permissions, boolean isOwner) {
        super.setContext(state, configuration, i18NSource, bpmSession,
                application, permissions, isOwner);
        ProcessToolContext ctx = ProcessToolContext.Util.getThreadProcessToolContext();
        processDictionaryRegistry = ctx.getProcessDictionaryRegistry();
    }

    private abstract class ComponentEvaluator<T> {
        protected T currentComponent;
        protected WidgetElement currentElement;

        protected ComponentEvaluator(Map<T, WidgetElement> input) {
            try {
                if (input != null) {
               
                    for (Entry<T, WidgetElement> entry : input.entrySet()) {
                        evaluate(currentComponent = entry.getKey(), currentElement = entry.getValue());
                    }
                 
                }
            } catch (Exception e) {
                handleException(getMessage("processdata.block.error.eval.other")
                        .replaceFirst("%s", nvl(currentComponent.toString(), "NIL"))
                        .replaceFirst("%s", nvl(currentElement.getBind(), "NIL")), e);
            }
        }

        public abstract void evaluate(T component, WidgetElement element) throws Exception;
    }

    @Override
    public Collection<String> validateData(final BpmTask task, boolean skipRequired) {
        final List<String> errors = new ArrayList<String>();
        new ComponentEvaluator<Property>(boundProperties) {
            @Override
            public void evaluate(Property component, WidgetElement element) throws Exception {
                if (component instanceof Validatable) {
                    Validatable validatable = (Validatable) component;
                    try {
                        validatable.validate();
                    } catch (InvalidValueException e) {
                        errors.add(e.getMessage());
                    }
                }
                if (!component.isReadOnly()) {
                    try {
                        fetchOrCreateAttribute(element);
                    } catch (Exception e) {
                        errors.add(getMessage("processdata.block.error.eval.other").replaceFirst("%s",
                                component.toString()).replaceFirst("%s", element.getBind()) +
                                "<br/>" + e.getMessage());
                    }
                }
            }
        };
        return errors;
    }

    private ProcessInstanceAttribute fetchOrCreateAttribute(WidgetElement element) throws InstantiationException, IllegalAccessException,
            ClassNotFoundException, InvocationTargetException, NoSuchMethodException {
        int index = element.getBind().indexOf('.');
        String attributeName = index == -1 ? element.getBind() : element.getBind().substring(0, index);
        ProcessInstanceAttribute attribute = processAttributes.get(attributeName);
        if (attribute == null && (hasText(element.getInheritedAttributeClass()) || index == -1)) {
            attribute = hasText(element.getInheritedAttributeClass())
                    ? (ProcessInstanceAttribute) getClass().getClassLoader().loadClass(element.getInheritedAttributeClass()).newInstance()
                    : new ProcessInstanceSimpleAttribute();
            attribute.setProcessInstance(processInstance);
            attribute.setKey(attributeName);

            processAttributes.put(attributeName, attribute);
        }
        if (index != -1) {
            String propertyName = element.getBind().substring(index + 1);
            Object bean = attribute;
            while (resolver.hasNested(propertyName)) {
                String next = resolver.next(propertyName);
                Object nestedBean = resolver.isMapped(next) ? PropertyUtils.getMappedProperty(bean, next)
                        : resolver.isIndexed(next) ? PropertyUtils.getIndexedProperty(bean, next)
                        : PropertyUtils.getSimpleProperty(bean, next);
                if (nestedBean == null) {
                    Class clazz = PropertyUtils.getPropertyType(bean, next);
                    PropertyUtils.setProperty(bean, next, nestedBean = clazz.newInstance());
                }
                bean = nestedBean;
                propertyName = resolver.remove(propertyName);
            }
        }
        return attribute;
    }

    @Override
    public void saveData(final BpmTask task) {
        ProcessInstance pi = task.getProcessInstance();
        processAttributes.clear();
        for (ProcessInstanceAttribute attribute : pi.getProcessAttributes()) {
            processAttributes.put(attribute.getKey(), attribute);
        }

        new ComponentEvaluator<Property>(boundProperties) {
            @Override
            public void evaluate(Property component, WidgetElement element) throws Exception {
                if (!component.isReadOnly()) {
                    ProcessInstanceAttribute attribute = fetchOrCreateAttribute(element);
                    if (component instanceof FileUploadComponent) {
                        ProcessInstanceAttachmentAttribute attachment = (ProcessInstanceAttachmentAttribute) component.getValue();
                        if (attachment==null) return;
                        attachment.setProcessState(task.getTaskName());
                        attachment.setProcessInstance(task.getProcessInstance());
                        attachment.setKey(attribute.getKey());
                        PropertyUtils.setProperty(processAttributes, element.getBind(), component.getValue());
                    } else if (attribute instanceof ProcessInstanceSimpleAttribute) {
                        if (element instanceof DateWidgetElement) {
                            String dateString = null;
                            if (component.getValue() != null)
                                dateString = new SimpleDateFormat(((DateWidgetElement) element).getFormat()).format(component.getValue());
                            ((ProcessInstanceSimpleAttribute) attribute).setValue(dateString);
                        } else if (component.getValue() != null) {
                            ((ProcessInstanceSimpleAttribute) attribute).setValue(component.getValue().toString());
                        }
                    } else {
                        PropertyUtils.setProperty(processAttributes, element.getBind(), component.getValue());
                    }
                }
            }
        };
        pi.setProcessAttributes(new HashSet<ProcessInstanceAttribute>(processAttributes.values()));
    }

    @Override
    public void loadData(final BpmTask task) {
        boundProperties.clear();
        dictContainers.clear();
        if (Strings.hasText(widgetsDefinition)) {
            widgetsDefinitionElement = (WidgetsDefinitionElement) definitionLoader.unmarshall(widgetsDefinition);
        }
        this.processInstance = task.getProcessInstance();
        processAttributes.clear();
        for (ProcessInstanceAttribute attribute : processInstance.getProcessAttributes()) {
            processAttributes.put(attribute.getKey(), attribute);
        }
    }

    private void loadBindings() {
        new ComponentEvaluator<Property>(boundProperties) {
            @Override
            public void evaluate(Property component, WidgetElement element) throws Exception {
             
              allowFileldsListener=false;    // not so great but works, needs further works.
                Object value = null;
                try {
                    value = PropertyUtils.getProperty(processAttributes, element.getBind());
                } catch (NestedNullException e) {
                    logger.log(Level.SEVERE, e.getMessage(), e);
                }
               
                // file upload fix
                if (component instanceof FileUploadComponent && value instanceof ProcessInstanceAttachmentAttribute){
                  element.setValue(value);
                  component.setValue(value);
                }
               
                value = value instanceof ProcessInstanceSimpleAttribute ?
                        ((ProcessInstanceSimpleAttribute) value).getValue() : value;
                if (value != null) {
                    boolean readonly = component.isReadOnly();
                    if (readonly) {
                        component.setReadOnly(false);
                    }
                    if (Date.class.isAssignableFrom(component.getType())) {
            DateWidgetElement dwe = Lang2.assumeType(element, DateWidgetElement.class);
            Date v = new SimpleDateFormat(dwe.getFormat()).parse(String.valueOf(value));
                        component.setValue(v);
                    } else if (String.class.isAssignableFrom(component.getType())) {
                        component.setValue(nvl(value, ""));
                    } else if (component instanceof Container &&
                            component.getType().isAssignableFrom(value.getClass())) {
                        if (((Container) component).containsId(value)) {
                            component.setValue(value);
                        }
                    }
                    if (readonly) {
                        component.setReadOnly(true);
                    }
                }
                allowFileldsListener=true;
            }
        };
    }

    private void loadDictionaries() {
        new ComponentEvaluator<AbstractSelect>(dictContainers) {
            @Override
            public void evaluate(AbstractSelect component, WidgetElement element) throws Exception {
             
                ProcessDictionary dict = nvl(element.getGlobal(), false) ?
                        processDictionaryRegistry.getSpecificOrDefaultGlobalDictionary(element.getProvider(),
                                element.getDict(), i18NSource.getLocale().toString()) :
                        processDictionaryRegistry.getSpecificOrDefaultProcessDictionary(
                                processInstance.getDefinition(), element.getProvider(),
                                element.getDict(), i18NSource.getLocale().toString());

                if (dict != null) {
                    Date validForDate = getValidForDate(element);
                    int i = 0;
                    for (Object o : dict.items()) {
                        ProcessDictionaryItem item = (ProcessDictionaryItem) o;
                        component.addItem(item.getKey());
                        String itemKey = item.getKey().toString();
                        ProcessDictionaryItemValue val = item.getValueForDate(validForDate);
                        String message = getMessage((String) (val != null ? val.getValue() : item.getKey()));
                        component.setItemCaption(item.getKey(),message);
                        if (element instanceof AbstractSelectWidgetElement) {
                            AbstractSelectWidgetElement select = (AbstractSelectWidgetElement) element;
                            if (select.getDefaultSelect() != null && i == select.getDefaultSelect()) {
                                component.setValue(item.getKey());
                            }
                            List<ItemElement> values = select.getValues();
                            values.add(new ItemElement(itemKey, message));
                           
                        }
                       
                        ++i;
                    }
                }
               
            }
        };
    }


    private void loadProcessInstanceDictionaries() {
        new ComponentEvaluator<AbstractSelect>(instanceDictContainers) {
            @Override
            public void evaluate(AbstractSelect component, WidgetElement element) throws Exception {
                AbstractSelectWidgetElement aswe = Lang2.assumeType(element, AbstractSelectWidgetElement.class);
                String dictAttribute = aswe.getDictionaryAttribute();
                ProcessInstanceDictionaryAttribute dict = (ProcessInstanceDictionaryAttribute) processInstance.findAttributeByKey(dictAttribute);
                if (dict != null) {
                    int i = 0;
                    Boolean prevReadOnly = component.isReadOnly();
                    component.setReadOnly(false);
                    for (Object o : dict.getItems()) {
                        ProcessInstanceDictionaryItem itemProcess = (ProcessInstanceDictionaryItem) o;
                        component.addItem(itemProcess.getKey());
                        String itemKey = itemProcess.getKey().toString();
                        String value = itemProcess.getValue();
                        component.setItemCaption(itemProcess.getKey(), value);
                        if (element instanceof AbstractSelectWidgetElement) {
                            AbstractSelectWidgetElement select = (AbstractSelectWidgetElement) element;
                            if (select.getDefaultSelect() != null && i == select.getDefaultSelect()) {
                                component.setValue(itemProcess.getKey());
                            }
                            List<ItemElement> values = select.getValues();
                            values.add(new ItemElement(itemKey, value));
                        }
                        ++i;
                    }
                    component.setReadOnly(prevReadOnly);
                }
            }
        };
    }

    private Date getValidForDate(WidgetElement element) throws Exception {
        Date validForDate = processInstance.getCreateDate();
        if (Strings.hasText(element.getValidFor())) {
            String[] splits = element.getValidFor().split(":");
            if (splits.length != 2) {
                throw new IllegalArgumentException("Error while loading values for "
                        + (nvl(element.getGlobal(), false) ? "global" : "process") + " dictionary["
                        + element.getDict() + "], provider[" + element.getProvider() + "]: "
                        + "Illegal argument: " + element.getValidFor()
                        + " for element: " + element.getClass().getName());
            }
            String controlChar = splits[0];
            String validFor = splits[1];
            DateFormat dateFormat = VaadinUtility.simpleDateFormat();
            if ("d".equalsIgnoreCase(controlChar) && !"current".equalsIgnoreCase(validFor)) {
                validForDate = dateFormat.parse(validFor);
            } else if ("a".equalsIgnoreCase(controlChar)) {
                Object attributeValue = PropertyUtils.getProperty(processAttributes, validFor);
                if (attributeValue instanceof String) {
                    validForDate = dateFormat.parse(validFor);
                } else if (attributeValue instanceof Date) {
                    validForDate = (Date) attributeValue;
                } else {
                    throw new IllegalArgumentException("Unable to date from property: " + validFor
                            + "for element: " + element.getClass().getName());
                }
            }
        }
        return validForDate;
    }

    @Override
    public void addChild(ProcessToolWidget child) {
        throw new IllegalArgumentException("children are not supported in this widget");
    }

    @Override
    public Component render() {
        return widgetsDefinitionElement != null ? renderInternal() : new Label(getMessage("processdata.block.nothing.to.render"));
    }

    /**
     * In subclasses without access to proper application object this method should be overridden
     * TODO: rethink this approach
     */
    protected void handleException(String message, Exception e) {
        logger.log(Level.SEVERE, message, e);
        VaadinUtility.validationNotification(getApplication(), i18NSource, message + "<br/>" + e.getMessage());
    }

    private Component renderInternal() {
        try {
            mainPanel = !hasText(widgetsDefinitionElement.getClassName()) ? new VerticalLayout()
                    : (ComponentContainer) getClass().getClassLoader().loadClass(widgetsDefinitionElement.getClassName()).newInstance();
        } catch (Exception e) {
            handleException(getMessage("processdata.block.error.load.class").replaceFirst("%s", widgetsDefinitionElement.getClassName()), e);
        }

        setupWidget(widgetsDefinitionElement, mainPanel);

        List<AbstractComponent> dynamicValidationComponents = createComponents();
       
     
        loadDictionaries();
        loadProcessInstanceDictionaries();
        loadBindings();
       
        addListinersToComponents(dynamicValidationComponents);
        if (executeScript()) {

            mainPanel.removeAllComponents();
            try {
                for (WidgetElement we : widgetsDefinitionElement.getWidgets()) {
                    processWidgetElement(widgetsDefinitionElement, we, mainPanel);
                }
            } catch (Exception e) {
                handleException(getMessage("widget.process_data_block.editor.validation.script.error"), e);
                mainPanel = null;
            }
        }
       
      
       

        return mainPanel;
    }

    private void addListinersToComponents(
      List<AbstractComponent> dynamicValidationComponents) {
      for (AbstractComponent component : dynamicValidationComponents) {
     
   
      ((Field) component).addListener(new ValueChangeListener() {
            @Override
            public void valueChange(ValueChangeEvent event) {
              boolean executedScript = executeScript();
                if (!executedScript){
                    return;
                }
                rebuildForm();
             
            }
        });
      }
  }

  private boolean executeScript() {
     
     
        boolean executed = false;
        try {
            if (!hasText(getScriptEngineType()) || !hasText(getScriptSourceCode()) && !hasText(getScriptExternalUrl()))
                return executed;
            Map<String, Object> fields = getFieldsMap(widgetsDefinitionElement.getWidgets());
            fields.put("process", processInstance);

            ScriptProcessorRegistry registry = ProcessToolContext.Util.getThreadProcessToolContext().getRegistry().lookupService(
                    ScriptProcessorRegistry.class.getName());
//          TODO: some smart cacheing
            InputStream is = loadScriptCode();
            ScriptProcessor scriptProcessor = registry.getScriptProcessor(getScriptEngineType());
            if (scriptProcessor == null) {
                logger.severe("Script processor not found: " + getScriptEngineType() + ", skipping script execution. ");
                return executed;
            }
            scriptProcessor.process(fields, is);
            executed = true;
            boundProperties.clear();
         //   dictContainers.clear();

        } catch (Exception e) {
            handleException(getMessage("widget.process_data_block.editor.validation.script.error"), e);
            executed = false;
        }
        return executed;
    }


    private Map<String, Object> getFieldsMap(List<WidgetElement> widgets) {
        Map<String, Object> map = new HashMap<String, Object>();
        for (WidgetElement we : widgets) {
            Property property = widgetDataSources.get(we);
            if (property != null && property.getValue() != null)
                we.setValue(property.getValue());
            if (we.getId() != null) {
                map.put(we.getId(), we);
            }
            if (we instanceof HasWidgetsElement)
                map.putAll(getFieldsMap(((HasWidgetsElement) we).getWidgets()));
        }
        return map;
    }

    private InputStream loadScriptCode() {


        if (hasText(getScriptExternalUrl()))
            try {
                return new URL(getScriptExternalUrl()).openStream();
            } catch (IOException e) {
                handleException(getMessage("validation.script.url-io-exception"), e);
            }
        if (hasText(getScriptSourceCode()))
            return new ByteArrayInputStream(getScriptSourceCode().getBytes());
        return null;
    }

    private AbstractComponent processWidgetElement(WidgetElement parent, WidgetElement element, ComponentContainer container) {
        AbstractComponent component = null;
        if (element instanceof LabelWidgetElement) {
            component = createLabelField((LabelWidgetElement) element);
        } else if (element instanceof InputWidgetElement) {
            component = createInputField((InputWidgetElement) element);
        } else if (element instanceof VerticalLayoutWidgetElement) {
            component = createVerticalLayout((VerticalLayoutWidgetElement) element);
        } else if (element instanceof HorizontalLayoutWidgetElement) {
            component = createHorizontalLayout((HorizontalLayoutWidgetElement) element);
        } else if (element instanceof AlignElement) {
            applyAlignment((AlignElement) element, container);
        } else if (element instanceof DateWidgetElement) {
            component = createDateField((DateWidgetElement) element);
        } else if (element instanceof FormWidgetElement) {
            component = createFormField((FormWidgetElement) element);
        } else if (element instanceof GridWidgetElement) {
            component = createGrid((GridWidgetElement) element);
        } else if (element instanceof LinkWidgetElement) {
            component = createLink((LinkWidgetElement) element);
        } else if (element instanceof ComboboxSelectElementWidget) {
            component = createComboSelectField((ComboboxSelectElementWidget) element);
        } else if (element instanceof RadioButtonSelectElementWidget) {
            component = createRadioButtonSelectField((RadioButtonSelectElementWidget) element);
        } else if (element instanceof TextAreaWidgetElement) {
            component = createTextAreaField((TextAreaWidgetElement) element);
        } else if (element instanceof CheckBoxWidgetElement) {
            component = createCheckBoxField((CheckBoxWidgetElement) element);
        } else if (element instanceof UploadWidgetElement) {
            component = createFileUploadField((UploadWidgetElement) element);
        }

        if (component != null) {
            component.setImmediate(true);
            component.setEnabled(hasPermission("EDIT"));
            if (component.isReadOnly() || !component.isEnabled()) {
                component.setHeight(null);
            }
            setupWidget(element, component);
            container.addComponent(component);
        }
        element.setParent(parent);

        if (component instanceof Field) {
            Property property = (Property) component;
            widgetDataSources.put(element, property);

            if (element.getDynamicValidation() == Boolean.TRUE)
                ((Field) component).addListener(new ValueChangeListener() {
                    @Override
                    public void valueChange(ValueChangeEvent event) {
                      if(allowFileldsListener){ // not so great, but works, needs further works.
                      boolean executedScript = executeScript();
                        if (!executedScript){
                            return;
                        }

                        mainPanel.removeAllComponents();
                        for (WidgetElement we : widgetsDefinitionElement.getWidgets()) {
                            processWidgetElement(widgetsDefinitionElement, we, mainPanel);
                        }
          }
                    }
                });
        }

        performAdditionalProcessing(element, component);

        return component;
    }
   
    private List<AbstractComponent> createComponents(){
      List<AbstractComponent> dynamicValidationElements = new ArrayList<AbstractComponent>();
     
      mainPanel.removeAllComponents();
        for (WidgetElement we : widgetsDefinitionElement.getWidgets()) {
            AbstractComponent component = processWidgetElement(widgetsDefinitionElement, we, mainPanel);
            if (component instanceof Field) {
                if (we.getDynamicValidation() == Boolean.TRUE){
                  dynamicValidationElements.add(component);
                }
            }
        }
        return dynamicValidationElements;
     
    }
   
    private void rebuildForm(){
List<AbstractComponent> dynamicValidationComponents = createComponents();
addListinersToComponents(dynamicValidationComponents);
     
    }
  

    /**
     * Override in subclasses for additional element/component processing
     *
     * @param element
     * @param component
     */
    protected void performAdditionalProcessing(WidgetElement element, AbstractComponent component) {
//      NOOP
    }

    private AbstractComponent createRadioButtonSelectField(RadioButtonSelectElementWidget element) {
        OptionGroup radioSelect = new OptionGroup();
        radioSelect.setNullSelectionAllowed(false);
        radioSelect.setCaption(element.getCaption());
        radioSelect.setHtmlContentAllowed(true);
//        if(element.getValues().isEmpty())
        for (int i = 0; i < element.getValues().size(); i++) {
            ItemElement item = element.getValues().get(i);
            radioSelect.addItem(item.getKey());
            radioSelect.setItemCaption(item.getKey(), item.getValue());


            if (radioSelect.getValue() == null && element.getDefaultSelect() != null && i == element.getDefaultSelect()) {
                radioSelect.setValue(item.getKey());
            }

        }
        if (element.getValue() != null) {
            radioSelect.setValue(element.getValue());
        }
        if (nvl(element.getRequired(), false)) {
            radioSelect.setRequired(true);
            if (hasText(element.getCaption())) {
                radioSelect.setRequiredError(getMessage("processdata.block.field-required-error") + " " + element.getCaption());
            } else {
                radioSelect.setRequiredError(getMessage("processdata.block.field-required-error"));
            }
        }
        return radioSelect;
    }

    private AbstractComponent createFileUploadField(UploadWidgetElement element) {
        FileUploadComponent upload = new FileUploadComponent(i18NSource);
        if(element.getValue()!=null){
          upload.setValue(element.getValue());
         
        }
        return upload;
    }

    private CheckBox createCheckBoxField(CheckBoxWidgetElement we) {
        CheckBox cb = new CheckBox();
        if (we.getDefaultSelect() != null && we.getDefaultSelect()) {
            cb.setValue(we.getDefaultSelect());
        }
        if (we.getValue() != null)
            cb.setValue(we.getValue());
        return cb;
    }

    private Select createComboSelectField(ComboboxSelectElementWidget swe) {
        Select select = new Select();
        if (!swe.getValues().isEmpty()) {
            for (int i = 0; i < swe.getValues().size(); ++i) {
                ItemElement item = swe.getValues().get(i);
                select.addItem(item.getKey());
                select.setItemCaption(item.getKey(), item.getValue());
                if (swe.getDefaultSelect() != null && i == swe.getDefaultSelect()) {
                    select.setValue(item.getKey());
                }
            }
            if (swe.getValue() != null)
                select.setValue(swe.getValue());
        }

        if (nvl(swe.getRequired(), false)) {
            select.setRequired(true);
            if (hasText(swe.getCaption())) {
                select.setRequiredError(getMessage("processdata.block.field-required-error") + " " + swe.getCaption());
            } else {
                select.setRequiredError(getMessage("processdata.block.field-required-error"));
            }
        }
        return select;
    }

//    private void processScriptElement(Select select, ScriptElement script) {
//        throw new RuntimeException("Not implemented yet!");
//    }

    private Form createFormField(FormWidgetElement fwe) {
        FormLayout layout = new FormLayout();
        processOrderedLayout(fwe, layout);
        Form form = new Form(layout);
        return form;
    }

    private AbstractComponent createTextAreaField(TextAreaWidgetElement taw) {
        AbstractComponent component;
        if (taw.getRich() != null && taw.getRich()) {
            RichTextArea rta = new RichTextArea();
            if (taw.getVisibleLines() != null) {
                rta.setHeight(taw.getVisibleLines() * 2 + 4, Sizeable.UNITS_EM);
            }
            if (taw.getLimit() != null) {
                rta.addValidator(new StringLengthValidator(getMessage("processdata.block.error.text.exceeded").replaceFirst("%s",
                        "" + taw.getLimit()), 0, taw.getLimit(), true));
            }
            if (nvl(taw.getRequired(), false)) {
                rta.setRequired(true);
                if (hasText(taw.getCaption())) {
                    rta.setRequiredError(getMessage("processdata.block.field-required-error") + " " + taw.getCaption());
                } else {
                    rta.setRequiredError(getMessage("processdata.block.field-required-error"));
                }
            }
            if (!hasPermission("EDIT")) {
                rta.setReadOnly(true);
                rta.setHeight(null);
            }
            if (taw.getValue() != null) {
                rta.setValue(taw.getValue());
      }

            component = rta;
        } else {
            TextArea ta = new TextArea();
            if (taw.getVisibleLines() != null) {
                ta.setRows(taw.getVisibleLines());
            }
            if (taw.getLimit() != null) {
                ta.setMaxLength(taw.getLimit());
            }

            if (nvl(taw.getRequired(), false)) {
                ta.setRequired(true);
                if (hasText(taw.getCaption())) {
                    ta.setRequiredError(getMessage("processdata.block.field-required-error") + " " + taw.getCaption());
                } else {
                    ta.setRequiredError(getMessage("processdata.block.field-required-error"));
                }
            }
            if (taw.getValue() != null) {
                ta.setValue(taw.getValue());
      }

            component = ta;
        }

        return component;
    }

    private Link createLink(LinkWidgetElement we) {
        Link link = new Link();
        link.setTargetName("_blank");
        String url = we.getUrl();
    if(url.matches("#\\{.*\\}")){
          String urlKey = url.replaceAll("#\\{(.*)\\}", "$1");
          if(processAttributes.containsKey(urlKey))
            url = ((ProcessInstanceSimpleAttribute)processAttributes.get(urlKey)).getValue();
        }
        link.setResource(new ExternalResource(url));
        return link;
    }

    private GridLayout createGrid(GridWidgetElement gwe) {
        GridLayout grid = new GridLayout();
        if (gwe.getCols() != null) {
            grid.setColumns(gwe.getCols());
        }
        if (gwe.getRows() != null) {
            grid.setRows(gwe.getRows());
        }
        processOrderedLayout(gwe, grid);
        return grid;
    }

    private VerticalLayout createVerticalLayout(VerticalLayoutWidgetElement vlw) {
        VerticalLayout vl = new VerticalLayout();
        vl.setSpacing(true);
        processOrderedLayout(vlw, vl);
        return vl;
    }

    private HorizontalLayout createHorizontalLayout(HorizontalLayoutWidgetElement hlw) {
        HorizontalLayout hl = new HorizontalLayout();
        hl.setSpacing(true);
        processOrderedLayout(hlw, hl);
        return hl;
    }

    private void processOrderedLayout(HasWidgetsElement hwe, AbstractLayout al) {
        for (WidgetElement we : hwe.getWidgets()) {
            Component widget = processWidgetElement(hwe, we, al);
            if (widget != null && al instanceof AbstractOrderedLayout) {
                AbstractOrderedLayout aol = (AbstractOrderedLayout) al;
                aol.setExpandRatio(widget, 1f);
            }
        }
    }

    private void applyAlignment(AlignElement ae, ComponentContainer container) {
        if (container instanceof AlignmentHandler) {
            AlignmentHandler ah = (AlignmentHandler) container;
            for (WidgetElement awe : ae.getWidgets()) {
                Component widget = processWidgetElement(ae, awe, container);
                if (widget != null) {
                    if (XmlConstants.ALIGN_POS_CENTER_TOP.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, TOP_CENTER);
                    } else if (XmlConstants.ALIGN_POS_LEFT_TOP.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, TOP_LEFT);
                    } else if (XmlConstants.ALIGN_POS_RIGHT_TOP.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, TOP_RIGHT);
                    } else if (XmlConstants.ALIGN_POS_CENTER_MIDDLE.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, MIDDLE_CENTER);
                    } else if (XmlConstants.ALIGN_POS_LEFT_MIDDLE.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, MIDDLE_LEFT);
                    } else if (XmlConstants.ALIGN_POS_RIGHT_MIDDLE.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, MIDDLE_RIGHT);
                    } else if (XmlConstants.ALIGN_POS_CENTER_BOTTOM.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, BOTTOM_CENTER);
                    } else if (XmlConstants.ALIGN_POS_LEFT_BOTTOM.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, BOTTOM_LEFT);
                    } else if (XmlConstants.ALIGN_POS_RIGHT_BOTTOM.equals(ae.getPos())) {
                        ah.setComponentAlignment(widget, BOTTOM_RIGHT);
                    }
                }
            }
        }
    }

    private AbstractTextField createInputField(InputWidgetElement iwe) {
        AbstractTextField field = iwe.getSecret() != null && iwe.getSecret() ? new PasswordField() : new TextField();
        if (iwe.getMaxLength() != null) {
            field.setMaxLength(iwe.getMaxLength());
        }
        if (hasText(iwe.getRegexp()) && hasText(iwe.getRegexp())) {
            field.addValidator(new RegexpValidator(WidgetDefinitionLoader.replaceXmlEscapeCharacters(iwe.getRegexp()), iwe.getErrorKey() != null ?
                    iwe.getErrorKey() : getMessage("processdata.block.error.regexp").replaceFirst("%s", iwe.getRegexp())));
        }
        if (nvl(iwe.getRequired(), false)) {
            field.setRequired(true);
            if (hasText(iwe.getCaption())) {
                String caption = iwe.getCaption();
                if (caption.endsWith(":"))
                    caption = caption.substring(0, caption.length() - 1);
                field.setRequiredError(getMessage("processdata.block.field-required-error") + " " + caption);
            } else {
                field.setRequiredError(getMessage("processdata.block.field-required-error"));
            }
        }
        if (hasText(iwe.getBaseText())) {
            field.setValue(getMessage(iwe.getBaseText()));
        }
        if (hasText(iwe.getPrompt())) {
            field.setInputPrompt(getMessage(iwe.getPrompt()));
        }
        if (iwe.getValue() != null)
            field.setValue(iwe.getValue());
        return field;
    }

    private Label createLabelField(LabelWidgetElement lwe) {
        Label label = new Label();
        if (lwe.getMode() != null) {
            label.setContentMode(lwe.getMode());
        }
        if (hasText(lwe.getText())) {
            label.setValue(WidgetDefinitionLoader.removeCDATATag(WidgetDefinitionLoader.replaceXmlEscapeCharacters(lwe.getText())));
        }
        if (lwe.getValue() != null)
            label.setValue(lwe.getValue());
        return label;
    }

    private DateField createDateField(final DateWidgetElement dwe) {
        final SimpleDateFormat sdf;
       try {
           sdf = new SimpleDateFormat(dwe.getFormat());
       } catch (Exception e) {
           handleException(getMessage("processdata.block.error.unparsable.format").replaceFirst("%s", dwe.getFormat()), e);
           return null;
       }

       final PopupDateField field = new PopupDateField();
       final boolean fieldMinResolution = dwe.getShowMinutes() != null && dwe.getShowMinutes();

       field.setDateFormat(dwe.getFormat());
       field.setResolution(fieldMinResolution ? DateField.RESOLUTION_MIN : DateField.RESOLUTION_DAY);
       if (hasText(dwe.getNotAfter())) {
           try {
               boolean usesCurrent = XmlConstants.DATE_CURRENT.equalsIgnoreCase(dwe.getNotAfter());
               final Date notAfter = (usesCurrent) ? sdf.parse(sdf.format(new Date())) : sdf.parse(dwe.getNotAfter());
               field.addValidator(new AbstractValidator(getMessage("processdata.block.error.date.notafter").replaceFirst("%s", dwe.getNotAfter())) {
          @Override
          public boolean isValid(Object value) {
            Date formatedDateFromCalendarInput = formatTimeFromCalendarInput((Date)value,sdf);
            return value == null ||  isBeforeCurrentDate(formatedDateFromCalendarInput);
          }
          private boolean isBeforeCurrentDate(Date formatedDateFromCalendarInput){

            return  isRightSideOpen()?  isBeforeCurrentDateRightSideOpen(formatedDateFromCalendarInput):isBeforeCurrentDateRightSideClosed(formatedDateFromCalendarInput);

            }

            private boolean isRightSideOpen(){
            if(dwe.getDiscludeNotAfter()==null){
              return false;
            }
            else{
              return dwe.getDiscludeNotAfter();
            }
          }


            private boolean isBeforeCurrentDateRightSideClosed(Date formatedDateFromCalendarInput){

              return notAfter.equals(formatedDateFromCalendarInput)? true : isNotAfter(formatedDateFromCalendarInput);
            }

            private boolean isBeforeCurrentDateRightSideOpen(Date formatedDateFromCalendarInput){
             
              return   notAfter.equals(formatedDateFromCalendarInput)? false : isNotAfter(formatedDateFromCalendarInput);
            }
           
            private boolean isNotAfter(Date formatedDateFromCalendarInput){
              return   !notAfter.before(formatedDateFromCalendarInput);
             
            }
        });
               //why notify and interrupt?
               //we already have a perfect validation mechanisms
               //so let's use classic validators
//               field.addListener(new ValueChangeListener() {
//                   @Override
//                   public void valueChange(ValueChangeEvent event) {
//                       Object value = event.getProperty().getValue();
//                       if (value != null && value instanceof Date) {
//                           if (notAfter.before((Date) value)) {
////                               TODO: TODO: notification fails on preview, because application object is only a stub
//                               VaadinUtility.validationNotification(getApplication(), i18NSource,
//                                       getMessage("processdata.block.error.date.notafter").replaceFirst("%s", dwe.getNotAfter()));
//                               field.setValue(notAfter);
//                           }
//                       }
//                   }
//               });
           } catch (ParseException e) {
               handleException(getMessage("processdata.block.error.unparsable.date").replaceFirst("%s", dwe.getNotAfter()), e);
           }
       }
       if (hasText(dwe.getNotBefore())) {
           try {
               boolean usesCurrent = XmlConstants.DATE_CURRENT.equalsIgnoreCase(dwe.getNotBefore());
               final Date notBefore = (usesCurrent) ? sdf.parse(sdf.format(new Date())) : sdf.parse(dwe.getNotBefore());
               field.addValidator(new AbstractValidator(getMessage("processdata.block.error.date.notbefore").replaceFirst("%s", dwe.getNotBefore())) {
          @Override
          public boolean isValid(Object value) {
            Date formatedDateFromCalendarInput = formatTimeFromCalendarInput((Date)value,sdf);
            return value == null || isAfterCurrentDate(formatedDateFromCalendarInput);
          }

          private boolean isAfterCurrentDate(Date formatedDateFromCalendarInput){

          return  isLeftSideOpen()?  isAfterCurrentDateLeftSideOpen(formatedDateFromCalendarInput):isAfterCurrentDateLeftSideClosed(formatedDateFromCalendarInput);

          }

          private boolean isLeftSideOpen(){
            if(dwe.getDiscludeNotBefore()==null){
              return false;
            }
            else{
            return dwe.getDiscludeNotBefore();
            }
          }

          private boolean isAfterCurrentDateLeftSideClosed(Date formatedDateFromCalendarInput){

            return notBefore.equals(formatedDateFromCalendarInput)? true : isNotBefore(formatedDateFromCalendarInput);
          }

          private boolean isAfterCurrentDateLeftSideOpen(Date formatedDateFromCalendarInput){

            return  notBefore.equals(formatedDateFromCalendarInput)? false : isNotBefore(formatedDateFromCalendarInput);
          }
         
         
         
          private boolean isNotBefore(Date formatedDateFromCalendarInput){
            return   !notBefore.after(formatedDateFromCalendarInput);
           
          }
        });
//               field.addListener(new ValueChangeListener() {
//                   @Override
//                   public void valueChange(ValueChangeEvent event) {
//                       Object value = event.getProperty().getValue();
//                       if (value != null && value instanceof Date) {
//                           if (notBefore.after((Date) value)) {
////                               TODO: notification fails on preview, because application object is only a stub
//                               VaadinUtility.validationNotification(getApplication(), i18NSource,
//                                       getMessage("processdata.block.error.date.notbefore").replaceFirst("%s", dwe.getNotBefore()));
//                               field.setValue(notBefore);
//                           }
//                       }
//                   }
//               });
           } catch (ParseException e) {
               handleException(getMessage("processdata.block.error.unparsable.date").replaceFirst("%s", dwe.getNotBefore()), e);
           }
       }
       if (nvl(dwe.getRequired(), false)) {
           field.setRequired(true);
           if (hasText(dwe.getCaption())) {
               field.setRequiredError(getMessage("processdata.block.field-required-error") + " " + dwe.getCaption());
           } else {
               field.setRequiredError(getMessage("processdata.block.field-required-error"));
           }
       }
       if (dwe.getValue() != null)
           field.setValue(dwe.getValue());


       return field;
   }
  
    private Date formatTimeFromCalendarInput(Date date, SimpleDateFormat sdf){
    Date parsedDate;
    try {
      parsedDate = sdf.parse(sdf.format(date));
      return parsedDate;
    } catch (ParseException e) {
      handleException(getMessage("processdata.block.error.unparsable.date").replaceFirst("%s", date.toString()), e);
    }
    return date;
  }
  
    private void setupWidget(WidgetElement we, Component component) {
        if (hasText(we.getCaption())) {
            component.setCaption(getMessage(we.getCaption()));
        }
        if (we.getFullSize() != null && we.getFullSize()) {
            component.setSizeFull();
        }
        if (we.getUndefinedSize() != null && we.getUndefinedSize()) {
            component.setSizeUndefined();
        }
        if (hasText(we.getHeight())) {
            component.setHeight(we.getHeight());
        }
        if (hasText(we.getWidth())) {
            component.setWidth(we.getWidth());
        }
        if (we.getReadonly() != null && we.getReadonly()) {
            component.setReadOnly(we.getReadonly());
        }
        if (hasText(we.getStyle())) {
            component.addStyleName(we.getStyle());
        }
        if (we instanceof HasWidgetsElement && component instanceof SpacingHandler) {
            HasWidgetsElement hwe = (HasWidgetsElement) we;
            if (hwe.getSpacing() != null && hwe.getSpacing()) {
                ((SpacingHandler) component).setSpacing(hwe.getSpacing());
            }
        }

        if (we.getVisible() == Boolean.FALSE) {
            component.setVisible(false);
        }

        if (hasText(we.getBind()) && component instanceof Property) {
            Property property = (Property) component;
            boundProperties.put(property, we);
        }

        if (hasText(we.getDict()) && hasText(we.getProvider()) && component instanceof AbstractSelect) {
            AbstractSelect select = (AbstractSelect) component;
            dictContainers.put(select, we);
        }

        if (we instanceof AbstractSelectWidgetElement) {
            AbstractSelectWidgetElement aswe = (AbstractSelectWidgetElement) we;
            if (!hasText(we.getDict()) && !hasText(we.getProvider()) && hasText(aswe.getDictionaryAttribute())) {
                AbstractSelect select = (AbstractSelect) component;
                instanceDictContainers.put(select, aswe);

            }
        }
    }

    public String getWidgetsDefinition() {
        return widgetsDefinition;
    }

    public void setWidgetsDefinition(String widgetsDefinition) {
        this.widgetsDefinition = widgetsDefinition;
    }

}
TOP

Related Classes of pl.net.bluesoft.rnd.processtool.ui.basewidgets.ProcessDataBlockWidget$ComponentEvaluator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.