/* * Copyright 2014 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.dashbuilder.displayer.client.widgets; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ArrayList; import java.util.Map; import javax.enterprise.context.Dependent; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.inject.Inject; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Widget; import org.dashbuilder.common.client.StringUtils; import org.dashbuilder.common.client.error.ClientRuntimeError; import org.dashbuilder.dataset.ColumnType; import org.dashbuilder.dataset.DataSetLookup; import org.dashbuilder.dataset.DataSetLookupConstraints; import org.dashbuilder.dataset.DataSetMetadata; import org.dashbuilder.dataset.DataSetOpType; import org.dashbuilder.dataset.client.DataSetClientServices; import org.dashbuilder.dataset.client.DataSetMetadataCallback; import org.dashbuilder.dataset.def.DataSetDef; import org.dashbuilder.dataset.events.DataSetDefModifiedEvent; import org.dashbuilder.dataset.events.DataSetDefRegisteredEvent; import org.dashbuilder.dataset.events.DataSetDefRemovedEvent; import org.dashbuilder.dataset.filter.DataSetFilter; import org.dashbuilder.dataset.group.AggregateFunctionType; import org.dashbuilder.dataset.group.ColumnGroup; import org.dashbuilder.dataset.group.DataSetGroup; import org.dashbuilder.dataset.group.GroupFunction; import org.dashbuilder.displayer.client.events.DataSetFilterChangedEvent; import org.dashbuilder.displayer.client.events.DataSetGroupDateChanged; import org.dashbuilder.displayer.client.events.DataSetLookupChangedEvent; import org.dashbuilder.displayer.client.events.GroupFunctionChangedEvent; import org.dashbuilder.displayer.client.events.GroupFunctionDeletedEvent; import org.dashbuilder.displayer.client.widgets.filter.DataSetFilterEditor; import org.dashbuilder.displayer.client.widgets.group.ColumnFunctionEditor; import org.dashbuilder.displayer.client.widgets.group.DataSetGroupDateEditor; import org.jboss.errai.common.client.api.RemoteCallback; import org.jboss.errai.ioc.client.container.SyncBeanManager; import org.uberfire.client.mvp.UberView; import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull; @Dependent public class DataSetLookupEditor implements IsWidget { public interface View extends UberView<DataSetLookupEditor> { void clearAll(); void clearDataSetSelector(); void setDataSetSelectorHintEnabled(boolean enabled); void addDataSetItem(String name, String id); void removeDataSetItem(int index); void setSelectedDataSetIndex(int index); String getSelectedDataSetId(); void error(ClientRuntimeError error); void errorDataSetNotFound(String dataSetUUID); void setFilterEnabled(boolean enabled); void setGroupEnabled(boolean enabled); void clearGroupColumnSelector(); void setGroupByDateEnabled(boolean enabled); void setGroupColumnSelectorTitle(String title); void setGroupColumnSelectorHintEnabled(boolean enabled); void addGroupColumnItem(String column); void setSelectedGroupColumnIndex(int index); String getSelectedGroupColumnId(); void setColumnsSectionEnabled(boolean enabled); void clearColumnList(); void setColumnSectionTitle(String title); void setAddColumnOptionEnabled(boolean enabled); void addColumnEditor(ColumnFunctionEditor editor); void removeColumnEditor(ColumnFunctionEditor editor); } public interface DataSetDefFilter { boolean accept(DataSetDef def); } View view; SyncBeanManager beanManager; DataSetClientServices clientServices; DataSetFilterEditor filterEditor; DataSetGroupDateEditor groupDateEditor; DataSetLookup dataSetLookup = null; DataSetLookupConstraints lookupConstraints = null; DataSetMetadata dataSetMetadata = null; Event<DataSetLookupChangedEvent> changeEvent = null; List<DataSetDef> _dataSetDefList = new ArrayList<DataSetDef>(); Map<Integer, ColumnFunctionEditor> _editorsMap = new HashMap<Integer, ColumnFunctionEditor>(); DataSetDefFilter dataSetDefFilter = new DataSetDefFilter() { public boolean accept(DataSetDef def) { return true; } }; @Inject public DataSetLookupEditor(final View view, SyncBeanManager beanManager, DataSetFilterEditor filterEditor, DataSetGroupDateEditor groupDateEditor, DataSetClientServices clientServices, Event<DataSetLookupChangedEvent> event) { this.view = view; this.beanManager = beanManager; this.filterEditor = filterEditor; this.groupDateEditor = groupDateEditor; this.clientServices = clientServices; this.changeEvent = event; this.dataSetLookup = null; this.lookupConstraints = null; this.dataSetMetadata = null; view.init(this); } public void init(DataSetLookupConstraints lookupConstraints, final DataSetLookup dataSetLookup) { this.dataSetLookup = dataSetLookup; this.lookupConstraints = lookupConstraints; this.view.clearAll(); this.clientServices.getPublicDataSetDefs(new RemoteCallback<List<DataSetDef>>() { public void callback(List<DataSetDef> dataSetDefs) { showDataSetDefs(dataSetDefs); if (dataSetLookup != null && dataSetLookup.getDataSetUUID() != null) { fetchMetadata(dataSetLookup.getDataSetUUID(), new RemoteCallback<DataSetMetadata>() { public void callback(DataSetMetadata metadata) { updateDataSetLookup(); } }); } } }); } void fetchMetadata(final String uuid, final RemoteCallback<DataSetMetadata> callback) { try { clientServices.fetchMetadata(uuid, new DataSetMetadataCallback() { public void callback(DataSetMetadata metadata) { dataSetMetadata = metadata; callback.callback(metadata); } public void notFound() { view.errorDataSetNotFound(uuid); } public boolean onError(ClientRuntimeError error) { view.error(error); return false; } }); } catch (Exception e) { view.error(new ClientRuntimeError(e)); } } @Override public Widget asWidget() { return view.asWidget(); } public View getView() { return view; } public DataSetFilterEditor getFilterEditor() { return filterEditor; } public DataSetGroupDateEditor getGroupDateEditor() { return groupDateEditor; } public DataSetLookup getDataSetLookup() { return dataSetLookup; } public DataSetLookupConstraints getLookupConstraints() { return lookupConstraints; } public void setDataSetDefFilter(DataSetDefFilter dataSetDefFilter) { this.dataSetDefFilter = dataSetDefFilter; } public String getDataSetUUID() { return dataSetLookup == null ? null : dataSetLookup.getDataSetUUID(); } public String getColumnId(int index) { return dataSetMetadata.getColumnId(index); } public ColumnType getColumnType(int index) { return dataSetMetadata.getColumnType(index); } public ColumnType getColumnType(String columnId) { return columnId == null ? null : dataSetMetadata.getColumnType(columnId); } public DataSetGroup getFirstGroupOp() { List<DataSetGroup> groupOpList = dataSetLookup.getOperationList(DataSetGroup.class); if (groupOpList.isEmpty()) { return null; } return groupOpList.get(0); } public boolean isFirstGroupOpDateBased() { DataSetGroup first = getFirstGroupOp(); if (first == null) { return false; } ColumnGroup cg = first.getColumnGroup(); if (cg == null) { return false; } ColumnType type = getColumnType(cg.getSourceId()); return ColumnType.DATE.equals(type); } public List<GroupFunction> getFirstGroupFunctions() { List<DataSetGroup> groupOpList = dataSetLookup.getOperationList(DataSetGroup.class); if (groupOpList.isEmpty()) { return null; } return groupOpList.get(0).getGroupFunctions(); } public int getFirstGroupFunctionIdx(GroupFunction gf) { List<DataSetGroup> groupOpList = dataSetLookup.getOperationList(DataSetGroup.class); if (groupOpList.isEmpty()) { return -1; } return groupOpList.get(0).getGroupFunctionIdx(gf); } public String getFirstGroupColumnId() { List<DataSetGroup> groupOpList = dataSetLookup.getOperationList(DataSetGroup.class); if (groupOpList.isEmpty()) { return null; } DataSetGroup groupOp = groupOpList.get(0); if (groupOp.getColumnGroup() == null) { return null; } return groupOp.getColumnGroup().getSourceId(); } public List<Integer> getAvailableGroupColumnIdxs() { List<Integer> result = new ArrayList<Integer>(); for (int i=0; i<dataSetMetadata.getNumberOfColumns(); i++) { ColumnType columnType = dataSetMetadata.getColumnType(i); if (ColumnType.LABEL.equals(columnType) || ColumnType.DATE.equals(columnType)) { result.add(i); } } return result; } public void showDataSetDefs(List<DataSetDef> ds) { _dataSetDefList.clear(); view.clearDataSetSelector(); String selectedUUID = getDataSetUUID(); view.setDataSetSelectorHintEnabled(StringUtils.isBlank(selectedUUID)); boolean found = false; for (int i=0; i<ds.size(); i++) { DataSetDef def = ds.get(i); if (dataSetDefFilter.accept(def)) { addDataSetDef(def); if (selectedUUID != null && selectedUUID.equals(def.getUUID())) { view.setSelectedDataSetIndex(i); found = true; } } } if (!StringUtils.isBlank(selectedUUID) && !found) { view.errorDataSetNotFound(selectedUUID); } } public void addDataSetDef(DataSetDef def) { _dataSetDefList.add(def); if (StringUtils.isBlank(def.getName())) { view.addDataSetItem(def.getUUID(), def.getUUID()); } else { view.addDataSetItem(def.getName(), def.getUUID()); } } public void removeDataSetDef(DataSetDef def) { int i = 0; Iterator<DataSetDef> it = _dataSetDefList.iterator(); while (it.hasNext()) { DataSetDef item = it.next(); if (item.getUUID().equals(def.getUUID())) { it.remove(); view.removeDataSetItem(i); } i++; } } void updateDataSetLookup() { view.setFilterEnabled(false); view.setGroupEnabled(false); view.setColumnsSectionEnabled(false); if (dataSetLookup != null && dataSetMetadata != null && lookupConstraints != null) { updateFilterControls(); updateGroupControls(); updateColumnControls(); } } void updateFilterControls() { view.setFilterEnabled(true); filterEditor.init(dataSetLookup.getFirstFilterOp(), dataSetMetadata); } void updateGroupControls() { view.setGroupEnabled(false); view.setGroupByDateEnabled(false); // Only show the group controls if group is enabled if (lookupConstraints.isGroupRequired() || lookupConstraints.isGroupAllowed()) { String groupColumnId = getFirstGroupColumnId(); // Always ensure a group exists when required if (lookupConstraints.isGroupRequired() && groupColumnId == null) { dataSetLookup = lookupConstraints.newDataSetLookup(dataSetMetadata); changeEvent.fire(new DataSetLookupChangedEvent(dataSetLookup)); } List<Integer> groupColumnIdxs = getAvailableGroupColumnIdxs(); String rowsTitle = lookupConstraints.getGroupsTitle(); view.setGroupEnabled(true); if (isFirstGroupOpDateBased()) { view.setGroupByDateEnabled(true); ColumnGroup columnGroup = getFirstGroupOp().getColumnGroup(); groupDateEditor.init(columnGroup); } if (!StringUtils.isBlank(rowsTitle)) { view.setGroupColumnSelectorTitle(rowsTitle); } view.clearGroupColumnSelector(); view.setGroupColumnSelectorHintEnabled(!lookupConstraints.isGroupRequired()); for (int i = 0; i < groupColumnIdxs.size(); i++) { int idx = groupColumnIdxs.get(i); String columnId = getColumnId(idx); view.addGroupColumnItem(columnId); if (groupColumnId != null && groupColumnId.equals(columnId)) { view.setSelectedGroupColumnIndex(i); } } } } void updateColumnControls() { String groupColumnId = getFirstGroupColumnId(); List<GroupFunction> groupFunctions = getFirstGroupFunctions(); String columnsTitle = lookupConstraints.getColumnsTitle(); boolean functionsRequired = lookupConstraints.isFunctionRequired(); boolean functionsEnabled = (groupColumnId != null || functionsRequired); boolean canDelete = groupFunctions.size() > lookupConstraints.getMinColumns(); int n = lookupConstraints.getMaxColumns(); boolean canAdd = lookupConstraints.areExtraColumnsAllowed() && (n < 0 || groupFunctions.size() < n); // Show the columns section view.setColumnsSectionEnabled(true); view.clearColumnList(); if (!StringUtils.isBlank(columnsTitle)) { view.setColumnSectionTitle(columnsTitle); } view.setAddColumnOptionEnabled(canAdd); // Destroy old editors Iterator<ColumnFunctionEditor> it = _editorsMap.values().iterator(); while (it.hasNext()) { ColumnFunctionEditor editor = it.next(); beanManager.destroyBean(editor); } // Build the column editors _editorsMap.clear(); ColumnType lastTargetType = null; ColumnType[] targetTypes = lookupConstraints.getColumnTypes(groupFunctions.size()); for (int i=0; i<groupFunctions.size(); i++) { final int columnIdx = i; final GroupFunction groupFunction = groupFunctions.get(columnIdx); if (targetTypes != null && i < targetTypes.length) { lastTargetType = targetTypes[i]; } if (columnIdx == 0 && groupColumnId != null && lookupConstraints.isGroupColumn()) { continue; } ColumnType columnType = null; if (targetTypes != null && i<targetTypes.length) { columnType = targetTypes[columnIdx]; } if (columnType == null) { columnType = lastTargetType; // Extra columns } String columnTitle = lookupConstraints.getColumnTitle(columnIdx); ColumnFunctionEditor columnEditor = beanManager.lookupBean(ColumnFunctionEditor.class).newInstance(); columnEditor.init(dataSetMetadata, groupFunction, columnType, columnTitle, functionsEnabled, canDelete); _editorsMap.put(_editorsMap.size(), columnEditor); view.addColumnEditor(columnEditor); } } int getGroupFunctionLastIdx(List<GroupFunction> groupFunctions, String sourceId) { int last = -1; for (GroupFunction gf : groupFunctions) { if (gf.getSourceId().equals(sourceId)) { int idx = getGroupFunctionColumnIdx(gf.getColumnId()); if (last == -1 || last < idx) { last = idx; } } } return last; } int getGroupFunctionColumnIdx(String columnId) { int sep = columnId.lastIndexOf('_'); if (sep != -1) { try { String str = columnId.substring(sep + 1); return Integer.parseInt(str); } catch (Exception e) { // Ignore } } return 1; } // View notifications void onDataSetSelected() { String selectedUUID = view.getSelectedDataSetId(); for (DataSetDef dataSetDef : _dataSetDefList) { if (dataSetDef.getUUID().equals(selectedUUID)) { fetchMetadata(selectedUUID, new RemoteCallback<DataSetMetadata>() { public void callback(DataSetMetadata metadata) { dataSetLookup = lookupConstraints.newDataSetLookup(metadata); updateDataSetLookup(); changeEvent.fire(new DataSetLookupChangedEvent(dataSetLookup)); } }); } } } void onGroupColumnSelected() { DataSetGroup groupOp = getFirstGroupOp(); if (groupOp != null) { // Group reset String columnId = view.getSelectedGroupColumnId(); if (columnId == null) { groupOp.setColumnGroup(null); if (lookupConstraints.isGroupColumn()) { groupOp.getGroupFunctions().remove(0); } if (!lookupConstraints.isFunctionRequired()) { for (GroupFunction groupFunction : groupOp.getGroupFunctions()) { groupFunction.setFunction(null); } } } // Group column change else { groupOp.setColumnGroup(new ColumnGroup(columnId, columnId)); if (lookupConstraints.isGroupColumn()) { if (groupOp.getGroupFunctions().size() > 1) { groupOp.getGroupFunctions().remove(0); } GroupFunction groupFunction = new GroupFunction(columnId, columnId, null); groupOp.getGroupFunctions().add(0, groupFunction); } } } // Refresh the group by date editor if required view.setGroupByDateEnabled(false); if (isFirstGroupOpDateBased()) { view.setGroupByDateEnabled(true); ColumnGroup columnGroup = getFirstGroupOp().getColumnGroup(); groupDateEditor.init(columnGroup); } // Reset the column list updateColumnControls(); // Notify the changes changeEvent.fire(new DataSetLookupChangedEvent(dataSetLookup)); } void onAddColumn() { if (lookupConstraints.areExtraColumnsAllowed()) { DataSetGroup op = getFirstGroupOp(); List<GroupFunction> functionList = op.getGroupFunctions(); GroupFunction last = functionList.get(functionList.size() - 1); GroupFunction clone = last.cloneInstance(); String newColumnId = lookupConstraints.buildUniqueColumnId(dataSetLookup, clone); clone.setColumnId(newColumnId); functionList.add(clone); updateColumnControls(); changeEvent.fire(new DataSetLookupChangedEvent(dataSetLookup)); } } // DataSetFilterEditor events void onFilterChanged(@Observes DataSetFilterChangedEvent event) { DataSetFilter filterOp = event.getFilter(); dataSetLookup.removeOperations(DataSetOpType.FILTER); if (filterOp != null) { dataSetLookup.addOperation(0, filterOp); } changeEvent.fire(new DataSetLookupChangedEvent(dataSetLookup)); } // DataSetGroupDateEditor events void onDateGroupChanged(@Observes DataSetGroupDateChanged event) { ColumnGroup columnGroup = event.getColumnGroup(); DataSetGroup groupOp = getFirstGroupOp(); if (groupOp != null) { groupOp.setColumnGroup(columnGroup); changeEvent.fire(new DataSetLookupChangedEvent(dataSetLookup)); } } // ColumnFunctionEditor's events void onColumnFunctionChanged(@Observes GroupFunctionChangedEvent event) { GroupFunction gf = event.getGroupFunction(); String newColumnId = lookupConstraints.buildUniqueColumnId(dataSetLookup, gf); gf.setColumnId(newColumnId); changeEvent.fire(new DataSetLookupChangedEvent(dataSetLookup)); } void onColumnFunctionDeleted(@Observes GroupFunctionDeletedEvent event) { List<GroupFunction> functionList = getFirstGroupFunctions(); boolean canDelete = functionList.size() > lookupConstraints.getMinColumns(); GroupFunction removed = event.getGroupFunction(); int index = getFirstGroupFunctionIdx(removed); if (canDelete && index >= 0) { functionList.remove(index); updateColumnControls(); changeEvent.fire(new DataSetLookupChangedEvent(dataSetLookup)); } } // Listen to data set lifecycle events void onDataSetDefRegisteredEvent(@Observes DataSetDefRegisteredEvent event) { checkNotNull("event", event); addDataSetDef(event.getDataSetDef()); } void onDataSetDefModifiedEvent(@Observes DataSetDefModifiedEvent event) { checkNotNull("event", event); removeDataSetDef(event.getOldDataSetDef()); addDataSetDef(event.getNewDataSetDef()); } void onDataSetDefRemovedEvent(@Observes DataSetDefRemovedEvent event) { checkNotNull("event", event); removeDataSetDef(event.getDataSetDef()); } }