package pl.net.bluesoft.rnd.processtool.ui.dict;
import com.vaadin.Application;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.ui.*;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import org.aperteworkflow.util.dict.ui.DictionaryItemForm;
import org.aperteworkflow.util.dict.ui.DictionaryItemTableBuilder;
import org.aperteworkflow.util.vaadin.GenericVaadinPortlet2BpmApplication;
import org.aperteworkflow.util.vaadin.TransactionProvider;
import org.aperteworkflow.util.vaadin.VaadinUtility;
import pl.net.bluesoft.rnd.processtool.ProcessToolContext;
import pl.net.bluesoft.rnd.processtool.bpm.ProcessToolBpmConstants;
import pl.net.bluesoft.rnd.processtool.bpm.ProcessToolBpmSession;
import pl.net.bluesoft.rnd.processtool.dict.GlobalDictionaryProvider;
import pl.net.bluesoft.rnd.processtool.dict.ProcessDictionaryProvider;
import pl.net.bluesoft.rnd.processtool.model.config.ProcessDefinitionConfig;
import pl.net.bluesoft.rnd.processtool.model.dict.ProcessDictionaryItem;
import pl.net.bluesoft.rnd.processtool.model.dict.db.*;
import pl.net.bluesoft.rnd.processtool.ui.dict.wrappers.DBDictionaryItemValueWrapper;
import pl.net.bluesoft.rnd.processtool.ui.dict.wrappers.DBDictionaryItemWrapper;
import pl.net.bluesoft.rnd.processtool.ui.widgets.ProcessToolGuiCallback;
import pl.net.bluesoft.rnd.util.i18n.I18NSource;
import pl.net.bluesoft.util.lang.Collections;
import pl.net.bluesoft.util.lang.Predicate;
import pl.net.bluesoft.util.lang.Strings;
import pl.net.bluesoft.util.lang.cquery.func.F;
import java.util.*;
import static org.aperteworkflow.util.vaadin.VaadinUtility.*;
import static pl.net.bluesoft.util.lang.cquery.CQuery.from;
public class DictionariesMainPane extends VerticalLayout implements ProcessToolBpmConstants, Refreshable, DictionaryItemTableBuilder.DictionaryItemModificationHandler<DBDictionaryItemWrapper> {
private GenericVaadinPortlet2BpmApplication application;
private I18NSource i18NSource;
private TransactionProvider transactionProvider;
private TabSheet tabSheet;
private Select globalDictionarySelect;
private Select processDefinitionSelect;
private HorizontalLayout processHeaderLayout;
private VerticalLayout processTableLayout;
private BeanItemContainer<ProcessDefinitionConfig> processContainer;
private BeanItemContainer<ProcessDBDictionary> globalDictionaryContainer;
private Map<ProcessDBDictionary, BeanItemContainer<DBDictionaryItemWrapper>> dictItemContainers;
private Map<ProcessDefinitionConfig, Map<String, Set<ProcessDBDictionary>>> processDictionariesMap;
private Map<String, Set<ProcessDBDictionary>> globalDictionariesMap;
private DictionaryItemTableBuilder<ProcessDBDictionaryItem, DBDictionaryItemValueWrapper, DBDictionaryItemWrapper> builder;
public DictionariesMainPane(final GenericVaadinPortlet2BpmApplication application, final I18NSource i18NSource, TransactionProvider transactionProvider) {
this.application = application;
this.i18NSource = i18NSource;
this.transactionProvider = transactionProvider;
this.builder = new DictionaryItemTableBuilder<ProcessDBDictionaryItem, DBDictionaryItemValueWrapper, DBDictionaryItemWrapper>(this) {
@Override
protected DictionaryItemForm createDictionaryItemForm(Application application, I18NSource source, BeanItem<DBDictionaryItemWrapper> item) {
return new DBDictionaryItemForm(application, source, item);
}
@Override
protected Application getApplication() {
return application;
}
@Override
protected I18NSource getI18NSource() {
return i18NSource;
}
};
setWidth("100%");
initWidget();
loadData();
}
private void initWidget() {
removeAllComponents();
dictItemContainers = new LinkedHashMap<ProcessDBDictionary, BeanItemContainer<DBDictionaryItemWrapper>>();
processContainer = new BeanItemContainer<ProcessDefinitionConfig>(ProcessDefinitionConfig.class);
globalDictionaryContainer = new BeanItemContainer<ProcessDBDictionary>(ProcessDBDictionary.class);
globalDictionariesMap = new TreeMap<String, Set<ProcessDBDictionary>>();
processDictionariesMap = new TreeMap<ProcessDefinitionConfig, Map<String, Set<ProcessDBDictionary>>>(new Comparator<ProcessDefinitionConfig>() {
@Override
public int compare(ProcessDefinitionConfig o1, ProcessDefinitionConfig o2) {
return o1.getId().compareTo(o2.getId());
}
});
Label titleLabel = new Label(getMessage("dict.title"));
titleLabel.addStyleName("h1 color processtool-title");
titleLabel.setWidth("100%");
processDefinitionSelect = select(getMessage("process.name"), processContainer, "description");
processDefinitionSelect.setItemCaptionMode(Select.ITEM_CAPTION_MODE_EXPLICIT);
processDefinitionSelect.addListener(new Property.ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
processConfigSelected((ProcessDefinitionConfig) processDefinitionSelect.getValue());
}
});
HorizontalLayout globalHeaderLayout = fullHorizontalLayout();
VerticalLayout globalTableLayout = verticalLayout();
globalDictionarySelect = createDictionaryNameSelect(globalTableLayout, globalHeaderLayout, globalDictionaryContainer, globalDictionariesMap, null);
VerticalLayout processDictLayout = verticalLayout(processHeaderLayout = fullHorizontalLayout(), processTableLayout = verticalLayout());
VerticalLayout globalDictLayout = verticalLayout(globalHeaderLayout, globalTableLayout);
reloadLayoutsWithComponents(processTableLayout, processHeaderLayout, processDefinitionSelect);
reloadLayoutsWithComponents(globalTableLayout, globalHeaderLayout, globalDictionarySelect);
tabSheet = new TabSheet();
tabSheet.setWidth("100%");
tabSheet.addTab(processDictLayout, getMessage("dict.title.process"), VaadinUtility.imageResource(application, "dict.png"));
tabSheet.addTab(globalDictLayout, getMessage("dict.title.global"), VaadinUtility.imageResource(application, "globe.png"));
addComponent(horizontalLayout(titleLabel, VaadinUtility.refreshIcon(application, this)));
addComponent(new Label(getMessage("dict.help.short")));
addComponent(tabSheet);
}
private void loadData() {
transactionProvider.withTransaction(new ProcessToolGuiCallback() {
@Override
public void callback(ProcessToolContext ctx, ProcessToolBpmSession session) {
Collection<ProcessDefinitionConfig> configs = ctx.getProcessDefinitionDAO().getActiveConfigurations();
processContainer.addAll(configs);
for (ProcessDefinitionConfig config : processContainer.getItemIds()) {
processDefinitionSelect.setItemCaption(config, i18NSource.getMessage(config.getDescription()));
}
ProcessDictionaryProvider pdp = ctx.getProcessDictionaryRegistry().getProcessDictionaryProvider("db");
List<ProcessDBDictionary> dictionaries = pdp.fetchAllActiveProcessDictionaries();
for (ProcessDBDictionary dict : dictionaries) {
if (hasPermissionsForDictionary(dict)) {
Map<String, Set<ProcessDBDictionary>> dictionariesMap = processDictionariesMap.get(dict.getProcessDefinition());
if (dictionariesMap == null) {
dictionariesMap = new TreeMap<String, Set<ProcessDBDictionary>>();
processDictionariesMap.put(dict.getProcessDefinition(), dictionariesMap);
}
groupDictionariesByLocale(dict, dictionariesMap);
bindBeanItemContainer(dict);
}
}
GlobalDictionaryProvider gdp = ctx.getProcessDictionaryRegistry().getGlobalDictionaryProvider("db");
dictionaries = gdp.fetchAllGlobalDictionaries();
for (ProcessDBDictionary dict : dictionaries) {
if (hasPermissionsForDictionary(dict)) {
groupDictionariesByLocale(dict, globalDictionariesMap);
bindBeanItemContainer(dict);
}
}
prepareDistinctDictionaryNameContainer(globalDictionaryContainer, globalDictionariesMap);
}
});
}
private void groupDictionariesByLocale(ProcessDBDictionary dict, Map<String, Set<ProcessDBDictionary>> dictionariesMap) {
String dictId = dict.getDictionaryId();
if (!Strings.hasText(dictId)) {
throw new IllegalArgumentException("Dictionary id cannot be null");
}
Set<ProcessDBDictionary> localeSet = dictionariesMap.get(dictId);
if (localeSet == null) {
localeSet = new TreeSet<ProcessDBDictionary>(new Comparator<ProcessDBDictionary>() {
@Override
public int compare(ProcessDBDictionary o1, ProcessDBDictionary o2) {
return o1.getLanguageCode().compareTo(o2.getLanguageCode());
}
});
dictionariesMap.put(dictId, localeSet);
}
localeSet.add(dict);
}
private void bindBeanItemContainer(ProcessDBDictionary dict) {
BeanItemContainer<DBDictionaryItemWrapper> container = new BeanItemContainer<DBDictionaryItemWrapper>(DBDictionaryItemWrapper.class);
List<ProcessDBDictionaryItem> items = new ArrayList<ProcessDBDictionaryItem>(dict.getItems().values());
container.addAll(from(items).select(new F<ProcessDBDictionaryItem, DBDictionaryItemWrapper>() {
@Override
public DBDictionaryItemWrapper invoke(ProcessDBDictionaryItem x) {
return new DBDictionaryItemWrapper(x);
}
}));
container.sort(new Object[] {"key"}, new boolean[] {false});
dictItemContainers.put(dict, container);
}
private BeanItemContainer<DBDictionaryItemWrapper> getDictionaryItems(ProcessDBDictionary dict) {
return dictItemContainers.get(dict);
}
public void refreshData() {
processDefinitionSelect.setValue(null);
globalDictionarySelect.setValue(null);
processContainer.removeAllItems();
globalDictionaryContainer.removeAllItems();
dictItemContainers.clear();
processDictionariesMap.clear();
globalDictionariesMap.clear();
loadData();
}
private Select createDictionaryNameSelect(final AbstractOrderedLayout tableLayout, final AbstractOrderedLayout headerLayout,
BeanItemContainer<ProcessDBDictionary> dictionaryContainer,
final Map<String, Set<ProcessDBDictionary>> dictionariesMap,
final Component headerComponent) {
final Select dictionaryNameSelect = select(getMessage("dict.dictionaryName"), dictionaryContainer, "dictionaryName");
dictionaryNameSelect.setWidth("300px");
dictionaryNameSelect.addListener(new ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
ProcessDBDictionary dict = (ProcessDBDictionary) dictionaryNameSelect.getValue();
if (dict != null) {
Button addButton = addIcon(application);
addButton.setCaption(getMessage("dict.addentry"));
Component localeSelect = dictionaryNameSelected(addButton, tableLayout, dictionariesMap.get(dict.getDictionaryId()));
reloadLayoutsWithComponents(tableLayout, headerLayout, headerComponent, dictionaryNameSelect, localeSelect, addButton);
}
else {
reloadLayoutsWithComponents(tableLayout, headerLayout, headerComponent, dictionaryNameSelect);
}
}
});
return dictionaryNameSelect;
}
private void showItemTable(AbstractOrderedLayout tableLayout, Component tableComponent) {
tableLayout.removeAllComponents();
if (tableComponent != null) {
tableLayout.addComponent(tableComponent);
tableLayout.setExpandRatio(tableComponent, 1.0F);
}
}
private void reloadLayoutsWithComponents(AbstractOrderedLayout tableLayout, AbstractOrderedLayout headerLayout, Component... components) {
tableLayout.removeAllComponents();
headerLayout.removeAllComponents();
if (components != null && components.length > 0) {
for (Component comp : components) {
if (comp != null) {
headerLayout.addComponent(comp);
headerLayout.setComponentAlignment(comp, Alignment.MIDDLE_LEFT);
}
}
headerLayout.setExpandRatio(headerLayout.getComponent(headerLayout.getComponentCount() - 1), 1.0F);
}
}
private void processConfigSelected(ProcessDefinitionConfig value) {
Select processDictionarySelect = null;
if (value != null) {
final Map<String, Set<ProcessDBDictionary>> localizedDictionariesMap = processDictionariesMap.get(value);
if (localizedDictionariesMap == null || localizedDictionariesMap.isEmpty()) {
informationNotification(application, getMessage("dict.dictsempty"));
}
else {
BeanItemContainer<ProcessDBDictionary> distinctProcessDictionaryContainer =
new BeanItemContainer<ProcessDBDictionary>(ProcessDBDictionary.class);
prepareDistinctDictionaryNameContainer(distinctProcessDictionaryContainer, localizedDictionariesMap);
processDictionarySelect = createDictionaryNameSelect(processTableLayout, processHeaderLayout,
distinctProcessDictionaryContainer, localizedDictionariesMap, processDefinitionSelect);
}
}
reloadLayoutsWithComponents(processTableLayout, processHeaderLayout, processDefinitionSelect, processDictionarySelect);
}
private void prepareDistinctDictionaryNameContainer(BeanItemContainer<ProcessDBDictionary> distinctNameContainer,
Map<String, Set<ProcessDBDictionary>> localizedDictionariesMap) {
Locale locale = i18NSource.getLocale();
for (Set<ProcessDBDictionary> dictSet : localizedDictionariesMap.values()) {
if (dictSet != null && !dictSet.isEmpty()) {
boolean addedEntry = false;
for (ProcessDBDictionary dict : dictSet) {
if (locale.toString().equals(dict.getLanguageCode())) {
distinctNameContainer.addBean(dict);
addedEntry = true;
break;
}
}
if (!addedEntry) {
distinctNameContainer.addBean(dictSet.iterator().next());
}
}
}
}
private Component dictionaryNameSelected(final Button addButton, final AbstractOrderedLayout tableLayout, Set<ProcessDBDictionary> dictSet) {
final Select localeSelect = select(getMessage("dict.locale"),
new BeanItemContainer<ProcessDBDictionary>(ProcessDBDictionary.class, dictSet), "languageCode");
addButton.addListener(new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
showItemDetails(new BeanItem<DBDictionaryItemWrapper>(new DBDictionaryItemWrapper()), new DictionaryItemTableBuilder.SaveCallback<DBDictionaryItemWrapper>() {
@Override
public void onSave(BeanItem<DBDictionaryItemWrapper> item) {
prepareAndSaveNewItem(item, (ProcessDBDictionary) localeSelect.getValue());
}
});
}
});
localeSelect.addListener(new Property.ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
addButton.setVisible(true);
Component processTableComponent = localeSelected((ProcessDBDictionary) localeSelect.getValue());
showItemTable(tableLayout, processTableComponent);
}
});
addButton.setVisible(false);
return localeSelect;
}
private Component localeSelected(ProcessDBDictionary dict) {
BeanItemContainer<DBDictionaryItemWrapper> container = getDictionaryItems(dict);
return builder.createTable(container);
}
public String getMessage(String key) {
return i18NSource.getMessage(key);
}
public String getMessage(String key, String defaultValue) {
return i18NSource.getMessage(key, defaultValue);
}
private void prepareAndSaveNewItem(BeanItem<DBDictionaryItemWrapper> item, ProcessDBDictionary dictionary) {
DBDictionaryItemWrapper bean = item.getBean();
ProcessDictionaryItem lookedUpItem = dictionary.lookup(bean.getKey());
if (lookedUpItem != null) {
validationNotification(application, i18NSource, getMessage("validate.dictentry.exists"));
}
else {
bean.getWrappedObject().setDictionary(dictionary);
dictionary.addItem(bean.getWrappedObject());
dictItemContainers.get(dictionary).addBean(bean);
handleItemSave(bean);
builder.closeDetailsWindow();
}
}
private void showItemDetails(BeanItem<DBDictionaryItemWrapper> item, DictionaryItemTableBuilder.SaveCallback<DBDictionaryItemWrapper> callback) {
builder.showItemDetails(item, callback);
}
private boolean hasPermissionsForDictionary(ProcessDBDictionary config) {
if (config.getPermissions() == null || config.getPermissions().isEmpty()) {
return true;
}
Collection<ProcessDBDictionaryPermission> edit = Collections.filter(config.getPermissions(), new Predicate<ProcessDBDictionaryPermission>() {
@Override
public boolean apply(ProcessDBDictionaryPermission input) {
return PRIVILEGE_EDIT.equalsIgnoreCase(input.getPrivilegeName());
}
});
ProcessDBDictionaryPermission permission = Collections.firstMatching(edit, new Predicate<ProcessDBDictionaryPermission>() {
@Override
public boolean apply(ProcessDBDictionaryPermission input) {
return application.hasMatchingRole(input.getRoleName());
}
});
return permission != null;
}
@Override
public void handleItemSave(DBDictionaryItemWrapper wrapper) {
final ProcessDBDictionaryItem item = wrapper.getWrappedObject();
if (item.getValues() == null) {
item.setValues(new HashSet<ProcessDBDictionaryItemValue>());
}
else {
for (ProcessDBDictionaryItemValue itemValue : item.getValues()) {
itemValue.setItem(item);
for (ProcessDBDictionaryItemExtension ext : itemValue.getExtensions().values()) {
ext.setItemValue(itemValue);
}
}
}
transactionProvider.withTransaction(new ProcessToolGuiCallback() {
@Override
public void callback(ProcessToolContext ctx, ProcessToolBpmSession session) {
ctx.getProcessDictionaryDAO().updateDictionary(item.getDictionary());
}
});
}
@Override
public void handleItemDelete(DBDictionaryItemWrapper wrapper) {
ProcessDBDictionaryItem item = wrapper.getWrappedObject();
final ProcessDBDictionary dictionary = item.getDictionary();
dictionary.removeItem(item.getKey());
item.setDictionary(null);
transactionProvider.withTransaction(new ProcessToolGuiCallback() {
@Override
public void callback(ProcessToolContext ctx, ProcessToolBpmSession session) {
ctx.getProcessDictionaryDAO().updateDictionary(dictionary);
}
});
}
}