001 package railo.runtime.tag; 002 003 import java.awt.Color; 004 import java.awt.Font; 005 import java.awt.GradientPaint; 006 import java.awt.Rectangle; 007 import java.awt.image.BufferedImage; 008 import java.io.ByteArrayOutputStream; 009 import java.io.IOException; 010 import java.io.OutputStream; 011 import java.io.Serializable; 012 import java.util.ArrayList; 013 import java.util.Collections; 014 import java.util.Iterator; 015 import java.util.List; 016 import java.util.TimeZone; 017 018 import org.jfree.chart.ChartFactory; 019 import org.jfree.chart.ChartRenderingInfo; 020 import org.jfree.chart.ChartUtilities; 021 import org.jfree.chart.JFreeChart; 022 import org.jfree.chart.axis.Axis; 023 import org.jfree.chart.axis.CategoryAxis; 024 import org.jfree.chart.axis.ValueAxis; 025 import org.jfree.chart.block.ColumnArrangement; 026 import org.jfree.chart.block.LineBorder; 027 import org.jfree.chart.imagemap.ImageMapUtilities; 028 import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator; 029 import org.jfree.chart.imagemap.URLTagFragmentGenerator; 030 import org.jfree.chart.plot.CategoryPlot; 031 import org.jfree.chart.plot.PiePlot; 032 import org.jfree.chart.plot.PiePlot3D; 033 import org.jfree.chart.plot.Plot; 034 import org.jfree.chart.plot.PlotOrientation; 035 import org.jfree.chart.plot.XYPlot; 036 import org.jfree.chart.renderer.category.BarRenderer3D; 037 import org.jfree.chart.renderer.category.CategoryItemRenderer; 038 import org.jfree.chart.renderer.category.LineAndShapeRenderer; 039 import org.jfree.chart.renderer.xy.XYItemRenderer; 040 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 041 import org.jfree.chart.title.LegendTitle; 042 import org.jfree.chart.title.TextTitle; 043 import org.jfree.data.Range; 044 import org.jfree.data.category.CategoryDataset; 045 import org.jfree.data.category.DefaultCategoryDataset; 046 import org.jfree.data.general.DefaultPieDataset; 047 import org.jfree.data.time.Second; 048 import org.jfree.data.time.TimeSeries; 049 import org.jfree.data.time.TimeSeriesCollection; 050 import org.jfree.data.xy.XYDataset; 051 import org.jfree.ui.HorizontalAlignment; 052 import org.jfree.ui.RectangleAnchor; 053 import org.jfree.ui.RectangleEdge; 054 import org.jfree.ui.RectangleInsets; 055 import org.jfree.util.ShapeUtilities; 056 057 import railo.commons.color.ColorCaster; 058 import railo.commons.io.IOUtil; 059 import railo.commons.io.res.Resource; 060 import railo.commons.io.res.util.ResourceUtil; 061 import railo.commons.lang.Md5; 062 import railo.commons.lang.StringUtil; 063 import railo.runtime.chart.BarRenderer3DWrap; 064 import railo.runtime.chart.CategoryToolTipGeneratorImpl; 065 import railo.runtime.chart.EmptyURLTagFragmentGenerator; 066 import railo.runtime.chart.LabelFormatUtil; 067 import railo.runtime.chart.PieSectionLabelGeneratorImpl; 068 import railo.runtime.chart.PieSectionLegendLabelGeneratorImpl; 069 import railo.runtime.chart.PieToolTipGeneratorImpl; 070 import railo.runtime.chart.TickUnitsImpl; 071 import railo.runtime.chart.ToolTipTagFragmentGeneratorImpl; 072 import railo.runtime.converter.JavaConverter; 073 import railo.runtime.engine.ThreadLocalPageContext; 074 import railo.runtime.exp.ApplicationException; 075 import railo.runtime.exp.ExpressionException; 076 import railo.runtime.exp.PageException; 077 import railo.runtime.ext.tag.BodyTagImpl; 078 import railo.runtime.functions.dateTime.DateAdd; 079 import railo.runtime.img.Image; 080 import railo.runtime.op.Caster; 081 import railo.runtime.op.date.DateCaster; 082 import railo.runtime.type.dt.DateTime; 083 084 085 public final class Chart extends BodyTagImpl implements Serializable { 086 087 088 089 public static final Color COLOR_999999=new Color(0x99,0x99,0x99); 090 public static final Color COLOR_666666=new Color(0x66,0x66,0x66); 091 public static final Color COLOR_333333=new Color(0x33,0x33,0x33); 092 093 public static final String FONT_ARIAL = "arial"; 094 public static final String FONT_TIMES = "times"; 095 public static final String FONT_COURIER = "courier"; 096 public static final String FONT_ARIAL_UNICODE_MS = "arialunicodems"; 097 098 public static final int FORMAT_GIF = 0; 099 public static final int FORMAT_JPG = 1; 100 public static final int FORMAT_PNG = 2; 101 public static final int FORMAT_FLASH=3; 102 103 104 public static final int PIE_SLICE_STYLE_SOLID = 0; 105 public static final int PIE_SLICE_STYLE_SLICED = 1; 106 107 public static final int SERIES_PLACEMENT_DEFAULT = 0; 108 public static final int SERIES_PLACEMENT_CLUSTER = 1; 109 public static final int SERIES_PLACEMENT_STACKED = 2; 110 public static final int SERIES_PLACEMENT_PERCENT = 3; 111 112 public static final int TIP_STYLE_NONE = 0; 113 public static final int TIP_STYLE_FORMATS = 1; 114 public static final int TIP_STYLE_MOUSEDOWN = 2; 115 public static final int TIP_STYLE_MOUSEOVER = 3; 116 117 private static final int NONE = 0; 118 private static final int YES = 1; 119 private static final int NO = 2; 120 121 122 private static int chartIndex=0; 123 124 private Color backgroundcolor=Color.WHITE; 125 private Color databackgroundcolor=Color.WHITE; 126 private Color foregroundcolor=Color.BLACK; 127 private Color tipbgcolor=Color.WHITE; 128 private String xaxistitle=null; 129 private String yaxistitle=null; 130 131 132 private int chartheight=240; 133 private int chartwidth=320; 134 135 private String font=FONT_ARIAL; 136 private int fontstyle=0; 137 private int fontsize=11; 138 139 private int format=FORMAT_PNG; 140 private int gridlines=10; 141 142 private int labelFormat=LabelFormatUtil.LABEL_FORMAT_NUMBER; 143 private int markersize=-1; 144 145 private String name=null; 146 147 private int pieslicestyle=PIE_SLICE_STYLE_SLICED; 148 149 private double scalefrom=Double.NaN; 150 private double scaleto=Double.NaN; 151 private boolean legendMultiLine=false; 152 153 private int seriesplacement=SERIES_PLACEMENT_DEFAULT; 154 155 private boolean show3d=false; 156 private boolean showtooltip=true; 157 private boolean showborder=false; 158 private boolean showlegend=true; 159 private boolean showmarkers=true; 160 private int showxgridlines=NONE; 161 private boolean showygridlines=false; 162 private boolean sortxaxis=false; 163 164 private String style=null; 165 private String title=""; 166 167 private int tipstyle=TIP_STYLE_MOUSEOVER; 168 private List<ChartSeriesBean> _series=new ArrayList<ChartSeriesBean>(); 169 170 private String url; 171 private double xoffset=0.1; 172 private double yoffset=0.1; 173 private String xaxistype="category"; 174 private String yaxistype="category"; 175 private double smallest; 176 private double biggest; 177 private boolean showXLabel=true; 178 private String source; 179 180 public void release() { 181 _series.clear(); 182 183 url=null; 184 xoffset=0.1; 185 yoffset=0.1; 186 xaxistype="category"; 187 yaxistype="category"; 188 189 190 191 xaxistitle=""; 192 yaxistitle=""; 193 legendMultiLine=false; 194 // TODO super.release(); 195 backgroundcolor=Color.WHITE; 196 databackgroundcolor=Color.WHITE; 197 foregroundcolor=Color.BLACK; 198 tipbgcolor=Color.WHITE; 199 200 chartheight=240; 201 chartwidth=320; 202 203 font=FONT_ARIAL; 204 fontstyle=0; 205 fontsize=11; 206 207 format=FORMAT_PNG; 208 gridlines=10; 209 210 labelFormat=LabelFormatUtil.LABEL_FORMAT_NUMBER; 211 markersize=-1; 212 name=null; 213 214 pieslicestyle=PIE_SLICE_STYLE_SLICED; 215 216 scalefrom=Double.NaN; 217 scaleto=Double.NaN; 218 seriesplacement=SERIES_PLACEMENT_DEFAULT; 219 220 show3d=false; 221 showborder=false; 222 showlegend=true; 223 showmarkers=true; 224 showxgridlines=NONE; 225 showygridlines=false; 226 sortxaxis=false; 227 showXLabel=true; 228 showtooltip=true; 229 style=null; 230 title=""; 231 source=null; 232 tipstyle=TIP_STYLE_MOUSEOVER; 233 } 234 235 236 237 public void setShowxlabel(boolean showXLabel) { 238 this.showXLabel = showXLabel; 239 } 240 public void setSource(String source) { 241 this.source = source; 242 } 243 public void setShowtooltip(boolean showtooltip) { 244 this.showtooltip = showtooltip; 245 } 246 public void setBackgroundcolor(String strBackgroundColor) throws ExpressionException { 247 this.backgroundcolor = ColorCaster.toColor(strBackgroundColor); 248 } 249 250 public void setDatabackgroundcolor(String strDatabackgroundcolor) throws ExpressionException { 251 this.databackgroundcolor = ColorCaster.toColor(strDatabackgroundcolor); 252 } 253 254 public void setForegroundcolor(String strForegroundcolor) throws ExpressionException { 255 this.foregroundcolor = ColorCaster.toColor(strForegroundcolor); 256 } 257 258 public void setTipbgcolor(String strTipbgcolor) throws ExpressionException { 259 this.tipbgcolor = ColorCaster.toColor(strTipbgcolor); 260 } 261 262 public void setChartheight(double chartheight) { 263 this.chartheight = (int) chartheight; 264 } 265 266 public void setChartwidth(double chartwidth) { 267 this.chartwidth = (int) chartwidth; 268 } 269 270 public void setFont(String strFont) { 271 strFont=strFont.trim().toLowerCase(); 272 if("arial".equals(strFont))font=FONT_ARIAL; 273 else if("times".equals(strFont))font=FONT_TIMES; 274 else if("courier".equals(strFont))font=FONT_COURIER; 275 else if("arialunicodems".equals(strFont))font=FONT_ARIAL_UNICODE_MS; 276 else font=strFont; 277 //else throw new ExpressionException("invalid value ["+strFont+"] for attribute font, for this attribute only the following values are supported [arial,times,courier,arialunicodeMS]"); 278 } 279 280 public void setFontbold(boolean fontbold) { 281 if(fontbold)fontstyle+=Font.BOLD; 282 } 283 284 public void setFontitalic(boolean fontitalic) { 285 if(fontitalic)fontstyle+=Font.ITALIC; 286 } 287 288 public void setFontsize(double fontsize) { 289 this.fontsize = (int) fontsize; 290 } 291 292 public void setFormat(String strFormat) throws ExpressionException { 293 strFormat=strFormat.trim().toLowerCase(); 294 if("gif".equals(strFormat)) format=FORMAT_GIF; 295 else if("jpg".equals(strFormat)) format=FORMAT_JPG; 296 else if("jpeg".equals(strFormat)) format=FORMAT_JPG; 297 else if("jpe".equals(strFormat)) format=FORMAT_JPG; 298 else if("png".equals(strFormat)) format=FORMAT_PNG; 299 //else if("flash".equals(strFormat)) format=FORMAT_FLASH; 300 //else if("swf".equals(strFormat)) format=FORMAT_FLASH; 301 302 else throw new ExpressionException("invalid value ["+strFormat+"] for attribute format, for this attribute only the following values are supported [gif,jpg,png]"); 303 } 304 305 public void setGridlines(double gridlines) { 306 this.gridlines = (int) gridlines; 307 } 308 309 public void setLabelformat(String strLabelFormat) throws ExpressionException { 310 strLabelFormat=strLabelFormat.trim().toLowerCase(); 311 if("number".equals(strLabelFormat)) labelFormat=LabelFormatUtil.LABEL_FORMAT_NUMBER; 312 else if("numeric".equals(strLabelFormat)) labelFormat=LabelFormatUtil.LABEL_FORMAT_NUMBER; 313 else if("currency".equals(strLabelFormat)) labelFormat=LabelFormatUtil.LABEL_FORMAT_CURRENCY; 314 else if("date".equals(strLabelFormat)) labelFormat=LabelFormatUtil.LABEL_FORMAT_DATE; 315 else if("percent".equals(strLabelFormat)) labelFormat=LabelFormatUtil.LABEL_FORMAT_PERCENT; 316 //else if("integer".equals(strLabelFormat)) labelFormat=LabelFormatUtil.LABEL_FORMAT_INTEGER; 317 318 else throw new ExpressionException("invalid value ["+strLabelFormat+"] for attribute labelFormat, for this attribute only the following values are supported [date,percent,currency,number]"); 319 } 320 321 public void setMarkersize(double markersize) throws ExpressionException { 322 if(markersize<1) throw new ExpressionException("invalid value ["+markersize+"] for attribute markersize, value must be a positive integer greater than 0"); 323 this.markersize=(int) markersize; 324 } 325 326 public void setName(String name) { 327 this.name = name; 328 } 329 330 public void setPieslicestyle(String strPieslicestyle) throws ExpressionException { 331 strPieslicestyle=strPieslicestyle.trim().toLowerCase(); 332 if("sliced".equals(strPieslicestyle)) pieslicestyle=PIE_SLICE_STYLE_SLICED; 333 else if("slice".equals(strPieslicestyle)) pieslicestyle=PIE_SLICE_STYLE_SLICED; 334 else if("solid".equals(strPieslicestyle)) pieslicestyle=PIE_SLICE_STYLE_SOLID; 335 336 else throw new ExpressionException("invalid value ["+strPieslicestyle+"] for attribute pieSliceStyle, for this attribute only the following values are supported [sliced,solid]"); 337 } 338 339 public void setScaleto(double scaleto) { 340 //if(scaleto<0) throw new ExpressionException("invalid value ["+scaleto+"] for attribute scaleto, value must be a positive integer greater or equal than 0"); 341 this.scaleto = scaleto; 342 } 343 344 public void setScalefrom(double scaletrom) { 345 //if(scaletrom<0) throw new ExpressionException("invalid value ["+scaletrom+"] for attribute scaletrom, value must be a positive integer greater or equal than 0"); 346 this.scalefrom = scaletrom; 347 } 348 349 public void setSeriesplacement(String strSeriesplacement) throws ExpressionException { 350 strSeriesplacement=strSeriesplacement.trim().toLowerCase(); 351 if("default".equals(strSeriesplacement)) seriesplacement=SERIES_PLACEMENT_DEFAULT; 352 else if("cluster".equals(strSeriesplacement))seriesplacement=SERIES_PLACEMENT_CLUSTER; 353 else if("stacked".equals(strSeriesplacement))seriesplacement=SERIES_PLACEMENT_STACKED; 354 else if("percent".equals(strSeriesplacement))seriesplacement=SERIES_PLACEMENT_PERCENT; 355 356 else throw new ExpressionException("invalid value ["+strSeriesplacement+"] for attribute seriesplacement, for this attribute only the following values are supported [default,cluster,percent,stacked]"); 357 } 358 359 public void setShow3d(boolean show3d) { 360 this.show3d = show3d; 361 } 362 363 public void setShowborder(boolean showborder) { 364 this.showborder = showborder; 365 } 366 367 public void setShowlegend(boolean showlegend) { 368 this.showlegend = showlegend; 369 } 370 371 public void setShowmarkers(boolean showmarkers) { 372 this.showmarkers = showmarkers; 373 } 374 375 public void setShowxgridlines(boolean showxgridlines) { 376 this.showxgridlines = showxgridlines?YES:NO; 377 } 378 379 public void setShowygridlines(boolean showygridlines) { 380 this.showygridlines = showygridlines; 381 } 382 383 public void setSortxaxis(boolean sortxaxis) { 384 this.sortxaxis = sortxaxis; 385 } 386 387 public void setStyle(String style) { 388 this.style = style; 389 } 390 391 public void setTitle(String title) { 392 this.title = title; 393 } 394 395 public void setTipstyle(String strTipstyle) throws ExpressionException { 396 strTipstyle=strTipstyle.trim().toLowerCase(); 397 if("mousedown".equals(strTipstyle)) tipstyle=TIP_STYLE_MOUSEDOWN; 398 else if("mouseover".equals(strTipstyle))tipstyle=TIP_STYLE_MOUSEOVER; 399 else if("none".equals(strTipstyle)) tipstyle=TIP_STYLE_NONE; 400 else if("formats".equals(strTipstyle)) tipstyle=TIP_STYLE_FORMATS; 401 402 else throw new ExpressionException("invalid value ["+strTipstyle+"] for attribute Tipstyle, for this attribute only the following values are supported [mouseover,mousedown,one,formats]"); 403 } 404 405 406 407 /** 408 * @param xaxistitle the xaxistitle to set 409 */ 410 public void setXaxistitle(String xaxistitle) { 411 this.xaxistitle = xaxistitle; 412 } 413 414 /** 415 * @param yaxistitle the yaxistitle to set 416 */ 417 public void setYaxistitle(String yaxistitle) { 418 this.yaxistitle = yaxistitle; 419 } 420 421 public void addChartSeries(ChartSeriesBean series) { 422 _series.add(series); 423 } 424 425 426 public int doStartTag() { 427 return EVAL_BODY_INCLUDE; 428 } 429 430 @Override 431 public int doEndTag() throws PageException { 432 if(_series.size()==0) throw new ApplicationException("at least one cfchartseries tag required inside cfchart"); 433 //if(_series.size()>1) throw new ApplicationException("only one cfchartseries tag allowed inside cfchart"); 434 //doSingleSeries((ChartSeriesBean) _series.get(0)); 435 ChartSeriesBean first= _series.get(0); 436 437 try { 438 439 if(first.getType()==ChartSeriesBean.TYPE_BAR) 440 //throw new ApplicationException("type bar is not supported"); 441 chartBar(); 442 else if(first.getType()==ChartSeriesBean.TYPE_TIME) 443 chartTimeLine(); 444 else if(first.getType()==ChartSeriesBean.TYPE_AREA) 445 chartArea(); 446 else if(first.getType()==ChartSeriesBean.TYPE_CONE) 447 throw new ApplicationException("type cone is not supported"); 448 else if(first.getType()==ChartSeriesBean.TYPE_CURVE) 449 chartLine(); 450 //throw new ApplicationException("type curve is not supported"); 451 else if(first.getType()==ChartSeriesBean.TYPE_CYLINDER) 452 throw new ApplicationException("type cylinder is not supported"); 453 else if(first.getType()==ChartSeriesBean.TYPE_HORIZONTALBAR) 454 throw new ApplicationException("type horizontalbar is not supported"); 455 else if(first.getType()==ChartSeriesBean.TYPE_LINE) 456 chartLine(); 457 //throw new ApplicationException("type line is not supported"); 458 else if(first.getType()==ChartSeriesBean.TYPE_PIE) 459 chartPie(); 460 else if(first.getType()==ChartSeriesBean.TYPE_PYRAMID) 461 throw new ApplicationException("type pyramid is not supported"); 462 else if(first.getType()==ChartSeriesBean.TYPE_SCATTER) 463 throw new ApplicationException("type scatter is not supported"); 464 else if(first.getType()==ChartSeriesBean.TYPE_STEP) 465 throw new ApplicationException("type step is not supported"); 466 } 467 catch(IOException ioe) { 468 throw Caster.toPageException(ioe); 469 } 470 471 return EVAL_PAGE; 472 } 473 474 private void chartPie() throws PageException, IOException { 475 // do dataset 476 DefaultPieDataset dataset = new DefaultPieDataset(); 477 ChartSeriesBean csb = _series.get(0); 478 479 ChartDataBean cdb; 480 481 List datas=csb.getDatas(); 482 if(sortxaxis)Collections.sort(datas); 483 Iterator itt = datas.iterator(); 484 while(itt.hasNext()) { 485 cdb=(ChartDataBean) itt.next(); 486 dataset.setValue(cdb.getItemAsString(), cdb.getValue()); 487 } 488 489 490 JFreeChart chart = show3d? 491 ChartFactory.createPieChart3D (title, dataset, false, true, true): 492 ChartFactory.createPieChart (title, dataset, false, true, true); 493 494 Plot p = chart.getPlot(); 495 PiePlot pp = (PiePlot)p; 496 497 Font _font = getFont(); 498 pp.setLegendLabelGenerator(new PieSectionLegendLabelGeneratorImpl(_font,chartwidth)); 499 pp.setBaseSectionOutlinePaint(Color.GRAY); // rand der st゚cke 500 pp.setLegendItemShape(new Rectangle(7,7)); 501 pp.setLabelFont(new Font(font,0,11)); 502 pp.setLabelLinkPaint(COLOR_333333); 503 pp.setLabelLinkMargin(-0.05); 504 pp.setInteriorGap(0.123); 505 pp.setLabelGenerator(new PieSectionLabelGeneratorImpl(labelFormat)); 506 507 508 509 510 databackgroundcolor=backgroundcolor; 511 512 setBackground(chart,p); 513 setBorder(chart,p); 514 setLegend(chart,p,_font); 515 set3d(p); 516 setFont(chart, _font); 517 setTooltip(chart); 518 setScale(chart); 519 520 // Slice Type and colors 521 boolean doSclice=pieslicestyle==PIE_SLICE_STYLE_SLICED; 522 Color[] colors = csb.getColorlist(); 523 Iterator it = csb.getDatas().iterator(); 524 int count=0; 525 while(it.hasNext()) { 526 cdb=(ChartDataBean) it.next(); 527 if(doSclice)pp.setExplodePercent(cdb.getItemAsString(), 0.13); 528 529 if(count<colors.length){ 530 pp.setSectionPaint(cdb.getItemAsString(), colors[count]); 531 } 532 count++; 533 } 534 535 writeOut(chart); 536 } 537 538 539 private void set3d(Plot plot) { 540 if(!show3d) return; 541 542 plot.setForegroundAlpha(0.6f); 543 544 if(plot instanceof CategoryPlot) { 545 plot.setForegroundAlpha(0.8f); 546 CategoryPlot cp=(CategoryPlot) plot; 547 CategoryItemRenderer renderer = cp.getRenderer(); 548 if(renderer instanceof BarRenderer3D) { 549 BarRenderer3D br3d=(BarRenderer3D) renderer; 550 cp.setRenderer(new BarRenderer3DWrap(br3d,xoffset,yoffset)); 551 } 552 553 } 554 else if(plot instanceof PiePlot3D) { 555 PiePlot3D pp3d=(PiePlot3D) plot; 556 pp3d.setDepthFactor(0.10); 557 } 558 559 560 561 //CategoryItemRenderer renderer = plot.getRenderer(); 562 563 } 564 565 private void setFont(JFreeChart chart, Font font) { 566 // title 567 TextTitle title=chart.getTitle(); 568 if(title!=null) { 569 title.setFont(font); 570 title.setPaint(foregroundcolor); 571 chart.setTitle(title); 572 } 573 574 // axis fonts 575 Plot plot = chart.getPlot(); 576 if(plot instanceof CategoryPlot) { 577 CategoryPlot cp = (CategoryPlot)plot; 578 setAxis(cp.getRangeAxis(),font); 579 setAxis(cp.getDomainAxis(),font); 580 } 581 if(plot instanceof XYPlot) { 582 XYPlot cp = (XYPlot)plot; 583 setAxis(cp.getRangeAxis(),font); 584 setAxis(cp.getDomainAxis(),font); 585 } 586 587 588 } 589 590 591 private void setAxis(Axis axis, Font font) { 592 if(axis!=null) { 593 axis.setLabelFont(font); 594 axis.setLabelPaint(foregroundcolor); 595 596 axis.setTickLabelFont(font); 597 axis.setTickLabelPaint(foregroundcolor); 598 axis.setTickLabelsVisible(true); 599 } 600 } 601 602 603 604 private void setLegend(JFreeChart chart, Plot plot, Font font) { 605 if(!showlegend) return; 606 607 608 Color bg = backgroundcolor==null?databackgroundcolor:backgroundcolor; 609 if(font==null)font=getFont(); 610 611 612 613 LegendTitle legend = legendMultiLine? 614 new LegendTitle(plot,new ColumnArrangement(), new ColumnArrangement()): 615 new LegendTitle(plot); 616 legend.setBackgroundPaint(bg); 617 legend.setMargin(new RectangleInsets(1.0, 1.0, 1.0, 1.0)); 618 legend.setFrame(new LineBorder()); 619 legend.setPosition(RectangleEdge.BOTTOM); 620 legend.setHorizontalAlignment(HorizontalAlignment.LEFT); 621 622 legend.setWidth(chartwidth-20);// geht nicht 623 legend.setItemFont(font); 624 legend.setItemPaint(foregroundcolor); 625 626 //RectangleInsets labelPadding; 627 legend.setItemLabelPadding(new RectangleInsets(2,2,2,2)); 628 legend.setBorder(0,0,0,0); 629 legend.setLegendItemGraphicLocation(RectangleAnchor.TOP_LEFT); 630 legend.setLegendItemGraphicPadding(new RectangleInsets(8,10,0,0)); 631 chart.addLegend(legend); 632 633 } 634 635 636 637 private void setBorder(JFreeChart chart, Plot plot) { 638 chart.setBorderVisible(false); 639 chart.setBorderPaint(foregroundcolor); 640 plot.setOutlinePaint(foregroundcolor); 641 } 642 643 644 645 private void setBackground(JFreeChart chart, Plot plot) { 646 //Color bg = backgroundcolor==null?databackgroundcolor:backgroundcolor; 647 648 chart.setBackgroundPaint(backgroundcolor); 649 plot.setBackgroundPaint(databackgroundcolor); 650 chart.setBorderPaint(databackgroundcolor); 651 652 653 plot.setOutlineVisible(false); 654 655 // Pie 656 if(plot instanceof PiePlot) { 657 PiePlot pp=(PiePlot) plot; 658 pp.setLabelOutlinePaint(backgroundcolor); 659 pp.setLabelBackgroundPaint(backgroundcolor); 660 pp.setLabelShadowPaint(backgroundcolor); 661 pp.setShadowPaint(backgroundcolor); 662 } 663 // Bar 664 /*if(plot instanceof CategoryPlot) { 665 CategoryPlot cp=(CategoryPlot) plot; 666 667 }*/ 668 } 669 670 671 672 673 674 private Font getFont() { 675 return new Font(font,fontstyle,fontsize); 676 } 677 678 private void writeOut(JFreeChart jfc) throws PageException, IOException { 679 final ChartRenderingInfo info=new ChartRenderingInfo(); 680 681 // map name 682 chartIndex++; 683 if(chartIndex<0)chartIndex=0; 684 String mapName="chart_"+(chartIndex++); 685 686 // write out to variable 687 if(!StringUtil.isEmpty(name)){ 688 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 689 copy(baos, jfc,info); 690 pageContext.setVariable(name, baos.toByteArray()); 691 return; 692 } 693 694 // write out as link 695 String id=Md5.getDigestAsString(JavaConverter.serialize(this)); 696 Resource graph = pageContext.getConfig().getTempDirectory().getRealResource("graph"); 697 Resource res = graph.getRealResource(id); 698 if(!res.exists()) { 699 clean(graph); 700 copy(res.getOutputStream(),jfc,info); 701 } 702 703 String src="/railo-context/graph.cfm?img="+id+"&type="+formatToString(format); 704 if(!StringUtil.isEmpty(source)) { 705 pageContext.setVariable(source, src); 706 return; 707 } 708 try { 709 // Tooltip 710 if(showtooltip) { 711 ToolTipTagFragmentGenerator tttfg = new ToolTipTagFragmentGeneratorImpl(url); 712 URLTagFragmentGenerator utfg=new EmptyURLTagFragmentGenerator(); 713 714 String map=ImageMapUtilities.getImageMap(mapName, info,tttfg,utfg).trim(); 715 pageContext.write(map); 716 } 717 pageContext.write("<img border=\"0\" usemap=\"#"+mapName+"\" src=\""+src+"\">"); 718 } 719 catch (IOException e) { 720 throw Caster.toPageException(e); 721 } 722 } 723 724 private void copy(OutputStream os, JFreeChart jfc, ChartRenderingInfo info) throws ApplicationException, IOException, ExpressionException { 725 //OutputStream os = null; 726 try { 727 //os = res.getOutputStream(); 728 BufferedImage bi = jfc.createBufferedImage(chartwidth,chartheight,info); 729 Image img; 730 731 // add border 732 if(showborder) { 733 try { 734 img = new Image(bi); 735 img.addBorder(1,Color.BLACK,Image.BORDER_TYPE_CONSTANT); 736 bi=img.getBufferedImage(); 737 } 738 catch (PageException e) {} 739 } 740 if(format==FORMAT_PNG) ChartUtilities.writeBufferedImageAsPNG(os, bi); 741 else if(format==FORMAT_JPG) ChartUtilities.writeBufferedImageAsJPEG(os, bi); 742 else if(format==FORMAT_GIF) { 743 img = new railo.runtime.img.Image(bi); 744 img.writeOut(os, "gif",1,true); 745 746 //throw new ApplicationException("format gif not supported"); 747 } 748 else if(format==FORMAT_FLASH)throw new ApplicationException("format flash not supported"); 749 } 750 finally { 751 IOUtil.flushEL(os); 752 IOUtil.closeEL(os); 753 } 754 } 755 756 private String formatToString(int format) { 757 if(format==FORMAT_GIF) return "gif"; 758 if(format==FORMAT_JPG) return "jpeg"; 759 if(format==FORMAT_PNG) return "png"; 760 return "swf"; 761 } 762 763 private void clean(Resource graph) throws IOException { 764 if(!graph.exists())graph.createDirectory(true); 765 else if(graph.isDirectory() && ResourceUtil.getRealSize(graph)>(1024*1024)) { 766 767 Resource[] children = graph.listResources(); 768 long maxAge=System.currentTimeMillis()-(1000*60); 769 for(int i=0;i<children.length;i++) { 770 if(children[i].lastModified()<maxAge) 771 children[i].delete(); 772 } 773 } 774 } 775 776 private void chartBar() throws PageException, IOException { 777 // create the chart... 778 final JFreeChart chart = show3d? 779 ChartFactory.createBarChart3D(title,xaxistitle,yaxistitle,createDatasetCategory(),PlotOrientation.VERTICAL,false,true,false): 780 ChartFactory.createBarChart (title,xaxistitle,yaxistitle,createDatasetCategory(),PlotOrientation.VERTICAL,false,true,false); 781 Plot p = chart.getPlot(); 782 Font _font = getFont(); 783 // settings 784 785 786 setBackground(chart,p); 787 setBorder(chart,p); 788 set3d(p); 789 setFont(chart,_font); 790 setLabelFormat(chart); 791 setLegend(chart, p, _font); 792 setTooltip(chart); 793 setScale(chart); 794 setAxis(chart); 795 setColor(chart); 796 797 writeOut(chart); 798 } 799 800 801 802 803 804 private void chartLine() throws PageException, IOException { 805 // create the chart... 806 final JFreeChart chart = show3d? 807 ChartFactory.createLineChart3D(title,xaxistitle,yaxistitle,createDatasetCategory(),PlotOrientation.VERTICAL,false,true,false): 808 ChartFactory.createLineChart(title,xaxistitle,yaxistitle,createDatasetCategory(),PlotOrientation.VERTICAL,false,true,false); 809 Plot p = chart.getPlot(); 810 Font _font = getFont(); 811 812 // settings 813 setMarker(chart,p); 814 setBackground(chart,p); 815 setBorder(chart,p); 816 set3d(p); 817 setFont(chart,_font); 818 setLabelFormat(chart); 819 setLegend(chart, p, _font); 820 setTooltip(chart); 821 setScale(chart); 822 setAxis(chart); 823 setColor(chart); 824 825 writeOut(chart); 826 } 827 828 private void chartArea() throws PageException, IOException { 829 // create the chart... 830 final JFreeChart chart = ChartFactory.createAreaChart(title,xaxistitle,yaxistitle,createDatasetCategory(),PlotOrientation.VERTICAL,false,true,false); 831 Plot p = chart.getPlot(); 832 Font _font = getFont(); 833 834 // settings 835 setMarker(chart,p); 836 setBackground(chart,p); 837 setBorder(chart,p); 838 set3d(p); 839 setFont(chart,_font); 840 setLabelFormat(chart); 841 setLegend(chart, p, _font); 842 setTooltip(chart); 843 setScale(chart); 844 setAxis(chart); 845 setColor(chart); 846 847 writeOut(chart); 848 } 849 850 private void chartTimeLine() throws PageException, IOException { 851 // create the chart... 852 final JFreeChart chart = ChartFactory.createTimeSeriesChart(title,xaxistitle,yaxistitle,createTimeSeriesCollection(),false,true,false); 853 Plot p = chart.getPlot(); 854 Font _font = getFont(); 855 856 // settings 857 setMarker(chart,p); 858 setBackground(chart,p); 859 setBorder(chart,p); 860 set3d(p); 861 setFont(chart,_font); 862 setLabelFormat(chart); 863 setLegend(chart, p, _font); 864 setTooltip(chart); 865 setScale(chart); 866 setAxis(chart); 867 setColor(chart); 868 869 writeOut(chart); 870 } 871 872 873 874 private void setMarker(JFreeChart chart, Plot p) { 875 if(!showmarkers) return; 876 877 if(markersize<1 || markersize>100) markersize=4; 878 879 880 881 if(p instanceof XYPlot) { 882 XYPlot xyp=(XYPlot) p; 883 XYItemRenderer r = xyp.getRenderer(); 884 if (r instanceof XYLineAndShapeRenderer) { 885 XYLineAndShapeRenderer xyr = (XYLineAndShapeRenderer) r; 886 xyr.setBaseShapesVisible(true); 887 xyr.setBaseShapesFilled(true); 888 889 int seriesCount=_series.size(); 890 for(int i=0;i<seriesCount;i++){ 891 xyr.setSeriesShapesVisible(i, true); 892 xyr.setSeriesItemLabelsVisible(i, true); 893 xyr.setSeriesShape(i, ShapeUtilities.createDiamond(markersize)); 894 xyr.setUseFillPaint(true); 895 xyr.setBaseFillPaint(databackgroundcolor); 896 } 897 } 898 } 899 else if(p instanceof CategoryPlot) { 900 CategoryPlot cp=(CategoryPlot) p; 901 CategoryItemRenderer r = cp.getRenderer(); 902 if (r instanceof LineAndShapeRenderer) { 903 LineAndShapeRenderer lsr = (LineAndShapeRenderer)r; 904 905 int seriesCount=_series.size(); 906 for(int i=0;i<seriesCount;i++){ 907 lsr.setSeriesShapesVisible(i, true); 908 lsr.setSeriesItemLabelsVisible(i, true); 909 lsr.setSeriesShape(i, ShapeUtilities.createDiamond(markersize)); 910 lsr.setUseFillPaint(true); 911 lsr.setBaseFillPaint(databackgroundcolor); 912 } 913 } 914 } 915 } 916 917 918 919 private void setAxis(JFreeChart chart) { 920 Plot plot = chart.getPlot(); 921 if(plot instanceof CategoryPlot) { 922 CategoryPlot cp=(CategoryPlot)plot; 923 924 // Y 925 cp.setDomainGridlinesVisible(showygridlines); 926 if(showygridlines) cp.setDomainGridlinePaint(foregroundcolor); 927 928 cp.setRangeGridlinesVisible(showxgridlines!=NO); 929 if(showxgridlines==NONE)cp.setRangeGridlinePaint(Color.GRAY); 930 else if(showxgridlines==YES)cp.setRangeGridlinePaint(foregroundcolor); 931 } 932 else if(plot instanceof XYPlot) { 933 XYPlot cp=(XYPlot)plot; 934 935 // Y 936 cp.setDomainGridlinesVisible(showygridlines); 937 if(showygridlines) cp.setDomainGridlinePaint(foregroundcolor); 938 939 cp.setRangeGridlinesVisible(showxgridlines!=NO); 940 if(showxgridlines==NONE)cp.setRangeGridlinePaint(Color.GRAY); 941 else if(showxgridlines==YES)cp.setRangeGridlinePaint(foregroundcolor); 942 } 943 } 944 945 946 947 private void setTooltip(JFreeChart chart) { 948 Plot plot = chart.getPlot(); 949 if(plot instanceof PiePlot) { 950 PiePlot pp = (PiePlot)plot; 951 952 pp.setToolTipGenerator(new PieToolTipGeneratorImpl(labelFormat)); 953 954 } 955 else if(plot instanceof CategoryPlot) { 956 CategoryPlot cp=(CategoryPlot) plot; 957 CategoryItemRenderer renderer = cp.getRenderer(); 958 renderer.setBaseToolTipGenerator(new CategoryToolTipGeneratorImpl(labelFormat)); 959 } 960 /*else if(plot instanceof XYPlot) { 961 XYPlot cp=(XYPlot) plot; 962 XYItemRenderer renderer = cp.getRenderer(); 963 renderer.setBaseToolTipGenerator(new XYToolTipGeneratorImpl(labelFormat)); 964 }*/ 965 966 } 967 968 969 970 private void setScale(JFreeChart chart) { 971 Plot plot = chart.getPlot(); 972 if(plot instanceof CategoryPlot) { 973 CategoryPlot cp=(CategoryPlot) plot; 974 ValueAxis rangeAxis = cp.getRangeAxis(); 975 Range r=rangeAxis.getRange(); 976 double lower=r.getLowerBound(); 977 double upper=r.getUpperBound(); 978 979 if(labelFormat==LabelFormatUtil.LABEL_FORMAT_DATE && rangeAxis.getRange().getLowerBound()==0) { 980 lower = smallest; 981 upper=biggest; 982 try { 983 DateTime d = Caster.toDate(Caster.toDouble(lower),true,null,null); 984 lower = DateAdd.call(pageContext,"yyyy", -1, d).castToDoubleValue(lower); 985 } 986 catch (PageException e) {} 987 } 988 if(!Double.isNaN(scalefrom))lower=scalefrom; 989 if(!Double.isNaN(scaleto))upper=scaleto; 990 rangeAxis.setRange(new Range(lower,upper),true,true); 991 } 992 else if(plot instanceof XYPlot) { 993 XYPlot cp=(XYPlot) plot; 994 ValueAxis rangeAxis = cp.getRangeAxis(); 995 Range r=rangeAxis.getRange(); 996 double lower=r.getLowerBound(); 997 double upper=r.getUpperBound(); 998 999 if(labelFormat==LabelFormatUtil.LABEL_FORMAT_DATE && rangeAxis.getRange().getLowerBound()==0) { 1000 lower = smallest; 1001 upper=biggest; 1002 try { 1003 DateTime d = Caster.toDate(Caster.toDouble(lower),true,null,null); 1004 lower = DateAdd.call(pageContext,"yyyy", -1, d).castToDoubleValue(lower); 1005 } 1006 catch (PageException e) {} 1007 } 1008 if(!Double.isNaN(scalefrom))lower=scalefrom; 1009 if(!Double.isNaN(scaleto))upper=scaleto; 1010 rangeAxis.setRange(new Range(lower,upper),true,true); 1011 } 1012 } 1013 1014 private void setLabelFormat(JFreeChart chart) { 1015 Plot plot = chart.getPlot(); 1016 if(plot instanceof CategoryPlot) { 1017 CategoryPlot cp=(CategoryPlot) plot; 1018 ValueAxis rangeAxis = cp.getRangeAxis(); 1019 rangeAxis.setAutoTickUnitSelection(true); 1020 rangeAxis.setStandardTickUnits(new TickUnitsImpl(rangeAxis.getStandardTickUnits(),labelFormat)); 1021 CategoryItemRenderer r = cp.getRenderer(); 1022 r.setBaseItemLabelsVisible(false); 1023 1024 CategoryAxis da = cp.getDomainAxis(); 1025 if(!showXLabel)da.setTickLabelsVisible(false); 1026 //da.setVisible(false); 1027 } 1028 if(plot instanceof XYPlot) { 1029 XYPlot cp=(XYPlot) plot; 1030 ValueAxis rangeAxis = cp.getRangeAxis(); 1031 rangeAxis.setAutoTickUnitSelection(true); 1032 rangeAxis.setStandardTickUnits(new TickUnitsImpl(rangeAxis.getStandardTickUnits(),labelFormat)); 1033 XYItemRenderer r = cp.getRenderer(); 1034 r.setBaseItemLabelsVisible(false); 1035 1036 ValueAxis da = cp.getDomainAxis(); 1037 if(!showXLabel)da.setTickLabelsVisible(false); 1038 //da.setVisible(false); 1039 } 1040 } 1041 1042 1043 1044 // set individual colors for series 1045 private void setColor(JFreeChart chart) { 1046 Plot p = chart.getPlot(); 1047 if(p instanceof CategoryPlot) { 1048 CategoryPlot cp=(CategoryPlot) p; 1049 1050 CategoryItemRenderer renderer = cp.getRenderer(); 1051 1052 1053 1054 Iterator<ChartSeriesBean> cs = _series.iterator(); 1055 //int seriesCount=_series.size(); 1056 ChartSeriesBean csb; 1057 GradientPaint gp; 1058 Color c=null; 1059 Color[] ac; 1060 1061 int index=0; 1062 while(cs.hasNext()) { 1063 csb= cs.next(); 1064 // more than 1 series 1065 //if(seriesCount>1) { 1066 c=csb.getSeriesColor(); 1067 if(c==null) { 1068 ac=csb.getColorlist(); 1069 if(ac!=null && ac.length>0)c=ac[0]; 1070 } 1071 1072 //} 1073 if(c==null) continue; 1074 gp = new GradientPaint(0.0f, 0.0f, c, 0.0f, 0.0f,c); 1075 renderer.setSeriesPaint(index++, gp); 1076 } 1077 } 1078 else if(p instanceof XYPlot) { 1079 XYPlot cp=(XYPlot) p; 1080 1081 XYItemRenderer renderer = cp.getRenderer(); 1082 1083 1084 1085 Iterator<ChartSeriesBean> cs = _series.iterator(); 1086 //int seriesCount=_series.size(); 1087 ChartSeriesBean csb; 1088 GradientPaint gp; 1089 Color c=null; 1090 Color[] ac; 1091 1092 int index=0; 1093 while(cs.hasNext()) { 1094 csb= cs.next(); 1095 // more than 1 series 1096 //if(seriesCount>1) { 1097 c=csb.getSeriesColor(); 1098 if(c==null) { 1099 ac=csb.getColorlist(); 1100 if(ac!=null && ac.length>0)c=ac[0]; 1101 } 1102 1103 //} 1104 if(c==null) continue; 1105 gp = new GradientPaint(0.0f, 0.0f, c, 0.0f, 0.0f,c); 1106 renderer.setSeriesPaint(index++, gp); 1107 } 1108 } 1109 } 1110 1111 1112 1113 private DefaultPieDataset createDatasetPie() { 1114 DefaultPieDataset dataset = new DefaultPieDataset(); 1115 ChartSeriesBean csb = _series.get(0); 1116 1117 ChartDataBean cdb; 1118 // write data set 1119 Iterator itt = csb.getDatas().iterator(); 1120 while(itt.hasNext()) { 1121 cdb=(ChartDataBean) itt.next(); 1122 dataset.setValue(cdb.getItemAsString(), cdb.getValue()); 1123 } 1124 return dataset; 1125 } 1126 1127 1128 1129 1130 1131 private CategoryDataset createDatasetCategory() { 1132 final DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 1133 Iterator<ChartSeriesBean> it = _series.iterator(); 1134 //int seriesCount=_series.size(); 1135 Iterator itt; 1136 List datas; 1137 ChartSeriesBean csb; 1138 ChartDataBean cdb; 1139 int count=0; 1140 smallest=Double.MAX_VALUE; 1141 biggest = Double.MIN_VALUE; 1142 String label; 1143 boolean hasLabels=false; 1144 while(it.hasNext()) { 1145 count++; 1146 csb= it.next(); 1147 label=csb.getSeriesLabel(); 1148 if(StringUtil.isEmpty(label))label=""+count; 1149 else hasLabels=true; 1150 datas = csb.getDatas(); 1151 if(sortxaxis)Collections.sort(datas); 1152 itt=datas.iterator(); 1153 while(itt.hasNext()) { 1154 cdb=(ChartDataBean) itt.next(); 1155 if(smallest>cdb.getValue())smallest=cdb.getValue(); 1156 if(biggest<cdb.getValue())biggest=cdb.getValue(); 1157 //if(seriesCount>1) 1158 1159 dataset.addValue(cdb.getValue(), label,cdb.getItemAsString()); 1160 1161 //else dataset.addValue(cdb.getValue(), cdb.getItem(),""); 1162 1163 1164 } 1165 } 1166 if(!hasLabels)showlegend=false; 1167 return dataset; 1168 } 1169 private XYDataset createTimeSeriesCollection() { 1170 TimeZone tz = ThreadLocalPageContext.getTimeZone(); 1171 final TimeSeriesCollection coll=new TimeSeriesCollection(tz); 1172 1173 //final DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 1174 Iterator<ChartSeriesBean> it = _series.iterator(); 1175 //int seriesCount=_series.size(); 1176 Iterator itt; 1177 List datas; 1178 ChartSeriesBean csb; 1179 ChartDataBean cdb; 1180 int count=0; 1181 smallest=Double.MAX_VALUE; 1182 biggest = Double.MIN_VALUE; 1183 String label; 1184 boolean hasLabels=false; 1185 while(it.hasNext()) { 1186 count++; 1187 csb=it.next(); 1188 label=csb.getSeriesLabel(); 1189 if(StringUtil.isEmpty(label))label=""+count; 1190 else hasLabels=true; 1191 datas = csb.getDatas(); 1192 if(sortxaxis)Collections.sort(datas); 1193 itt=datas.iterator(); 1194 TimeSeries ts=new TimeSeries(label,Second.class); 1195 while(itt.hasNext()) { 1196 cdb=(ChartDataBean) itt.next(); 1197 if(smallest>cdb.getValue())smallest=cdb.getValue(); 1198 if(biggest<cdb.getValue())biggest=cdb.getValue(); 1199 //if(seriesCount>1) 1200 ts.addOrUpdate(new Second(DateCaster.toDateSimple(cdb.getItem(),false,false, tz,null)), cdb.getValue()); 1201 1202 //else dataset.addValue(cdb.getValue(), cdb.getItem(),""); 1203 1204 1205 } 1206 coll.addSeries(ts); 1207 } 1208 if(!hasLabels)showlegend=false; 1209 return coll; 1210 } 1211 1212 /** 1213 * @param url the url to set 1214 */ 1215 public void setUrl(String url) { 1216 this.url = url; 1217 } 1218 1219 /** 1220 * @param xoffset the xoffset to set 1221 */ 1222 public void setXoffset(double xoffset) { 1223 this.xoffset = xoffset; 1224 } 1225 1226 /** 1227 * @param yoffset the yoffset to set 1228 */ 1229 public void setYoffset(double yoffset) { 1230 this.yoffset = yoffset; 1231 } 1232 1233 /** 1234 * @param yaxistype the yaxistype to set 1235 */ 1236 public void setYaxistype(String yaxistype) { 1237 this.yaxistype = yaxistype; 1238 } 1239 /** 1240 * @param yaxistype the yaxistype to set 1241 */ 1242 public void setXaxistype(String xaxistype) { 1243 this.xaxistype = xaxistype; 1244 } 1245 1246 }