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; } }