All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dashbuilder.displayer.client.AbstractDisplayer Maven / Gradle / Ivy

/*
 * 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;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.OptionalDouble;
import java.util.Set;

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.DataColumn;
import org.dashbuilder.dataset.DataSet;
import org.dashbuilder.dataset.ValidationError;
import org.dashbuilder.dataset.client.DataSetReadyCallback;
import org.dashbuilder.dataset.date.DayOfWeek;
import org.dashbuilder.dataset.date.Month;
import org.dashbuilder.dataset.filter.DataSetFilter;
import org.dashbuilder.dataset.group.ColumnGroup;
import org.dashbuilder.dataset.group.DataSetGroup;
import org.dashbuilder.dataset.group.DateIntervalPattern;
import org.dashbuilder.dataset.group.DateIntervalType;
import org.dashbuilder.dataset.group.GroupStrategy;
import org.dashbuilder.dataset.group.Interval;
import org.dashbuilder.dataset.sort.SortOrder;
import org.dashbuilder.displayer.ColumnSettings;
import org.dashbuilder.displayer.DisplayerConstraints;
import org.dashbuilder.displayer.DisplayerSettings;
import org.dashbuilder.displayer.client.export.ExportCallback;
import org.dashbuilder.displayer.client.export.ExportFormat;
import org.dashbuilder.displayer.client.formatter.ValueFormatter;

/**
 * Base class for implementing custom displayers.
 * 

Any derived class must implement: *

    *
  • The draw(), redraw() & close() methods.
  • *
  • The capture of events coming from the DisplayerListener interface.
  • *
*/ public abstract class AbstractDisplayer implements Displayer { public interface View extends IsWidget { void errorMissingSettings(); void errorMissingHandler(); void showLoading(); void showVisualization(); void clear(); void setId(String id); void errorDataSetNotFound(String uuid); void error(ClientRuntimeError error); void enableRefreshTimer(int seconds); void cancelRefreshTimer(); } public interface Formatter { String formatDate(String pattern, Date d); Date parseDate(String pattern, String d); String formatNumber(String pattern, Number n); String formatDayOfWeek(DayOfWeek dayOfWeek); String formatMonth(Month month); } public interface ExpressionEval { String evalExpression(String value, String expression); } protected DataSet dataSet; protected DataSetHandler dataSetHandler; protected DisplayerSettings displayerSettings; protected DisplayerConstraints displayerConstraints; protected List listenerList = new ArrayList<>(); protected Map> columnSelectionMap = new HashMap<>(); protected Map formatterMap = new HashMap<>(); protected Formatter formatter = null; protected ExpressionEval evaluator = null; protected DataSetFilter currentFilter = null; protected boolean refreshEnabled = true; protected boolean drawn = false; @Override public Widget asWidget() { return getView().asWidget(); } /** * It returns the actual implementation of the View *

- To be provided by the concrete displayer implementation -

*/ public abstract V getView(); /** * It initializes the constraints this displayer conforms to *

- To be provided by the concrete displayer implementation -

*/ public abstract DisplayerConstraints createDisplayerConstraints(); /** * The required logic in charge of rendering the visualization * once the data has been retrieved during a call to draw() *

- To be provided by the concrete displayer implementation -

*/ protected abstract void createVisualization(); /** * The required logic in charge of updating a visualization * once the data has been retrieved during a call to redraw() *

- To be provided by the concrete displayer implementation -

*/ protected abstract void updateVisualization(); public DisplayerConstraints getDisplayerConstraints() { if (displayerConstraints == null) { displayerConstraints = createDisplayerConstraints(); } return displayerConstraints; } public DisplayerSettings getDisplayerSettings() { return displayerSettings; } public void setDisplayerSettings(DisplayerSettings displayerSettings) { checkDisplayerSettings(displayerSettings); this.displayerSettings = displayerSettings; } public void checkDisplayerSettings(DisplayerSettings displayerSettings) { DisplayerConstraints constraints = getDisplayerConstraints(); if (displayerConstraints != null) { ValidationError error = constraints.check(displayerSettings); if (error != null) { throw error; } } } public DataSetHandler getDataSetHandler() { return dataSetHandler; } public void setDataSetHandler(DataSetHandler dataSetHandler) { this.dataSetHandler = dataSetHandler; } public Formatter getFormatter() { if (formatter == null) { formatter = new DisplayerGwtFormatter(); } return formatter; } public void setFormatter(Formatter formatter) { this.formatter = formatter; } public ExpressionEval getEvaluator() { if (evaluator == null) { evaluator = new DisplayerGwtExprEval(this); } return evaluator; } public void setEvaluator(ExpressionEval evaluator) { this.evaluator = evaluator; } public void addListener(DisplayerListener... listeners) { for (DisplayerListener listener : listeners) { listenerList.add(listener); } } public String getDisplayerId() { String id = displayerSettings.getUUID(); if (!StringUtils.isBlank(id)) { return id; } id = displayerSettings.getTitle(); if (!StringUtils.isBlank(id)) { int hash = id.hashCode(); return Integer.toString(hash < 0 ? hash*-1 : hash); } return null; } // DRAW & REDRAW @Override public boolean isDrawn() { return drawn; } /** * Draw the displayer by executing first the lookup call to retrieve the target data set */ @Override public void draw() { if (displayerSettings == null) { getView().errorMissingSettings(); } else if (dataSetHandler == null) { getView().errorMissingHandler(); } else if (!isDrawn()) { try { drawn = true; getView().showLoading(); beforeLoad(); beforeDataSetLookup(); dataSetHandler.lookupDataSet(new DataSetReadyCallback() { public void callback(DataSet result) { try { dataSet = result; afterLoad(); afterDataSetLookup(result); createVisualization(); getView().showVisualization(); // Set the id of the container panel so that the displayer can be easily located // by testing tools for instance. String id = getDisplayerId(); if (!StringUtils.isBlank(id)) { getView().setId(id); } // Draw done afterDraw(); } catch (Exception e) { // Give feedback on any initialization error showError(new ClientRuntimeError(e)); } } public void notFound() { getView().errorDataSetNotFound(displayerSettings.getDataSetLookup().getDataSetUUID()); } @Override public boolean onError(final ClientRuntimeError error) { showError(error); return false; } }); } catch (Exception e) { showError(new ClientRuntimeError(e)); } } } /** * Just reload the data set and make the current displayer to redraw. */ @Override public void redraw() { if (!isDrawn()) { draw(); } else { try { beforeLoad(); beforeDataSetLookup(); dataSetHandler.lookupDataSet(new DataSetReadyCallback() { public void callback(DataSet result) { try { dataSet = result; afterDataSetLookup(result); updateVisualization(); // Redraw done afterRedraw(); } catch (Exception e) { // Give feedback on any initialization error showError(new ClientRuntimeError(e)); } } public void notFound() { String uuid = displayerSettings.getDataSetLookup().getDataSetUUID(); getView().errorDataSetNotFound(uuid); handleError("Data set not found: " + uuid); } @Override public boolean onError(final ClientRuntimeError error) { showError(error); requestDraw(); return false; } }); } catch (Exception e) { showError(new ClientRuntimeError(e)); } } } private void requestDraw() { drawn = false; } public void showError(ClientRuntimeError error) { getView().error(error); handleError(error); } /** * Close the displayer */ @Override public void close() { getView().clear(); // Close done afterClose(); } /** * Call back method invoked just before the data set lookup is executed. */ protected void beforeDataSetLookup() { } /** * Call back method invoked just after the data set lookup is executed. */ protected void afterDataSetLookup(DataSet dataSet) { } // REFRESH TIMER @Override public void setRefreshOn(boolean enabled) { boolean changed = enabled != refreshEnabled; refreshEnabled = enabled; if (changed) { updateRefreshTimer(); } } @Override public boolean isRefreshOn() { return refreshEnabled; } protected void updateRefreshTimer() { if (isDrawn()) { int seconds = displayerSettings.getRefreshInterval(); if (refreshEnabled && seconds > 0) { getView().enableRefreshTimer(seconds); } else { getView().cancelRefreshTimer(); } } } // LIFECYCLE CALLBACKS protected void beforeLoad() { for (DisplayerListener listener : listenerList) { listener.onDataLookup(this); } } protected void afterLoad() { for (DisplayerListener listener : listenerList) { listener.onDataLoaded(this); } } protected void afterDraw() { updateRefreshTimer(); for (DisplayerListener listener : listenerList) { listener.onDraw(this); } } protected void afterRedraw() { updateRefreshTimer(); for (DisplayerListener listener : listenerList) { listener.onRedraw(this); } } protected void afterClose() { setRefreshOn(false); for (DisplayerListener listener : listenerList) { listener.onClose(this); } } public void handleError(final String message) { handleError(new ClientRuntimeError(message, null)); } public void handleError(final String message, final Throwable error) { handleError(new ClientRuntimeError(message, error)); } public void handleError(final Throwable error) { handleError(new ClientRuntimeError(error)); } public void handleError(final ClientRuntimeError error) { for (DisplayerListener listener : listenerList) { listener.onError(this, error); } } // CAPTURE EVENTS RECEIVED FROM OTHER DISPLAYERS @Override public void onDataLookup(Displayer displayer) { // Do nothing } @Override public void onDataLoaded(Displayer displayer) { // Do nothing } @Override public void onDraw(Displayer displayer) { // Do nothing } @Override public void onRedraw(Displayer displayer) { // Do nothing } @Override public void onClose(Displayer displayer) { // Do nothing } @Override public void onError(final Displayer displayer, ClientRuntimeError error) { // Do nothing } @Override public void onFilterEnabled(Displayer displayer, DataSetGroup groupOp) { if (displayerSettings.isFilterListeningEnabled()) { if (dataSetHandler.filter(groupOp)) { redraw(); } } } @Override public void onFilterEnabled(Displayer displayer, DataSetFilter filter) { if (displayerSettings.isFilterListeningEnabled()) { if (dataSetHandler.filter(filter)) { redraw(); } } } @Override public void onFilterUpdate(Displayer displayer, DataSetFilter oldFilter, DataSetFilter newFilter) { if (displayerSettings.isFilterListeningEnabled()) { boolean unfilter = dataSetHandler.unfilter(oldFilter); boolean filter = dataSetHandler.filter(newFilter); if (unfilter || filter) { redraw(); } } } @Override public void onFilterReset(Displayer displayer, List groupOps) { if (displayerSettings.isFilterListeningEnabled()) { boolean applied = false; for (DataSetGroup groupOp : groupOps) { if (dataSetHandler.unfilter(groupOp)) { applied = true; } } if (applied) { redraw(); } } } @Override public void onFilterReset(Displayer displayer, DataSetFilter filter) { if (displayerSettings.isFilterListeningEnabled()) { if (dataSetHandler.unfilter(filter)) { redraw(); } } } // DATA COLUMN VALUES SELECTION, FILTER & NOTIFICATION /** * Get the set of columns being filtered. */ public Set filterColumns() { return columnSelectionMap.keySet(); } /** * Get the current filter intervals for the given data set column. * * @param columnId The column identifier. * @return A list of intervals. */ public List filterIntervals(String columnId) { List selected = columnSelectionMap.get(columnId); if (selected == null) { return new ArrayList<>(); } return selected; } /** * Get the current filter interval matching the specified index * * @param columnId The column identifier. * @param idx The index of the interval * @return The target interval matching the specified parameters or null if it does not exist. */ public Interval filterInterval(String columnId, int idx) { List selected = columnSelectionMap.get(columnId); if (selected != null && !selected.isEmpty()) { for (Interval interval : selected) { if (interval.getIndex() == idx) { return interval; } } } return null; } /** * Get the current filter selected interval indexes for the given data set column. * * @param columnId The column identifier. * @return A list of interval indexes */ public List filterIndexes(String columnId) { List result = new ArrayList<>(); List selected = columnSelectionMap.get(columnId); if (selected == null) { return result; } for (Interval interval : selected) { result.add(interval.getIndex()); } return result; } /** * Updates the current filter values for the given data set column. * * @param columnId The column to filter for. * @param row The row selected. */ public void filterUpdate(String columnId, int row) { filterUpdate(columnId, row, null); } /** * Updates the current filter values for the given data set column. * * @param columnId The column to filter for. * @param row The row selected. * @param maxSelections The number of different selectable values available. */ public void filterUpdate(String columnId, int row, Integer maxSelections) { if (displayerSettings.isFilterEnabled()) { List selectedIntervals = columnSelectionMap.get(columnId); Interval intervalFiltered = filterInterval(columnId, row); // Existing interval reset if (intervalFiltered != null) { selectedIntervals.remove(intervalFiltered); if (!selectedIntervals.isEmpty()) { filterApply(columnId, selectedIntervals); } else { filterReset(columnId); } } // No current filter => Add the selected interval else if (selectedIntervals == null) { Interval intervalSelected = dataSetHandler.getInterval(columnId, row); if (intervalSelected != null) { selectedIntervals = new ArrayList<>(); selectedIntervals.add(intervalSelected); columnSelectionMap.put(columnId, selectedIntervals); filterApply(columnId, selectedIntervals); } } // Extra interval added to an already filtered column else { Interval intervalSelected = dataSetHandler.getInterval(columnId, row); if (intervalSelected != null) { if (displayerSettings.isFilterSelfApplyEnabled()) { selectedIntervals = new ArrayList<>(); columnSelectionMap.put(columnId, selectedIntervals); } selectedIntervals.add(intervalSelected); if (maxSelections != null && maxSelections > 0 && selectedIntervals.size() >= maxSelections) { filterReset(columnId); } else { filterApply(columnId, selectedIntervals); } } } } } /** * Filter the values of the given column. * * @param columnId The name of the column to filter. * @param intervalList A list of interval selections to filter for. */ public void filterApply(String columnId, List intervalList) { if (displayerSettings.isFilterEnabled()) { // For string column filters, init the group interval selection operation. DataSetGroup groupOp = dataSetHandler.getGroupOperation(columnId); groupOp.setSelectedIntervalList(intervalList); // Notify to those interested parties the selection event. if (displayerSettings.isFilterNotificationEnabled()) { for (DisplayerListener listener : listenerList) { listener.onFilterEnabled(this, groupOp); } } // Drill-down support if (displayerSettings.isFilterSelfApplyEnabled()) { dataSetHandler.drillDown(groupOp); redraw(); } } } /** * Apply the given filter * * @param filter A filter */ public void filterApply(DataSetFilter filter) { if (displayerSettings.isFilterEnabled()) { this.currentFilter = filter; // Notify to those interested parties the selection event. if (displayerSettings.isFilterNotificationEnabled()) { for (DisplayerListener listener : listenerList) { listener.onFilterEnabled(this, filter); } } // Drill-down support if (displayerSettings.isFilterSelfApplyEnabled()) { dataSetHandler.filter(filter); redraw(); } } } /** * Updates the current filter values for the given data set column. Any previous filter is reset. * * @param filter A filter */ public void filterUpdate(DataSetFilter filter) { if (displayerSettings.isFilterEnabled()) { DataSetFilter oldFilter = currentFilter; this.currentFilter = filter; // Notify to those interested parties the selection event. if (displayerSettings.isFilterNotificationEnabled()) { for (DisplayerListener listener : listenerList) { listener.onFilterUpdate(this, oldFilter, filter); } } // Drill-down support if (displayerSettings.isFilterSelfApplyEnabled()) { dataSetHandler.unfilter(oldFilter); dataSetHandler.filter(filter); redraw(); } } } /** * Clear any filter on the given column. * * @param columnId The name of the column to reset. */ public void filterReset(String columnId) { if (displayerSettings.isFilterEnabled()) { columnSelectionMap.remove(columnId); DataSetGroup groupOp = dataSetHandler.getGroupOperation(columnId); // Notify to those interested parties the reset event. if (displayerSettings.isFilterNotificationEnabled()) { for (DisplayerListener listener : listenerList) { listener.onFilterReset(this, Arrays.asList(groupOp)); } } // Apply the selection to this displayer if (displayerSettings.isFilterSelfApplyEnabled()) { dataSetHandler.drillUp(groupOp); redraw(); } } } /** * Clear any filter. */ public void filterReset() { if (displayerSettings.isFilterEnabled()) { List groupOpList = new ArrayList(); for (String columnId : columnSelectionMap.keySet()) { DataSetGroup groupOp = dataSetHandler.getGroupOperation(columnId); groupOpList.add(groupOp); } columnSelectionMap.clear(); // Notify to those interested parties the reset event. if (displayerSettings.isFilterNotificationEnabled()) { for (DisplayerListener listener : listenerList) { if (currentFilter != null) { listener.onFilterReset(this, currentFilter); } listener.onFilterReset(this, groupOpList); } } // Apply the selection to this displayer if (displayerSettings.isFilterSelfApplyEnabled()) { boolean applied = false; if (currentFilter != null) { if (dataSetHandler.unfilter(currentFilter)) { applied = true; } } for (DataSetGroup groupOp : groupOpList) { if (dataSetHandler.drillUp(groupOp)) { applied = true; } } if (applied) { redraw(); } } if (currentFilter != null) { currentFilter = null; } } } // DATA COLUMN SORT /** * Set the sort order operation to apply to the data set. * * @param columnId The name of the column to sort. * @param sortOrder The sort order. */ public void sortApply(String columnId, SortOrder sortOrder) { dataSetHandler.sort(columnId, sortOrder); } // DATA FORMATTING public String formatInterval(Interval interval, DataColumn column) { // Raw values if (column == null || column.getColumnGroup() == null) { return interval.getName(); } // Date interval String type = interval.getType(); if (StringUtils.isBlank(type)) type = column.getIntervalType(); if (StringUtils.isBlank(type)) type = column.getColumnGroup().getIntervalSize(); DateIntervalType intervalType = DateIntervalType.getByName(type); if (intervalType != null) { ColumnSettings columnSettings = displayerSettings.getColumnSettings(column.getId()); String pattern = columnSettings != null ? columnSettings.getValuePattern() : ColumnSettings.getDatePattern(intervalType); String expression = columnSettings != null ? columnSettings.getValueExpression() : null; if (pattern == null) { pattern = ColumnSettings.getDatePattern(intervalType); } if (expression == null && column.getColumnGroup().getStrategy().equals(GroupStrategy.FIXED)) { expression = ColumnSettings.getFixedExpression(intervalType); } return formatDate(intervalType, column.getColumnGroup().getStrategy(), interval.getName(), pattern, expression); } // Label interval ColumnSettings columnSettings = displayerSettings.getColumnSettings(column); String expression = columnSettings.getValueExpression(); if (StringUtils.isBlank(expression)) return interval.getName(); return getEvaluator().evalExpression(interval.getName(), expression); } public void addFormatter(String columnId, ValueFormatter formatter) { formatterMap.put(columnId, formatter); } public ValueFormatter getFormatter(String columnId) { return formatterMap.get(columnId); } public String formatValue(int row, int column) { Object value = row < dataSet.getRowCount() ? dataSet.getValueAt(row, column) : null; DataColumn columnObj = dataSet.getColumnByIndex(column); ValueFormatter formatter = getFormatter(columnObj.getId()); if (formatter != null) { return formatter.formatValue(dataSet, row, column); } return formatValue(value, columnObj); } public String formatValue(Object value, DataColumn column) { ValueFormatter formatter = getFormatter(column.getId()); if (formatter != null) { return formatter.formatValue(value); } ColumnSettings columnSettings = displayerSettings.getColumnSettings(column); String pattern = columnSettings.getValuePattern(); String empty = columnSettings.getEmptyTemplate(); String expression = columnSettings.getValueExpression(); if (value == null) { return empty; } // Date grouped columns DateIntervalType intervalType = DateIntervalType.getByName(column.getIntervalType()); if (intervalType != null) { ColumnGroup columnGroup = column.getColumnGroup(); return formatDate(intervalType, columnGroup.getStrategy(), value.toString(), pattern, expression); } // Label grouped columns, aggregations & raw values else { ColumnType columnType = column.getColumnType(); if (ColumnType.DATE.equals(columnType)) { Date d = (Date) value; return getFormatter().formatDate(pattern, d); } else if (ColumnType.NUMBER.equals(columnType)) { OptionalDouble od = OptionalDouble.empty(); if (value instanceof Number) { od = OptionalDouble.of(((Number) value).doubleValue()); } if (!StringUtils.isBlank(expression)) { String r = getEvaluator().evalExpression(value.toString(), expression); try { od = OptionalDouble.of(Double.parseDouble(r)); } catch (NumberFormatException e) { return r; } } return getFormatter().formatNumber(pattern, od.getAsDouble()); } else { if (StringUtils.isBlank(expression)) { return value.toString(); } return getEvaluator().evalExpression(value.toString(), expression); } } } // DATE FORMATTING protected String formatDate(DateIntervalType type, GroupStrategy strategy, String date, String pattern, String expression) { if (date == null) { return null; } String str = GroupStrategy.FIXED.equals(strategy) ? formatDateFixed(type, date) : formatDateDynamic(type, date, pattern); if (StringUtils.isBlank(expression)) { return str; } return getEvaluator().evalExpression(str, expression); } protected String formatDateFixed(DateIntervalType type, String date) { if (date == null) { return null; } int index = Integer.parseInt(date); if (DateIntervalType.DAY_OF_WEEK.equals(type)) { DayOfWeek dayOfWeek = DayOfWeek.getByIndex(index); return getFormatter().formatDayOfWeek(dayOfWeek); } if (DateIntervalType.MONTH.equals(type)) { Month month = Month.getByIndex(index); return getFormatter().formatMonth(month); } return date; } protected String formatDateDynamic(DateIntervalType type, String date, String pattern) { if (date == null) { return null; } Date d = parseDynamicGroupDate(type, date); return getFormatter().formatDate(pattern, d); } protected Date parseDynamicGroupDate(DateIntervalType type, String date) { String pattern = DateIntervalPattern.getPattern(type); return getFormatter().parseDate(pattern, date); } // EXPORT @Override public void export(ExportFormat format, int maxRows, ExportCallback callback) { if (dataSetHandler == null) { callback.noData(); } else { Map columnNameMap = new HashMap<>(); displayerSettings.getColumnSettingsList().forEach(cs -> columnNameMap.put(cs.getColumnId(), cs.getColumnName())); dataSetHandler.exportCurrentDataSetLookup(format, maxRows, callback, columnNameMap); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy