/*
* OpenFaces - JSF Component Library 2.0
* Copyright (C) 2007-2012, TeamDev Ltd.
* licensing@openfaces.org
* Unless agreed in writing the contents of this file are subject to
* the GNU Lesser General Public License Version 2.1 (the "LGPL" License).
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* Please visit http://openfaces.org/licensing/ for more details.
*/
package org.openfaces.component.chart;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.Plot;
import org.openfaces.component.OUIData;
import org.openfaces.component.OUIObjectIteratorBase;
import org.openfaces.component.chart.impl.JfcRenderHints;
import org.openfaces.component.chart.impl.ModelInfo;
import org.openfaces.component.chart.impl.helpers.JFreeChartAdapter;
import org.openfaces.renderkit.chart.ChartDefaultStyle;
import org.openfaces.renderkit.cssparser.CSSUtil;
import org.openfaces.renderkit.cssparser.StyleObjectModel;
import org.openfaces.renderkit.cssparser.StyledComponent;
import org.openfaces.taglib.internal.chart.ChartTag;
import org.openfaces.util.Components;
import org.openfaces.util.ValueBindings;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The Chart component represents various datasets in a graphical form, for example as a pie,
* line, or bar charts. The component is based on the JFreeChart engine and exposes a friendly
* API with JSF-specific features. Styles can be customized for every chart element (image,
* legend, title, etc.).
*
* @author Ekaterina Shliakhovetskaya
*/
public class Chart extends OUIObjectIteratorBase implements StyledComponent, NamingContainer {
public static final String COMPONENT_TYPE = "org.openfaces.Chart";
public static final String COMPONENT_FAMILY = "org.openfaces.Chart";
public static final StyledComponent DEFAULT_CHART_STYLE = new ChartDefaultStyle();
private Integer width;
private Integer height;
private ChartViewType view;
private ChartModel model;
private ChartLegend legend;
private Boolean legendVisible;
private String textStyle;
private ChartTitle title; //todo(question): maybe better reduce API to just using <chartTitle> tag?
private JfcRenderHints renderHints = new JfcRenderHints();
private byte[] imageBytes;
private ChartNoDataMessage noDataMessage;
private TimePeriod timePeriodPrecision;
private Object initialDescendantComponentState = null;
private Map<String, Object> descendantComponentState = new HashMap<String, Object>();
private int entityIndex = -1;
private int selectedIndex = -1;
public Chart() {
setRendererType(ChartTag.RENDERER_TYPE);
}
public String getFamily() {
return COMPONENT_FAMILY;
}
public ChartViewType getView() {
return ValueBindings.get(this, "view", view, ChartViewType.class);
}
public void setView(ChartViewType view) {
this.view = view;
}
public JfcRenderHints getRenderHints() {
return renderHints;
}
public void setRenderHints(JfcRenderHints renderHints) {
this.renderHints = renderHints;
}
public ChartNoDataMessage getNoDataMessage() {
return noDataMessage;
}
public void setNoDataMessage(ChartNoDataMessage noDataMessage) {
this.noDataMessage = noDataMessage;
}
public int getHeight() {
return ValueBindings.get(this, "height", height, 400);
}
public void setHeight(int height) {
this.height = height;
}
public int getWidth() {
return ValueBindings.get(this, "width", width, 400);
}
public void setWidth(int width) {
this.width = width;
}
public ChartLegend getLegend() {
return legend;
}
public void setLegend(ChartLegend legend) {
this.legend = legend;
}
public boolean isLegendVisible() {
return ValueBindings.get(this, "legendVisible", legendVisible, true);
}
public void setLegendVisible(boolean legendVisible) {
this.legendVisible = legendVisible;
}
public ChartTitle getTitle() {
return title;
}
public void setTitle(ChartTitle title) {
this.title = title;
}
public ChartView getChartView() {
List<UIComponent> children = getChildren();
ChartView view = null;
for (UIComponent child : children) {
if (child instanceof ChartView) {
if (view != null)
throw new RuntimeException("There should be only one view child under this component: " + getId());
view = (ChartView) child;
}
}
ChartViewType viewType = getView();
if (viewType == null)
return view;
if (view == null) {
view = viewType.createChartView();
children.add(view);
return view;
}
if (!viewType.isViewOfThisType(view)) {
children.remove(view);
view = viewType.createChartView();
children.add(view);
}
return view;
}
public String getTextStyle() {
return ValueBindings.get(this, "testStyle", textStyle);
}
public void setTextStyle(String style) {
textStyle = style;
}
public StyleObjectModel getStyleObjectModel() {
return CSSUtil.getStyleObjectModel(getComponentsChain());
}
public StyledComponent[] getComponentsChain() {
StyledComponent[] chain = new StyledComponent[2];
chain[0] = DEFAULT_CHART_STYLE;
chain[1] = this;
return chain;
}
public String getHint() {
return null;
}
public ChartModel getModel() {
return ValueBindings.get(this, "model", model, ChartModel.class);
}
public void setModel(ChartModel model) {
this.model = model;
}
public byte[] getImageBytes() {
return imageBytes;
}
public void setImageBytes(byte[] imageBytes) {
this.imageBytes = imageBytes;
}
public ChartMenu getChartMenu() {
return Components.findChildWithClass(this, ChartMenu.class, "<o:chartMenu>");
}
public ChartSelection getChartSelection() {
return Components.findChildWithClass(this, ChartSelection.class, "<o:chartSelection>");
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
setEntityIndex(-1);
initialDescendantComponentState = null;
descendantComponentState.clear();
super.encodeBegin(context);
}
@Override
public void encodeEnd(FacesContext context) throws IOException {
super.encodeEnd(context);
setEntityIndex(-1);
}
@Override
public void processDecodes(FacesContext context) {
super.processDecodes(context);
if (getEntityIndex() != -1) {
setEntityIndex(-1);
}
}
@Override
public void processValidators(FacesContext context) {
super.processValidators(context);
if (getEntityIndex() != -1) {
setEntityIndex(-1);
}
}
@Override
public void processUpdates(FacesContext context) {
super.processUpdates(context);
if (getEntityIndex() != -1) {
setEntityIndex(-1);
}
}
public Integer getEntityIndex() {
return entityIndex;
}
public void setEntityIndex(int index) {
if (this.entityIndex == index) {
return;
}
if (index < -1) {
throw new IllegalArgumentException("index is less than -1");
}
FacesContext facesContext = getFacesContext();
if (this.entityIndex == -1) {
if (initialDescendantComponentState == null) {
initialDescendantComponentState = OUIData.saveDescendantComponentStates(getChildren().iterator(), false);
}
} else {
descendantComponentState.put(getClientId(facesContext), OUIData.saveDescendantComponentStates(getChildren().iterator(), false));
}
this.entityIndex = index;
if (index == -1) {
OUIData.restoreDescendantComponentStates(getChildren().iterator(), initialDescendantComponentState, false);
} else {
Object rowState = descendantComponentState.get(getClientId(facesContext));
if (rowState == null) {
OUIData.restoreDescendantComponentStates(getChildren().iterator(), initialDescendantComponentState, false);
} else {
OUIData.restoreDescendantComponentStates(getChildren().iterator(), rowState, false);
}
}
}
public void setObjectId(String objectId) {
if (objectId != null) {
setEntityIndex(Integer.valueOf(objectId));
setId(getId());
} else
setEntityIndex(-1);
}
public String getObjectId() {
if (getEntityIndex() != -1)
return getEntityIndex().toString();
else
return null;
}
private JFreeChart getJFreeChart() {
ChartRenderingInfo chartRenderingInfo = new ChartRenderingInfo();
renderHints.setRenderingInfo(chartRenderingInfo);
ChartModel model = getModel();
ModelInfo info = new ModelInfo(model);
renderHints.setModelInfo(info);
Plot plot = PlotFactory.createPlot(this, info);
return new JFreeChartAdapter(plot, this);
}
public BufferedImage make() {
JFreeChart jFreeChart = getJFreeChart();
ChartRenderingInfo chartRenderingInfo = renderHints.getRenderingInfo();
return jFreeChart.createBufferedImage(getWidth(), getHeight(), chartRenderingInfo);
}
@Override
public Object saveState(FacesContext facesContext) {
Object superState = super.saveState(facesContext);
return new Object[]{
superState,
view,
height,
width,
model,
legendVisible,
textStyle,
imageBytes,
saveAttachedState(facesContext, renderHints),
saveAttachedState(facesContext, timePeriodPrecision),
};
}
@Override
public void restoreState(FacesContext facesContext, Object object) {
Object[] state = (Object[]) object;
int i = 0;
super.restoreState(facesContext, state[i++]);
view = (ChartViewType) state[i++];
height = (Integer) state[i++];
width = (Integer) state[i++];
model = (ChartModel) state[i++];
legendVisible = (Boolean) state[i++];
textStyle = (String) state[i++];
imageBytes = (byte[]) state[i++];
renderHints = (JfcRenderHints) restoreAttachedState(facesContext, state[i++]);
timePeriodPrecision = (TimePeriod) restoreAttachedState(facesContext, state[i++]);
}
public TimePeriod getTimePeriodPrecision() {
return ValueBindings.get(this, "timePeriodPrecision", timePeriodPrecision, TimePeriod.DAY, TimePeriod.class);
}
public void setTimePeriodPrecision(TimePeriod timePeriodPrecision) {
this.timePeriodPrecision = timePeriodPrecision;
}
}