/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 ro.nextreports.server.web.dashboard.indicator;
import java.awt.Color;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnLoadHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.EmptyPanel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.spring.injection.annot.SpringBean;
import ro.nextreports.engine.ReportRunnerException;
import ro.nextreports.engine.exporter.exception.NoDataFoundException;
import ro.nextreports.engine.exporter.util.IndicatorData;
import ro.nextreports.server.service.DashboardService;
import ro.nextreports.server.web.NextServerApplication;
import ro.nextreports.server.web.dashboard.WidgetView;
import ro.nextreports.server.web.dashboard.chart.ChartHTML5Panel;
import ro.nextreports.server.web.dashboard.model.WidgetModel;
public class IndicatorWidgetView extends WidgetView {
private static final long serialVersionUID = 1L;
private final ResourceReference INDICATOR_UTIL_JS = new JavaScriptResourceReference(ChartHTML5Panel.class, NextServerApplication.NEXT_CHARTS_JS);
private String PARAM = "Param";
private String isHTML5 = "";
private WebMarkupContainer container;
@SpringBean
private DashboardService dashboardService;
public IndicatorWidgetView(String id, WidgetModel model, boolean zoom) {
this(id, model, zoom, null);
}
@SuppressWarnings("unchecked")
public IndicatorWidgetView(String id, WidgetModel model, boolean zoom, Map<String, Object> urlQueryParameters) {
super(id, model, zoom);
final String widgetId = model.getWidgetId();
final IndicatorModel indicatorModel = new IndicatorModel(widgetId, urlQueryParameters);
setModel(new CompoundPropertyModel(indicatorModel));
add(new Label("error", new Model<String>()) {
private static final long serialVersionUID = 1L;
@Override
protected void onInitialize() {
super.onInitialize();
if (indicatorModel.getObject() == null) {
if (indicatorModel.getError() instanceof NoDataFoundException) {
setDefaultModelObject(getString("ActionContributor.Run.nodata"));
} else {
setDefaultModelObject(ExceptionUtils.getRootCauseMessage(indicatorModel.getError()));
}
}
}
@Override
public boolean isVisible() {
return indicatorModel.hasError();
}
});
container = new WebMarkupContainer("indicator") {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return !indicatorModel.hasError();
}
};
container.setOutputMarkupId(true);
// indicator background color is set to the entire container
Color background = indicatorModel.getObject().getBackground();
String s = "background-color: rgb(" +
background.getRed() + "," +
background.getGreen() + "," +
background.getBlue() + ");";
container.add(new AttributeAppender("style", s));
add(container);
add(new HTML5Behavior(zoom, indicatorModel));
container.add(new EmptyPanel("image"));
//IndicatorHTML5Panel panel = new IndicatorHTML5Panel("image", width, height, indicatorModel);
//IndicatorImagePanel panel = new IndicatorImagePanel("image", width, height, indicatorModel);
//container.add(panel);
}
private IndicatorData getIndicatorData(String widgetId, Map<String, Object> urlQueryParameters) {
try {
return dashboardService.getIndicatorData(widgetId, urlQueryParameters);
} catch (ReportRunnerException e) {
throw new RuntimeException(e);
} catch (NoDataFoundException e) {
IndicatorData data = new IndicatorData();
data.setTitle("No Data");
return data;
} catch (TimeoutException e) {
IndicatorData data = new IndicatorData();
data.setTitle("Timeout Elapsed");
return data;
}
}
private class IndicatorModel extends LoadableDetachableModel<IndicatorData> {
private static final long serialVersionUID = 1L;
private Exception error;
private String widgetId;
private Map<String, Object> urlQueryParameters;
public IndicatorModel(String widgetId, Map<String, Object> urlQueryParameters) {
super();
this.widgetId = widgetId;
this.urlQueryParameters = urlQueryParameters;
}
@Override
protected IndicatorData load() {
error = null;
try {
return getIndicatorData(widgetId, urlQueryParameters);
} catch (Exception e) {
e.printStackTrace();
error = e;
return null;
}
}
public Exception getError() {
return error;
}
public boolean hasError() {
return error != null;
}
}
// if canvas is supported for HTML5 we will show IndicatorHTML5Panel
// else we will show IndicatorImagePanel
private class HTML5Behavior extends AbstractDefaultAjaxBehavior {
private static final long serialVersionUID = 1L;
private String width;
private String height;
private IndicatorModel indicatorModel;
public HTML5Behavior(boolean zoom, IndicatorModel indicatorModel) {
super();
this.indicatorModel = indicatorModel;
// height used to have two indicators (one under the other) in dashbord to occupy same height as a single chart
width = "200";
height = "122";
if (zoom) {
width = "100%";
height = "100%";
}
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
StringBuilder javaScript = new StringBuilder();
javaScript.append("var data = isCanvasEnabled();");
javaScript.append("console.log(data);");
javaScript.append("return { '" + PARAM + "': data }");
attributes.getDynamicExtraParameters().add(javaScript);
}
@Override
public void renderHead(Component component, IHeaderResponse response) {
super.renderHead(component, response);
//include js file
response.render(JavaScriptHeaderItem.forReference(INDICATOR_UTIL_JS));
response.render(OnLoadHeaderItem.forScript(getCallbackFunctionBody()));
}
@Override
protected void respond(AjaxRequestTarget target) {
String param = getRequest().getRequestParameters().getParameterValue(PARAM).toString();
//System.out.println("---> "+param);
// behavior is called on any refresh, we have to call it only once
// (otherwise the panel will be replaced in the same time the old one is refreshed)
if (isHTML5.isEmpty()) {
isHTML5 = param;
if (Boolean.parseBoolean(param)) {
container.replace(new IndicatorHTML5Panel("image", width, height, indicatorModel).setOutputMarkupId(true));
} else {
// for image height must be a little less than html5 panel
// to have two indicators (one under the other) in dashboard to occupy same height as a single chart
if ("122".equals(height)) {
height = "120";
}
container.replace(new IndicatorImagePanel("image", width, height, indicatorModel).setOutputMarkupId(true));
}
target.add(container);
}
}
}
}