/*
* 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());
}
}