/* * 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.ArrayList; import java.util.Iterator; import java.util.List; 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.error.ClientRuntimeError; import org.dashbuilder.dataset.DataColumn; import org.dashbuilder.dataset.DataSet; import org.dashbuilder.dataset.DataSetLookup; import org.dashbuilder.dataset.DataSetLookupConstraints; import org.dashbuilder.dataset.DataSetMetadata; import org.dashbuilder.dataset.ValidationError; import org.dashbuilder.dataset.client.DataSetClientServices; import org.dashbuilder.dataset.group.DataSetGroup; import org.dashbuilder.dataset.group.GroupFunction; import org.dashbuilder.displayer.ColumnSettings; import org.dashbuilder.displayer.DisplayerAttributeDef; import org.dashbuilder.displayer.DisplayerAttributeGroupDef; import org.dashbuilder.displayer.DisplayerConstraints; import org.dashbuilder.displayer.DisplayerSettings; import org.dashbuilder.displayer.DisplayerSubType; import org.dashbuilder.displayer.DisplayerType; import org.dashbuilder.displayer.client.AbstractDisplayerListener; import org.dashbuilder.displayer.client.Displayer; import org.dashbuilder.displayer.client.DisplayerListener; import org.dashbuilder.displayer.client.DisplayerLocator; import org.dashbuilder.displayer.client.events.DataSetLookupChangedEvent; import org.dashbuilder.displayer.client.events.DisplayerEditorClosedEvent; import org.dashbuilder.displayer.client.events.DisplayerEditorSavedEvent; import org.dashbuilder.displayer.client.events.DisplayerSettingsChangedEvent; import org.dashbuilder.displayer.client.events.DisplayerSubtypeSelectedEvent; import org.dashbuilder.displayer.client.events.DisplayerTypeSelectedEvent; import org.dashbuilder.displayer.client.prototypes.DisplayerPrototypes; import org.uberfire.client.mvp.UberView; import org.uberfire.mvp.Command; @Dependent public class DisplayerEditor implements IsWidget { public interface View extends UberView<DisplayerEditor> { String getBrandNewDisplayerTitle(); boolean isTableDisplayModeOn(); void setTableDisplayModeEnabled(boolean enabled); void showDisplayer(IsWidget displayer); void setTypeSelectionEnabled(boolean enabled); void setDisplaySettingsEnabled(boolean enabled); void setDataSetLookupConfEnabled(boolean enabled); void gotoTypeSelection(DisplayerTypeSelector typeSelector); void gotoDataSetLookupConf(DataSetLookupEditor lookupEditor); void gotoDisplaySettings(DisplayerSettingsEditor settingsEditor); void showTypeChangedWarning(Command yes, Command no); void error(String error); void error(ClientRuntimeError error); } protected View view = null; protected DataSetClientServices clientServices = null; protected DisplayerLocator displayerLocator = null; protected DisplayerPrototypes displayerPrototypes = null; protected DisplayerSettings displayerSettings = null; protected DisplayerSettings selectedTypeSettings = null; protected boolean brandNewDisplayer = true; protected DisplayerTypeSelector typeSelector; protected DataSetLookupEditor lookupEditor; protected DisplayerSettingsEditor settingsEditor; protected DisplayerEditorStatus editorStatus; protected Displayer displayer = null; protected DisplayerHtmlEditor displayerHtmlEditor = null; protected int activeSection = -1; protected boolean typeSelectionEnabled = true; protected boolean dataLookupConfEnabled = true; protected boolean displaySettingsEnabled = true; protected Event<DisplayerEditorSavedEvent> saveEvent; protected Event<DisplayerEditorClosedEvent> closeEvent; protected Command onCloseCommand = () -> {}; protected Command onSaveCommand = () -> {}; DisplayerListener displayerListener = new AbstractDisplayerListener() { public void onError(Displayer displayer, ClientRuntimeError error) { view.error(error); } }; @Inject public DisplayerEditor(View view, DataSetClientServices clientServices, DisplayerLocator displayerLocator, DisplayerPrototypes displayerPrototypes, DisplayerTypeSelector typeSelector, DataSetLookupEditor lookupEditor, DisplayerSettingsEditor settingsEditor, DisplayerEditorStatus editorStatus, DisplayerHtmlEditor displayerHtmlEditor, Event<DisplayerEditorSavedEvent> savedEvent, Event<DisplayerEditorClosedEvent> closedEvent) { this.view = view; this.displayerLocator = displayerLocator; this.clientServices = clientServices; this.displayerPrototypes = displayerPrototypes; this.typeSelector = typeSelector; this.lookupEditor = lookupEditor; this.settingsEditor = settingsEditor; this.editorStatus = editorStatus; this.displayerHtmlEditor = displayerHtmlEditor; this.saveEvent = savedEvent; this.closeEvent = closedEvent; view.init(this); } public void init(DisplayerSettings settings) { if (settings != null) { brandNewDisplayer = false; displayerSettings = settings; } else { brandNewDisplayer = true; displayerSettings = displayerPrototypes.getProto(DisplayerType.BARCHART); displayerSettings.setTitle(view.getBrandNewDisplayerTitle()); } selectedTypeSettings = displayerSettings; initDisplayer(); initTypeSelector(); initLookupEditor(); initSettingsEditor(); gotoLastSection(); showDisplayer(); } protected boolean supportsHtmlTemplate() { return displayer.getDisplayerConstraints().getSupportedAttributes().contains(DisplayerAttributeDef.HTML_TEMPLATE); } protected void initDisplayer() { if (displayer != null) { displayer.close(); } displayer = displayerLocator.lookupDisplayer(displayerSettings); displayer.addListener(displayerListener); displayer.setRefreshOn(false); displayer.draw(); } protected void initLookupEditor() { DataSetLookupConstraints lookupConstraints = displayer.getDisplayerConstraints().getDataSetLookupConstraints(); lookupEditor.init(lookupConstraints, displayerSettings.getDataSetLookup()); } protected void initTypeSelector() { typeSelector.init(displayerSettings.getType(), displayerSettings.getSubtype()); } protected void initSettingsEditor() { settingsEditor.init(displayer); } @Override public Widget asWidget() { return view.asWidget(); } public View getView() { return view; } public boolean isBrandNewDisplayer() { return brandNewDisplayer; } public DisplayerSettings getDisplayerSettings() { return displayerSettings; } public Displayer getDisplayer() { return displayer; } public DisplayerTypeSelector getTypeSelector() { return typeSelector; } public DataSetLookupEditor getLookupEditor() { return lookupEditor; } public DisplayerSettingsEditor getSettingsEditor() { return settingsEditor; } public void setTypeSelectorEnabled(boolean enabled) { typeSelectionEnabled = enabled; view.setTypeSelectionEnabled(enabled); } public void setDataSetLookupConfEnabled(boolean enabled) { dataLookupConfEnabled = enabled; view.setDataSetLookupConfEnabled(enabled); } public void setDisplaySettingsEnabled(boolean enabled) { displaySettingsEnabled = enabled; view.setDisplaySettingsEnabled(enabled); } public void setOnSaveCommand(Command saveCommand) { this.onSaveCommand = saveCommand != null ? saveCommand : onCloseCommand; } public void setOnCloseCommand(Command closeCommand) { this.onCloseCommand = closeCommand != null ? closeCommand : onCloseCommand; } public void showDisplayer() { if (view.isTableDisplayModeOn()) { try { DisplayerSettings tableSettings = displayerSettings.cloneInstance(); tableSettings.setTitleVisible(false); tableSettings.setType(DisplayerType.TABLE); tableSettings.setTablePageSize(8); tableSettings.setTableWidth(800); tableSettings.setRenderer("default"); Displayer tableDisplayer = displayerLocator.lookupDisplayer(tableSettings); tableDisplayer.addListener(displayerListener); tableDisplayer.setRefreshOn(false); tableDisplayer.draw(); view.showDisplayer(tableDisplayer); } catch (Exception e) { view.error(new ClientRuntimeError(e)); } } else if (supportsHtmlTemplate()) { displayerHtmlEditor.setDisplayer(displayer); view.showDisplayer(displayerHtmlEditor); } else { view.showDisplayer(displayer); } } public void gotoFirstSectionEnabled() { if (typeSelectionEnabled) { gotoTypeSelection(); } else if (dataLookupConfEnabled) { gotoDataSetLookupConf(); } else if (displaySettingsEnabled) { gotoDisplaySettings(); } else { view.error("Nothing to show!"); } } public void gotoLastSection() { int lastOption = editorStatus.getSelectedOption(displayerSettings.getUUID()); if (activeSection < 0 || activeSection != lastOption) { switch (lastOption) { case 2: gotoDisplaySettings(); break; case 1: gotoDataSetLookupConf(); break; default: gotoFirstSectionEnabled(); break; } } } public void gotoTypeSelection() { activeSection = 0; editorStatus.saveSelectedOption(displayerSettings.getUUID(), activeSection); view.gotoTypeSelection(typeSelector); } public void gotoDataSetLookupConf() { activeSection = 1; editorStatus.saveSelectedOption(displayerSettings.getUUID(), activeSection); view.gotoDataSetLookupConf(lookupEditor); view.setTableDisplayModeEnabled(!DisplayerType.TABLE.equals(displayerSettings.getType())); } public void gotoDisplaySettings() { activeSection = 2; editorStatus.saveSelectedOption(displayerSettings.getUUID(), activeSection); view.gotoDisplaySettings(settingsEditor); } public void save() { // Clear settings before return DisplayerConstraints displayerConstraints = displayer.getDisplayerConstraints(); displayerConstraints.removeUnsupportedAttributes(displayerSettings); // Dispose the displayer if (displayer != null) { displayer.close(); } // Notify event onSaveCommand.execute(); saveEvent.fire(new DisplayerEditorSavedEvent(displayerSettings)); } public void close() { if (displayer != null) { displayer.close(); } onCloseCommand.execute(); closeEvent.fire(new DisplayerEditorClosedEvent(displayerSettings)); } // Widget listeners callback notifications void onDataSetLookupChanged(@Observes DataSetLookupChangedEvent event) { DataSetLookup dataSetLookup = event.getDataSetLookup(); displayerSettings.setDataSet(null); displayerSettings.setDataSetLookup(dataSetLookup); removeStaleSettings(); initDisplayer(); showDisplayer(); } void onDisplayerSettingsChanged(@Observes DisplayerSettingsChangedEvent event) { displayerSettings = event.getDisplayerSettings(); initDisplayer(); showDisplayer(); } void onDisplayerTypeChanged(@Observes DisplayerTypeSelectedEvent event) { displayerTypeChanged(event.getSelectedType(), null); } void onDisplayerSubtypeChanged(@Observes DisplayerSubtypeSelectedEvent event) { displayerTypeChanged(selectedTypeSettings.getType(), event.getSelectedSubType()); } void displayerTypeChanged(DisplayerType type, DisplayerSubType displayerSubType) { // Create new settings for the selected type selectedTypeSettings = displayerPrototypes.getProto(type, displayerSubType); DataSet oldDataSet = displayerSettings.getDataSet(); DataSetLookup oldDataLookup = displayerSettings.getDataSetLookup(); // Check if the current data lookup is compatible with the new displayer type if (oldDataSet == null && oldDataLookup != null) { Displayer displayer = displayerLocator.lookupDisplayer(selectedTypeSettings); DisplayerConstraints displayerConstraints = displayer.getDisplayerConstraints(); DataSetLookupConstraints dataConstraints = displayerConstraints.getDataSetLookupConstraints(); DataSetMetadata metadata = clientServices.getMetadata(oldDataLookup.getDataSetUUID()); // Keep the current data settings provided it satisfies the data constraints ValidationError validationError = dataConstraints.check(oldDataLookup, metadata); if (validationError == null) { selectedTypeSettings.setDataSet(null); selectedTypeSettings.setDataSetLookup(oldDataLookup); applySelectedType(); } // If the data lookup is not compatible then ask the user what to do else { view.showTypeChangedWarning(this::applySelectedType, this::abortSelectedType); } } // If the displayer is static (no data lookup) then just display the selected displayer prototype else { applySelectedType(); } } void applySelectedType() { // Remove the non supported attributes displayerSettings.removeDisplayerSetting(DisplayerAttributeGroupDef.TYPE); displayerSettings.removeDisplayerSetting(DisplayerAttributeGroupDef.SUBTYPE); displayerSettings.removeDisplayerSetting(DisplayerAttributeGroupDef.GENERAL_GROUP); displayerSettings.removeDisplayerSetting(DisplayerAttributeGroupDef.CHART_GROUP); displayerSettings.removeDisplayerSetting(DisplayerAttributeGroupDef.CHART_MARGIN_GROUP); displayerSettings.removeDisplayerSetting(DisplayerAttributeGroupDef.CHART_LEGEND_GROUP); displayerSettings.removeDisplayerSetting(DisplayerAttributeGroupDef.AXIS_GROUP); displayerSettings.removeDisplayerSetting(DisplayerAttributeGroupDef.HTML_GROUP); selectedTypeSettings.getSettingsFlatMap().putAll(displayerSettings.getSettingsFlatMap()); try { // Ensure the renderer supports the new type displayerLocator.lookupDisplayer(selectedTypeSettings); } catch(Exception e) { // The new type might not support the selected renderer. selectedTypeSettings.removeDisplayerSetting(DisplayerAttributeDef.RENDERER); } // Re-initialize the editor with the new settings init(selectedTypeSettings); removeStaleSettings(); } void abortSelectedType() { selectedTypeSettings = displayerSettings; typeSelector.init(displayerSettings.getType(), displayerSettings.getSubtype()); view.showDisplayer(displayer); } List<String> getExistingDataColumnIds() { DataSet dataSet = displayerSettings.getDataSet(); DataSetLookup dataSetLookup = displayerSettings.getDataSetLookup(); List<String> columnIds = new ArrayList<String>(); if (dataSet != null) { for (DataColumn dataColumn : dataSet.getColumns()) { columnIds.add(dataColumn.getId()); } } else if (dataSetLookup != null) { int idx = dataSetLookup.getLastGroupOpIndex(0); if (idx != -1) { DataSetGroup groupOp = dataSetLookup.getOperation(idx); for (GroupFunction groupFunction : groupOp.getGroupFunctions()) { columnIds.add(groupFunction.getColumnId()); } } } return columnIds; } void removeStaleSettings() { List<String> columnIds = getExistingDataColumnIds(); // Remove the settings for non existing columns Iterator<ColumnSettings> it = displayerSettings.getColumnSettingsList().iterator(); while (it.hasNext()) { ColumnSettings columnSettings = it.next(); if (!columnIds.contains(columnSettings.getColumnId())) { it.remove(); } } // Reset table sort column if (!columnIds.contains(displayerSettings.getTableDefaultSortColumnId())) { displayerSettings.setTableDefaultSortColumnId(null); } } }