001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.component.outputchart;
035:
036: import com.icesoft.faces.component.CSS_DEFAULT;
037: import com.icesoft.faces.component.ext.HtmlCommandButton;
038: import com.icesoft.faces.component.ext.renderkit.FormRenderer;
039: import com.icesoft.faces.component.ext.taglib.Util;
040: import com.icesoft.faces.context.DOMContext;
041: import com.icesoft.faces.context.DOMResponseWriter;
042: import org.krysalis.jcharts.Chart;
043: import org.krysalis.jcharts.imageMap.ImageMapArea;
044: import org.w3c.dom.Element;
045: import org.w3c.dom.Text;
046:
047: import javax.faces.component.UIComponent;
048: import javax.faces.context.ExternalContext;
049: import javax.faces.context.FacesContext;
050: import javax.faces.el.MethodBinding;
051: import javax.faces.el.ValueBinding;
052: import javax.faces.event.AbortProcessingException;
053: import javax.faces.event.ActionEvent;
054: import javax.faces.event.FacesEvent;
055: import javax.faces.event.ActionListener;
056: import javax.servlet.ServletContext;
057: import javax.servlet.http.HttpServletRequest;
058:
059: import java.beans.Beans;
060: import java.io.File;
061: import java.io.FileNotFoundException;
062: import java.io.FileOutputStream;
063: import java.io.IOException;
064: import java.io.OutputStream;
065: import java.io.Serializable;
066: import java.util.Iterator;
067: import java.util.Map;
068:
069: public class OutputChart extends HtmlCommandButton implements
070: Serializable {
071:
072: public static String AREA_CHART_TYPE = "area";
073: public static String AREA_STACKED_CHART_TYPE = "areastacked";
074: public static String BAR_CHART_TYPE = "bar";
075: public static String BAR_CLUSTERED_CHART_TYPE = "barclustered";
076: public static String BAR_STACKED_CHART_TYPE = "barstacked";
077: public static String LINE_CHART_TYPE = "line";
078: public static String PIE2D_CHART_TYPE = "pie2D";
079: public static String PIE3D_CHART_TYPE = "pie3D";
080: public static String POINT_CHART_TYPE = "point";
081: public static String STOCK_CHART_TYPE = "stock";
082: public static String CUSTOM_CHART_TYPE = "custom";
083: public static String DEFAULT_CHART_TYPE = BAR_CHART_TYPE;
084:
085: private static int COMPONENT_ID = 0;
086: private static int CLIENT_SIDE_IMAGE_MAP_KEY = 1;
087: private static String DEFAULT_HEIGHT = "400";
088: private static String DEFAULT_WIDTH = "400";
089:
090: private static String DEFAULT_CHART_TITLE = "Default Chart title";
091: private static String DEFAULT_YAXIS_TITLE = "Default Y title";
092: private static String DEFAULT_XAXIS_TITLE = "Default X title";
093: private static String DEFAULT_DATA = "20, 30, 40";
094: static String ICE_CHART_COMPONENT = "iceChartComponent";
095: private int imageCounter = 0;
096: private String width;
097: private String height;
098:
099: private boolean render = false;
100: private FileOutputStream out;
101:
102: private String chartTitle;
103: private Object data;
104: private Object labels;
105: private Object colors;
106: private Object shapes;
107:
108: private Object xaxisLabels;
109: private String xaxisTitle;
110: private String yaxisTitle;
111: private String style = null;
112: private String styleClass = null;
113: private Object legendPlacement;
114: private Object legendColumns;
115: private boolean horizontal;
116: private boolean horizontalSet;
117: File folder = null;
118:
119: public OutputChart() {
120: setRendererType("com.icesoft.faces.OutputChartRenderer");
121: }
122:
123: /**
124: *<p>Return the value of the <code>labels</code> property.</p>
125: */
126: public Object getLabels() {
127: if (labels != null) {
128: return labels;
129: }
130: ValueBinding vb = getValueBinding("labels");
131: return vb != null ? vb.getValue(getFacesContext()) : null;
132: }
133:
134: /**
135: * <p>Set the value of the <code>labels</code> property. </p>
136: */
137: public void setLabels(Object labels) {
138: this .labels = labels;
139: }
140:
141: /**
142: *<p>Return a boolean flag which tells if the chart can have a
143: *clientSideImageMap or not.</p>
144: */
145: public boolean isClientSideImageMap() {
146: if (hasActionListener()
147: && (!getType().equalsIgnoreCase(AREA_CHART_TYPE) && !getType()
148: .equalsIgnoreCase(AREA_STACKED_CHART_TYPE))) {
149: return true;
150: } else {
151: return false;
152: }
153: }
154:
155: protected boolean hasActionListener() {
156: MethodBinding actionListener = getActionListener();
157: if (actionListener != null) {
158: return true;
159: }
160: ActionListener[] actionListeners = getActionListeners();
161: if (actionListeners != null && actionListeners.length > 0) {
162: return true;
163: }
164: return false;
165: }
166:
167: /**
168: *<p>Return the value of the <code>data</code> property.</p>
169: */
170: public Object getData() {
171: if (data != null) {
172: return data;
173: }
174: ValueBinding vb = getValueBinding("data");
175: if (vb != null) {
176: return vb.getValue(getFacesContext());
177: } else {
178:
179: if (!Beans.isDesignTime()) {
180: setChartTitle(getChartTitle() + " with default data");
181: }
182: return DEFAULT_DATA;
183: }
184: }
185:
186: /**
187: * <p>Set the value of the <code>data</code> property. </p>
188: */
189: public void setData(Object data) {
190: this .data = data;
191: }
192:
193: /**
194: *<p>Return the value of the <code>colors</code> property.</p>
195: */
196: public Object getColors() {
197: if (colors != null) {
198: return colors;
199: }
200: ValueBinding vb = getValueBinding("colors");
201: return vb != null ? vb.getValue(getFacesContext()) : null;
202: }
203:
204: /**
205: * <p>Set the value of the <code>colors</code> property. </p>
206: */
207: public void setColors(Object colors) {
208: this .colors = colors;
209: }
210:
211: /**
212: *<p>Return the value of the <code>shapes</code> property.</p>
213: */
214: public Object getShapes() {
215: if (shapes != null) {
216: return shapes;
217: }
218: ValueBinding vb = getValueBinding("shapes");
219: return vb != null ? vb.getValue(getFacesContext()) : null;
220: }
221:
222: /**
223: * <p>Set the value of the <code>shapes</code> property. </p>
224: */
225: public void setShapes(Object shapes) {
226: this .shapes = shapes;
227: }
228:
229: /**
230: *<p>Return the value of the <code>xaxisTitle</code> property.</p>
231: */
232: public String getXaxisTitle() {
233: if (xaxisTitle != null) {
234: return xaxisTitle;
235: }
236: ValueBinding vb = getValueBinding("xaxisTitle");
237: return vb != null ? (String) vb.getValue(getFacesContext())
238: : DEFAULT_XAXIS_TITLE;
239: }
240:
241: /**
242: * <p>Set the value of the <code>xaxisTitle</code> property. </p>
243: */
244: public void setXaxisTitle(String xaxisTitle) {
245: this .xaxisTitle = xaxisTitle;
246: }
247:
248: /**
249: *<p>Return the value of the <code>yaxisTitle</code> property.</p>
250: */
251: public String getYaxisTitle() {
252: if (yaxisTitle != null) {
253: return yaxisTitle;
254: }
255: ValueBinding vb = getValueBinding("yaxisTitle");
256: return vb != null ? (String) vb.getValue(getFacesContext())
257: : DEFAULT_YAXIS_TITLE;
258: }
259:
260: /**
261: * <p>Set the value of the <code>yaxisTitle</code> property. </p>
262: */
263: public void setYaxisTitle(String yaxisTitle) {
264: this .yaxisTitle = yaxisTitle;
265: }
266:
267: /**
268: *<p>Return the value of the <code>xaxisLabels</code> property.</p>
269: */
270: public Object getXaxisLabels() {
271: if (xaxisLabels != null) {
272: return xaxisLabels;
273: }
274: ValueBinding vb = getValueBinding("xaxisLabels");
275: return vb != null ? vb.getValue(getFacesContext()) : null;
276: }
277:
278: /**
279: * <p>Set the value of the <code>xaxisLabels</code> property. </p>
280: */
281: public void setXaxisLabels(Object xaxisLabels) {
282: this .xaxisLabels = xaxisLabels;
283: }
284:
285: /**
286: *<p>Return the value of the <code>chartTitle</code> property.</p>
287: */
288: public String getChartTitle() {
289: if (chartTitle != null) {
290: return chartTitle;
291: }
292: ValueBinding vb = getValueBinding("chartTitle");
293: return vb != null ? (String) vb.getValue(getFacesContext())
294: : DEFAULT_CHART_TITLE;
295: }
296:
297: /**
298: * <p>Set the value of the <code>chartTitle</code> property. </p>
299: */
300: public void setChartTitle(String chartTitle) {
301: this .chartTitle = chartTitle;
302: }
303:
304: /**
305: *<p>Return the value of the <code>width</code> property.</p>
306: */
307: public String getWidth() {
308: if (width != null) {
309: return width;
310: }
311: ValueBinding vb = getValueBinding("width");
312: return vb != null ? (String) vb.getValue(getFacesContext())
313: : DEFAULT_WIDTH;
314: }
315:
316: /**
317: * <p>Set the value of the <code>width</code> property. </p>
318: */
319: public void setWidth(String width) {
320: this .width = width;
321: }
322:
323: /**
324: *<p>Return the value of the <code>height</code> property.</p>
325: */
326: public String getHeight() {
327: if (height != null) {
328: return height;
329: }
330: ValueBinding vb = getValueBinding("height");
331: return vb != null ? (String) vb.getValue(getFacesContext())
332: : DEFAULT_HEIGHT;
333: }
334:
335: /**
336: * <p>Set the value of the <code>height</code> property. </p>
337: */
338: public void setHeight(String height) {
339: this .height = height;
340: }
341:
342: private MethodBinding renderOnSubmitMethodBinding;
343:
344: public void setRenderOnSubmit(MethodBinding renderOnSubmit) {
345: renderOnSubmitMethodBinding = renderOnSubmit;
346: }
347:
348: public MethodBinding getRenderOnSubmit() {
349: return renderOnSubmitMethodBinding;
350: }
351:
352: public Boolean evaluateRenderOnSubmit(FacesContext context) {
353: if (renderOnSubmitMethodBinding != null) {
354: Boolean b = (Boolean) renderOnSubmitMethodBinding.invoke(
355: context, new Object[] { this });
356: return b;
357: }
358: return Boolean.FALSE;
359: }
360:
361: /*
362: * (non-Javadoc)
363: * @see javax.faces.component.UIComponent#encodeBegin(javax.faces.context.FacesContext)
364: */
365: public void encodeBegin(FacesContext context) throws IOException {
366:
367: if (!Beans.isDesignTime()) {
368: try {
369: if (getAbstractChart() == null) {
370: createAbstractChart();
371: if (getType().equalsIgnoreCase(
372: OutputChart.CUSTOM_CHART_TYPE)) {
373: evaluateRenderOnSubmit(context);
374: }
375: getAbstractChart().encode();
376: } else if (evaluateRenderOnSubmit(context)
377: .booleanValue()) {
378: getAbstractChart().encode();
379: }
380: } catch (Throwable e) {
381: e.printStackTrace();
382: }
383: }
384: super .encodeBegin(context);
385: }
386:
387: /*
388: * (non-Javadoc)
389: * @see javax.faces.component.UIComponent#broadcast(javax.faces.event.FacesEvent)
390: */
391: public void broadcast(FacesEvent event)
392: throws AbortProcessingException {
393: super .broadcast(event);
394: }
395:
396: /*
397: * (non-Javadoc)
398: * @see javax.faces.component.UIComponent#decode(javax.faces.context.FacesContext)
399: */
400: public void decode(FacesContext context) {
401:
402: Map requestParameterMap = context.getExternalContext()
403: .getRequestParameterMap();
404: String chartComponentRequestIdentifier = (String) requestParameterMap
405: .get(OutputChart.ICE_CHART_COMPONENT);
406: if (chartComponentRequestIdentifier != null) {
407: String[] submittedValue = chartComponentRequestIdentifier
408: .split("id-key");
409: if (submittedValue[COMPONENT_ID]
410: .equals(getClientId(context))) {
411: ImageMapArea area = (ImageMapArea) getGeneratedImageMapArea()
412: .get(submittedValue[CLIENT_SIDE_IMAGE_MAP_KEY]);
413: if (area != null) {
414: setClickedImageMapArea(area);
415: queueEvent(new ActionEvent(this ));
416: }
417: }
418: }
419: super .decode(context);
420: }
421:
422: private String getChartFileName() {
423: HttpServletRequest req = (HttpServletRequest) ((ExternalContext) FacesContext
424: .getCurrentInstance().getExternalContext())
425: .getRequest();
426: return this .getType()
427: + this .getClientId(FacesContext.getCurrentInstance())
428: .replaceAll(":", "")
429: + req.getRequestedSessionId() + imageCounter++
430: + ".jpeg";
431: }
432:
433: /**
434: *<p>Return the the path where the chart is stored.</p>
435: */
436: public String getPath() {
437: String folder = "/";
438: if (getFacesContext() != null
439: && getFacesContext().getExternalContext() != null) {
440: if (DOMResponseWriter.isStreamWriting()) {
441: folder = "./web/chart/images";
442: } else {
443: folder = ((ServletContext) getFacesContext()
444: .getExternalContext().getContext())
445: .getRealPath("/charts");
446: }
447: }
448: return folder;
449: }
450:
451: /**
452: *<p>Return the value of the <code>chart</code> property.</p>
453: */
454: public Chart getChart() {
455: return getAbstractChart().getChart();
456: }
457:
458: /**
459: * <p>Set the value of the <code>chart</code> property. </p>
460: */
461: public void setChart(Chart chart) {
462: getAbstractChart().setChart(chart);
463: }
464:
465: public void render() {
466: render = true;
467: }
468:
469: public boolean isRender() {
470: return render;
471: }
472:
473: private String type = null;
474:
475: /**
476: * <p>Set the value of the <code>type</code> property. </p>
477: */
478: public void setType(String type) {
479: this .type = type;
480: }
481:
482: /**
483: *<p>Return the value of the <code>type</code> property.</p>
484: */
485: public String getType() {
486: if (type != null) {
487: return type;
488: }
489: ValueBinding vb = getValueBinding("type");
490: return vb != null ? (String) vb.getValue(getFacesContext())
491: : DEFAULT_CHART_TYPE;
492: }
493:
494: /**
495: *<p>Return the value of the <code>fileName</code> property.</p>
496: */
497: public String getFileName() {
498: return getAbstractChart().getImageFile().getName();
499: }
500:
501: /**
502: *<p>Return the value of the <code>data</code> property.</p>
503: */
504: OutputStream getNewOutputStream() {
505: //removed the old image, if exist
506: if (getAbstractChart().getImageFile() != null) {
507: getAbstractChart().getImageFile().delete();
508: }
509: getAbstractChart().setImageFile(
510: new File(getFolder(), getChartFileName()));
511: try {
512: return out = new FileOutputStream(getAbstractChart()
513: .getImageFile());
514: } catch (FileNotFoundException e) {
515: // TODO Auto-generated catch block
516: e.printStackTrace();
517: }
518: return null;
519: }
520:
521: File getFolder() {
522:
523: if (Beans.isDesignTime()) {
524: folder = new File(getPath());
525: return folder;
526: }
527:
528: if (folder == null) {
529: folder = new File(getPath());
530: if (folder != null) {
531: if (!folder.exists()) {
532: folder.mkdirs();
533: }
534: }
535: }
536: return folder;
537: }
538:
539: /**
540: *<p>Return the value of the <code>data</code> property.</p>
541: */
542: public OutputStream getOutputStream() {
543: return out;
544: }
545:
546: Map getGeneratedImageMapArea() {
547: return getAbstractChart().getGeneratedImageMapArea();
548: }
549:
550: /**
551: *<p>Return the value of the <code>data</code> property.</p>
552: */
553: public ImageMapArea getClickedImageMapArea() {
554: return getAbstractChart().getClickedImageMapArea();
555: }
556:
557: public void setClickedImageMapArea(ImageMapArea clickedImageMapArea) {
558: getAbstractChart().setClickedImageMapArea(clickedImageMapArea);
559: }
560:
561: void generateClientSideImageMap(DOMContext domContext, Element map) {
562: if (isClientSideImageMap()) {
563: Iterator area = getGeneratedImageMapArea().values()
564: .iterator();
565: while (area.hasNext()) {
566: ImageMapArea areaMap = (ImageMapArea) area.next();
567: Text areaNode = domContext
568: .createTextNode(areaMap
569: .toHTML("title ='"
570: + areaMap.getLengendLabel()
571: + "' href=\"return false;\" onclick=\"document.forms['"
572: + getParentFormId()
573: + "']['"
574: + ICE_CHART_COMPONENT
575: + "'].value='"
576: + getClientId(getFacesContext())
577: + "id-key"
578: + areaMap.hashCode()
579: + "';iceSubmitPartial(document.forms['"
580: + getParentFormId()
581: + "'],this,event); return false;\""));
582: map.appendChild(areaNode);
583: }
584: } else {
585: //logging client side image Map was not enabled
586: }
587: }
588:
589: //cache parentFormId, if ClientSideImageMap is required
590: private String parentFormId;
591:
592: private String getParentFormId() {
593: if (parentFormId != null) {
594: return parentFormId;
595: }
596: UIComponent uiForm = FormRenderer.findForm(this );
597: if (uiForm == null) {
598: //TODO logging and exception
599: //The component must have to be a decendent of the for element, inorder to use clientSideImageMap
600: return null;
601: }
602: return (parentFormId = uiForm.getClientId(getFacesContext()));
603: }
604:
605: /**
606: * <p>Set the value of the <code>styleClass</code> property.</p>
607: */
608: public void setStyleClass(String styleClass) {
609: this .styleClass = styleClass;
610: }
611:
612: /**
613: * <p>Return the value of the <code>styleClass</code> property.</p>
614: */
615: public String getStyleClass() {
616: return Util.getQualifiedStyleClass(this , styleClass,
617: CSS_DEFAULT.OUTPUT_CHART_DEFAULT_STYLE_CLASS,
618: "styleClass");
619: }
620:
621: /**
622: * <p>Set the value of the <code>style</code> property.</p>
623: */
624: public void setStyle(String style) {
625: this .style = style;
626: }
627:
628: /**
629: * <p>Return the value of the <code>style</code> property.</p>
630: */
631: public String getStyle() {
632: if (style != null) {
633: return style;
634: }
635: ValueBinding vb = getValueBinding("style");
636: return vb != null ? (String) vb.getValue(getFacesContext())
637: : null;
638: }
639:
640: /**
641: *<p>Return the value of the <code>legendLabel</code> property.</p>
642: */
643: public Object getLegendPlacement() {
644: if (legendPlacement != null) {
645: return legendPlacement;
646: }
647: ValueBinding vb = getValueBinding("legendPlacement");
648: return vb != null ? vb.getValue(getFacesContext()) : "bottom";
649: }
650:
651: /**
652: * <p>Set the value of the <code>legendPlacement</code> property. </p>
653: */
654: public void setLegendPlacement(Object legendPlacement) {
655: this .legendPlacement = legendPlacement;
656: }
657:
658: /**
659: *<p>Return the value of the <code>legendColumns</code> property.</p>
660: */
661: public Object getLegendColumns() {
662: if (legendColumns != null) {
663: return legendColumns;
664: }
665: ValueBinding vb = getValueBinding("legendColumns");
666: return vb != null ? vb.getValue(getFacesContext()) : "0";
667: }
668:
669: /**
670: * <p>Set the value of the <code>legendColumns</code> property. </p>
671: */
672: public void setLegendColumns(Object legendColumns) {
673: this .legendColumns = legendColumns;
674: }
675:
676: public boolean isHorizontal() {
677: if (this .horizontalSet) {
678: return (this .horizontal);
679: }
680: ValueBinding vb = getValueBinding("horizontal");
681: if (vb != null) {
682: return (Boolean.TRUE.equals(vb.getValue(getFacesContext())));
683: } else {
684: return (this .horizontal);
685: }
686: }
687:
688: public void setHorizontal(boolean horizontal) {
689: if (horizontal != this .horizontal) {
690: this .horizontal = horizontal;
691: }
692: this .horizontalSet = true;
693: }
694:
695: private AbstractChart createAbstractChart() throws Throwable {
696: return (AbstractChart) this .getAttributes().put(
697: getClientId(getFacesContext()),
698: AbstractChart.createChart(this ));
699: }
700:
701: private AbstractChart getAbstractChart() {
702: Object abstractChart = this .getAttributes().get(
703: getClientId(getFacesContext()));
704: return (abstractChart != null) ? (AbstractChart) abstractChart
705: : null;
706: }
707:
708: /**
709: * <p>Gets the state of the instance as a <code>Serializable</code>
710: * Object.</p>
711: */
712: public Object saveState(FacesContext context) {
713: Object values[] = new Object[22];
714: values[0] = super .saveState(context);
715: values[1] = new Integer(imageCounter);
716: values[2] = width;
717: values[3] = height;
718: values[4] = render ? Boolean.TRUE : Boolean.FALSE;
719: values[5] = chartTitle;
720: values[6] = data;
721: values[7] = labels;
722: values[8] = colors;
723: values[9] = shapes;
724: values[10] = xaxisLabels;
725: values[11] = xaxisTitle;
726: values[12] = yaxisTitle;
727: values[13] = style;
728: values[14] = styleClass;
729: values[15] = legendPlacement;
730: values[16] = legendColumns;
731: values[17] = horizontal ? Boolean.TRUE : Boolean.FALSE;
732: values[18] = horizontalSet ? Boolean.TRUE : Boolean.FALSE;
733: values[19] = type;
734: values[20] = saveAttachedState(context,
735: renderOnSubmitMethodBinding);
736: return ((Object) (values));
737: }
738:
739: /**
740: * <p>Perform any processing required to restore the state from the entries
741: * in the state Object.</p>
742: */
743: public void restoreState(FacesContext context, Object state) {
744: Object values[] = (Object[]) state;
745: super .restoreState(context, values[0]);
746: imageCounter = ((Integer) values[1]).intValue();
747: width = (String) values[2];
748: height = (String) values[3];
749: render = ((Boolean) values[4]).booleanValue();
750: chartTitle = (String) values[5];
751: data = values[6];
752: labels = values[7];
753: colors = values[8];
754: shapes = values[9];
755: xaxisLabels = values[10];
756: xaxisTitle = (String) values[11];
757: yaxisTitle = (String) values[12];
758: style = (String) values[13];
759: styleClass = (String) values[14];
760: legendPlacement = values[15];
761: legendColumns = values[16];
762: horizontal = ((Boolean) values[17]).booleanValue();
763: horizontalSet = ((Boolean) values[18]).booleanValue();
764: type = (String) values[19];
765: renderOnSubmitMethodBinding = (MethodBinding) restoreAttachedState(
766: context, values[20]);
767: }
768: }
|