/*
* Copyright 2015 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.renderer.client.metric;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import org.dashbuilder.common.client.StringTemplateBuilder;
import org.dashbuilder.common.client.StringUtils;
import org.dashbuilder.dataset.ColumnType;
import org.dashbuilder.dataset.DataSetLookupConstraints;
import org.dashbuilder.dataset.filter.DataSetFilter;
import org.dashbuilder.displayer.DisplayerAttributeDef;
import org.dashbuilder.displayer.DisplayerAttributeGroupDef;
import org.dashbuilder.displayer.DisplayerConstraints;
import org.dashbuilder.displayer.client.AbstractDisplayer;
import org.dashbuilder.displayer.client.resources.i18n.DisplayerConstants;
import org.dashbuilder.displayer.client.widgets.sourcecode.HasHtmlTemplate;
import org.dashbuilder.displayer.client.widgets.sourcecode.HasJsTemplate;
public class MetricDisplayer extends AbstractDisplayer<MetricDisplayer.View>
implements HasHtmlTemplate, HasJsTemplate {
public interface View extends AbstractDisplayer.View<MetricDisplayer> {
String getUniqueId();
void setHtml(String html);
void eval(String js);
String getNoDataString();
String getColumnsTitle();
}
public static final List<String> TEMPLATE_KEYS = Arrays.asList("value.raw", "value", "title",
"width", "height", "marginTop", "marginBottom", "marginRight", "marginLeft", "bgColor",
"isFilterEnabled", "isFilterOn", "isEmpty", "doFilter");
public static final String DEFAULT_HTML_TEMPLATE = "<div id=\"${this}\" class=\"card-pf card-pf-accented card-pf-aggregate-status\" " +
"style=\"background-color:${bgColor}; width:${width}px; height:${height}px; " +
"margin-top:${marginTop}px; margin-right:${marginRight}px; margin-bottom:${marginBottom}px; margin-left:${marginLeft}px;\">\n" +
" <h3>${title}</h3>\n" +
" <h2>${value}</h2>\n" +
"</div>";
public static final String DEFAULT_JS_TEMPLATE = "if (${isFilterEnabled}) { \n" +
" var filterOn = false;\n" +
" ${this}.style.cursor=\"pointer\";\n" +
"\n" +
" ${this}.onmouseover = function() {\n" +
" if (!filterOn) ${this}.style.backgroundColor = \"lightblue\";\n" +
" };\n" +
" ${this}.onmouseout = function() {\n" +
" if (!filterOn) ${this}.style.backgroundColor = \"${bgColor}\";\n" +
" };\n" +
" ${this}.onclick = function() {\n" +
" filterOn = !filterOn;\n" +
" ${this}.style.backgroundColor = filterOn ? \"lightblue\" : \"${bgColor}\";\n" +
" ${doFilter};\n" +
" };\n" +
"}";
protected View view;
protected boolean filterOn = false;
protected StringTemplateBuilder codeBuilder = new StringTemplateBuilder();
public MetricDisplayer() {
this(new MetricView());
}
public MetricDisplayer(View view) {
this.view = view;
this.view.init(this);
}
@Override
public View getView() {
return view;
}
@Override
public DisplayerConstraints createDisplayerConstraints() {
DataSetLookupConstraints lookupConstraints = new DataSetLookupConstraints()
.setGroupAllowed(false)
.setMaxColumns(1)
.setMinColumns(1)
.setFunctionRequired(true)
.setExtraColumnsAllowed(false)
.setColumnsTitle(view.getColumnsTitle())
.setColumnTypes(new ColumnType[] {
ColumnType.NUMBER});
return new DisplayerConstraints(lookupConstraints)
.supportsAttribute(DisplayerAttributeDef.TYPE)
.supportsAttribute(DisplayerAttributeDef.RENDERER)
.supportsAttribute(DisplayerAttributeGroupDef.COLUMNS_GROUP)
.supportsAttribute(DisplayerAttributeGroupDef.FILTER_GROUP)
.supportsAttribute(DisplayerAttributeGroupDef.REFRESH_GROUP)
.supportsAttribute(DisplayerAttributeGroupDef.GENERAL_GROUP)
.supportsAttribute(DisplayerAttributeDef.CHART_WIDTH)
.supportsAttribute(DisplayerAttributeDef.CHART_HEIGHT)
.supportsAttribute(DisplayerAttributeDef.CHART_BGCOLOR)
.supportsAttribute(DisplayerAttributeGroupDef.CHART_MARGIN_GROUP)
.supportsAttribute(DisplayerAttributeGroupDef.METER_GROUP)
.supportsAttribute(DisplayerAttributeGroupDef.HTML_GROUP);
}
@Override
protected void createVisualization() {
updateVisualization();
}
@Override
protected void updateVisualization() {
String template = getHtmlTemplate();
String html = parseHtmlTemplate(template);
view.setHtml(html);
// Invoke the onDraw JS callback if defined
String onDrawJs = getJsTemplate();
if (onDrawJs != null) {
onDrawJs = parseJsTemplate(onDrawJs);
view.eval(onDrawJs);
}
}
public String parseHtmlTemplate(String str) {
parseTemplate(str);
// Replace the extra keys with a reference to a displayer-bounded identifier
for (String key : codeBuilder.keys()) {
if (!TEMPLATE_KEYS.contains(key)) {
String id = getExtraKeyId(key);
codeBuilder.replace(key, id);
}
}
return codeBuilder.build();
}
public String parseJsTemplate(String str) {
parseTemplate(str);
// Replace the extra keys with a reference to its DOM element
for (String key : codeBuilder.keys()) {
if (!TEMPLATE_KEYS.contains(key)) {
String id = getExtraKeyId(key);
codeBuilder.replace(key, "document.getElementById(\"" + id + "\")");
}
}
return codeBuilder.build();
}
protected void parseTemplate(String template) {
boolean isEmpty = dataSet.getRowCount() == 0;
Double valueRaw = isEmpty ? 0 : (Double) dataSet.getValueAt(0, 0);
String valueStr = isEmpty ? view.getNoDataString() : super.formatValue(0, 0);
String title = displayerSettings.isTitleVisible() ? displayerSettings.getTitle() : "";
String bgcolor = displayerSettings.getChartBackgroundColor();
bgcolor = !StringUtils.isBlank(bgcolor) ? bgcolor : "white";
try {
Integer.parseInt(bgcolor, 16);
bgcolor = "#" + bgcolor;
} catch (NumberFormatException e) {
// No hash prefix needed
}
// Replace the core keys by their corresponding displayer settings.
codeBuilder.setTemplate(template);
codeBuilder.replace("value.raw", Double.toString(valueRaw))
.replace("value", valueStr != null ? valueStr : "")
.replace("title", title)
.replace("width", Integer.toString(displayerSettings.getChartWidth()))
.replace("height", Integer.toString(displayerSettings.getChartHeight()))
.replace("marginTop", Integer.toString(displayerSettings.getChartMarginTop()))
.replace("marginBottom", Integer.toString(displayerSettings.getChartMarginBottom()))
.replace("marginRight", Integer.toString(displayerSettings.getChartMarginRight()))
.replace("marginLeft", Integer.toString(displayerSettings.getChartMarginLeft()))
.replace("value.start", Long.toString(displayerSettings.getMeterStart()))
.replace("value.warning", Long.toString(displayerSettings.getMeterWarning()))
.replace("value.critical", Long.toString(displayerSettings.getMeterCritical()))
.replace("value.end", Long.toString(displayerSettings.getMeterEnd()))
.replace("bgColor", bgcolor)
.replace("isFilterEnabled", Boolean.toString(isFilterEnabled()))
.replace("isFilterOn", Boolean.toString(isFilterOn()))
.replace("isEmpty", Boolean.toString(isEmpty))
.replace("doFilter", "window.metricDisplayerDoFilter('" + view.getUniqueId() + "')");
}
public String getExtraKeyId(String key) {
return view.getUniqueId() + "_" + key;
}
public boolean isFilterOn() {
return filterOn;
}
public boolean isFilterEnabled() {
return displayerSettings.isFilterEnabled() && fetchFilter() != null;
}
public void updateFilter() {
if (isFilterEnabled()) {
if (filterOn) {
filterReset();
} else {
if (displayerSettings.isFilterEnabled()) {
filterApply();
}
}
}
}
public DataSetFilter fetchFilter() {
if (displayerSettings.getDataSetLookup() == null) {
return null;
}
List<DataSetFilter> filterOps = displayerSettings.getDataSetLookup().getOperationList(DataSetFilter.class);
if (filterOps == null || filterOps.isEmpty()) {
return null;
}
DataSetFilter filter = new DataSetFilter();
for (DataSetFilter filterOp : filterOps) {
filter.getColumnFilterList().addAll(filterOp.getColumnFilterList());
}
return filter;
}
public void filterApply() {
DataSetFilter filter = fetchFilter();
if (displayerSettings.isFilterEnabled() && filter != null) {
filterOn = true;
super.filterApply(filter);
}
}
@Override
public void filterReset() {
DataSetFilter filter = fetchFilter();
if (filterOn && filter != null) {
filterOn = false;
super.filterReset();
}
}
@Override
public String getHtmlTemplate() {
String template = displayerSettings.getHtmlTemplate();
if (StringUtils.isBlank(template)) {
return DEFAULT_HTML_TEMPLATE;
}
return template;
}
@Override
public Map<String, String> getHtmlVariableMap() {
return getCommonVariableMap();
}
@Override
public String getJsTemplate() {
String template = displayerSettings.getJsTemplate();
if (StringUtils.isBlank(template)) {
return DEFAULT_JS_TEMPLATE;
}
return template;
}
@Override
public Map<String, String> getJsVariableMap() {
Map<String, String> varMap = new HashMap<>();
// Append the user defined variables
String template = getHtmlTemplate();
codeBuilder.setTemplate(template);
for (String key : codeBuilder.keys()) {
if (!TEMPLATE_KEYS.contains(key)) {
String var = codeBuilder.asVar(key);
varMap.put(var, DisplayerConstants.INSTANCE.userDefinedVariableDescription());
}
}
varMap.putAll(getCommonVariableMap());
varMap.put(asVar("doFilter"), DisplayerConstants.INSTANCE.doFilterVariableDescription());
return varMap;
}
protected Map<String, String> getCommonVariableMap() {
Map<String, String> varMap = new HashMap<>();
varMap.put(asVar("value.raw"), DisplayerConstants.INSTANCE.valueRawVariableDescription());
varMap.put(asVar("value.start"), DisplayerConstants.INSTANCE.valueStartVariableDescription());
varMap.put(asVar("value.warning"), DisplayerConstants.INSTANCE.valueWarningVariableDescription());
varMap.put(asVar("value.critical"), DisplayerConstants.INSTANCE.valueCriticalVariableDescription());
varMap.put(asVar("value.end"), DisplayerConstants.INSTANCE.valueEndVariableDescription());
varMap.put(asVar("value"), DisplayerConstants.INSTANCE.valueVariableDescription());
varMap.put(asVar("title"), DisplayerConstants.INSTANCE.titleVariableDescription());
varMap.put(asVar("width"), DisplayerConstants.INSTANCE.widthVariableDescription());
varMap.put(asVar("height"), DisplayerConstants.INSTANCE.heightVariableDescription());
varMap.put(asVar("marginTop"), DisplayerConstants.INSTANCE.marginTopVariableDescription());
varMap.put(asVar("marginBottom"), DisplayerConstants.INSTANCE.marginBottomVariableDescription());
varMap.put(asVar("marginRight"), DisplayerConstants.INSTANCE.marginRightVariableDescription());
varMap.put(asVar("marginLeft"), DisplayerConstants.INSTANCE.marginLeftVariableDescription());
varMap.put(asVar("bgColor"), DisplayerConstants.INSTANCE.bgColorVariableDescription());
varMap.put(asVar("isFilterEnabled"), DisplayerConstants.INSTANCE.isFilterEnabledVariableDescription());
varMap.put(asVar("isFilterOn"), DisplayerConstants.INSTANCE.isFilterOnVariableDescription());
varMap.put(asVar("isEmpty"), DisplayerConstants.INSTANCE.isEmptyVariableDescription());
return varMap;
}
protected String asVar(String key) {
return codeBuilder.asVar(key);
}
}