1 /* ============================================================
2  * JRobin : Pure java implementation of RRDTool's functionality
3  * ============================================================
4  *
5  * Project Info:  http://www.jrobin.org
6  * Project Lead:  Sasa Markovic (saxon@jrobin.org)
7  *
8  * Developers:    Sasa Markovic (saxon@jrobin.org)
9  *
10  *
11  * (C) Copyright 2003-2005, by Sasa Markovic.
12  *
13  * This library is free software; you can redistribute it and/or modify it under the terms
14  * of the GNU Lesser General Public License as published by the Free Software Foundation;
15  * either version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19  * See the GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License along with this
22  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */

25 package org.jrobin.graph;
26
27 import java.awt.Font;
28 import java.awt.Paint;
29 import java.io.File;
30 import java.io.InputStream;
31 import java.net.MalformedURLException;
32 import java.net.URL;
33 import java.util.ArrayList;
34 import java.util.List;
35
36 import org.jrobin.core.RrdException;
37 import org.jrobin.core.Util;
38 import org.jrobin.data.Plottable;
39
40 /**
41  * Class which should be used to define new JRobin graph. Once constructed and populated with data
42  * object of this class should be passed to the constructor of the {@link RrdGraph} class which
43  * will actually create the graph.
44  * <p/>
45  * The text printed below the actual graph can be formated by appending
46  * special escaped characters at the end of a text. When ever such a
47  * character occurs, all pending text is pushed onto the graph according to
48  * the character specified.
49  * <p/>
50  * Valid markers are: \j for justified, \l for left aligned, \r for right
51  * aligned and \c for centered.
52  * <p/>
53  * Normally there are two space characters inserted between every two
54  * items printed into the graph. The space following a string can be
55  * suppressed by putting a \g at the end of the string. The \g also squashes
56  * any space inside the string if it is at the very end of the string.
57  * This can be used in connection with %s to suppress empty unit strings.
58  * <p/>
59  * A special case is COMMENT:\s this inserts some additional vertical
60  * space before placing the next row of legends.
61  * <p/>
62  * When text has to be formated without special instructions from your
63  * side, RRDTool will automatically justify the text as soon as one string
64  * goes over the right edge. If you want to prevent the justification
65  * without forcing a newline, you can use the special tag \J at the end of
66  * the string to disable the auto justification.
67  */

68 public class RrdGraphDef implements RrdGraphConstants {
69     boolean poolUsed = false// ok
70     boolean antiAliasing = false// ok
71     String filename = RrdGraphConstants.IN_MEMORY_IMAGE; // ok
72     long startTime, endTime; // ok
73     TimeAxisSetting timeAxisSetting = null// ok
74     ValueAxisSetting valueAxisSetting = null// ok
75     boolean altYGrid = false// ok
76     boolean noMinorGrid = false// ok
77     boolean altYMrtg = false// ok
78     boolean altAutoscale = false// ok
79     boolean altAutoscaleMax = false// ok
80     int unitsExponent = Integer.MAX_VALUE; // ok
81     int unitsLength = DEFAULT_UNITS_LENGTH; // ok
82     String verticalLabel = null// ok
83     int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT; // ok
84     boolean interlaced = false// ok
85     String imageInfo = null// ok
86     String imageFormat = DEFAULT_IMAGE_FORMAT; // ok
87     float imageQuality = DEFAULT_IMAGE_QUALITY; // ok
88     String backgroundImage = null// ok
89     String overlayImage = null// ok
90     String unit = null// ok
91     String signature = "Created with JRobin"// ok
92     boolean lazy = false// ok
93     double minValue = Double.NaN; // ok
94     double maxValue = Double.NaN; // ok
95     boolean rigid = false// ok
96     double base = DEFAULT_BASE;  // ok
97     boolean logarithmic = false// ok
98     Paint[] colors = new Paint[] {
99             // ok
100             DEFAULT_CANVAS_COLOR,
101             DEFAULT_BACK_COLOR,
102             DEFAULT_SHADEA_COLOR,
103             DEFAULT_SHADEB_COLOR,
104             DEFAULT_GRID_COLOR,
105             DEFAULT_MGRID_COLOR,
106             DEFAULT_FONT_COLOR,
107             DEFAULT_FRAME_COLOR,
108             DEFAULT_ARROW_COLOR
109     };
110     boolean noLegend = false// ok
111     boolean onlyGraph = false// ok
112     boolean forceRulesLegend = false// ok
113     String title = null// ok
114     long step = 0; // ok
115     protected Font smallFont;
116     protected Font largeFont;
117     boolean drawXGrid = true// ok
118     boolean drawYGrid = true// ok
119     int firstDayOfWeek = FIRST_DAY_OF_WEEK; // ok
120     boolean showSignature = true;
121     File fontDir = null;
122     
123     List<Source> sources = new ArrayList<Source>();
124     List<CommentText> comments = new ArrayList<CommentText>();
125     List<PlotElement> plotElements = new ArrayList<PlotElement>();
126
127     /**
128      * Creates RrdGraphDef object and sets default time span (default ending time is 'now',
129      * default starting time is 'end-1day'.
130      */

131     public RrdGraphDef() {
132         try {
133             setTimeSpan(Util.getTimestamps(DEFAULT_START, DEFAULT_END));
134         } catch (RrdException e) {
135             throw new RuntimeException(e);
136         }
137         
138         String fontdirProperty = System.getProperty("jrobin.fontdir");
139         if (fontdirProperty != null && fontdirProperty.length() != 0) {
140             fontDir = new File(fontdirProperty);
141         }
142         
143         // smallFont = this.getFontFromResourceName(RrdGraphConstants.DEFAULT_MONOSPACE_FONT_FILE).deriveFont(10);
144         // largeFont = this.getFontFromResourceName(RrdGraphConstants.DEFAULT_MONOSPACE_FONT_FILE).deriveFont(12);
145         smallFont = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 10);
146         largeFont = new Font(DEFAULT_FONT_NAME, Font.BOLD, 12);
147     }
148
149     protected Font getFontFromResourceName(String name) {
150         Font font = null;
151         Exception exception = null;
152         URL file = null;
153         
154         if (fontDir != null) {
155             try {
156                 file = new URL("file://" + new File(fontDir, name).getAbsolutePath());
157             } catch (MalformedURLException e) {
158                 // fall through to the jar
159                 exception = e;
160             }
161         }
162         if (file == null) {
163             file = this.getClass().getResource(name);
164         }
165         
166         if (file != null) {
167             // System.err.println("Found a font URL: " + file.toExternalForm());
168             try {
169                 InputStream fontStream = file.openStream();
170                 font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
171                 fontStream.close();
172             } catch (Exception e) {
173                 exception = e;
174             }
175         } else {
176             // we can't find our fonts, fall back to the system font
177             System.err.println("An error occurred loading the font '" + name + "'.  Falling back to the default.");
178             if (exception != null) {
179                 System.err.println(exception.getLocalizedMessage());
180             }
181             font = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 10);
182         }
183
184         if (font == null) {
185             font = new Font(null, Font.PLAIN, 10);
186         }
187         return font;
188     }
189     
190     /**
191      * Sets the signature string that runs along the right-side of the graph.
192      * Defaults to "Created with JRobin".
193      * 
194      * @param signature the string to print
195      */

196     public void setSignature(String signature) {
197         this.signature = signature;
198     }
199
200     /**
201      * Gets the signature string that runs along the right-side of the graph.
202      * @return the signature string
203      */

204     public String getSignature() {
205         return this.signature;
206     }
207
208     /**
209      * Sets the time when the graph should begin. Time in seconds since epoch
210      * (1970-01-01) is required. Negative numbers are relative to the current time.
211      *
212      * @param time Starting time for the graph in seconds since epoch
213      */

214     public void setStartTime(long time) {
215         this.startTime = time;
216         if (time <= 0) {
217             this.startTime += Util.getTime();
218         }
219     }
220
221     /**
222      * Sets the time when the graph should end. Time in seconds since epoch
223      * (1970-01-01) is required. Negative numbers are relative to the current time.
224      *
225      * @param time Ending time for the graph in seconds since epoch
226      */

227     public void setEndTime(long time) {
228         this.endTime = time;
229         if (time <= 0) {
230             this.endTime += Util.getTime();
231         }
232     }
233
234     /**
235      * Sets starting and ending time for the for the graph. Timestamps in seconds since epoch are
236      * required. Negative numbers are relative to the current time.
237      *
238      * @param startTime Starting time in seconds since epoch
239      * @param endTime   Ending time in seconds since epoch
240      */

241     public void setTimeSpan(long startTime, long endTime) {
242         setStartTime(startTime);
243         setEndTime(endTime);
244     }
245
246     /**
247      * Sets starting and ending time for the for the graph. Timestamps in seconds since epoch are
248      * required.
249      *
250      * @param timestamps Array of timestamps. The first array item will be chosen for the starting
251      *                   timestamp. The last array item will be chosen for the ending timestamp.
252      */

253     public void setTimeSpan(long[] timestamps) {
254         setTimeSpan(timestamps[0], timestamps[timestamps.length - 1]);
255     }
256
257     /**
258      * Sets RrdDbPool usage policy (defaults to true). If set to true,
259      * {@link org.jrobin.core.RrdDbPool RrdDbPool} will be used to
260      * access individual RRD files. If set to false, RRD files will be accessed directly.
261      *
262      * @param poolUsed trueif RrdDbPool class should be used. False otherwise.
263      */

264     public void setPoolUsed(boolean poolUsed) {
265         this.poolUsed = poolUsed;
266     }
267
268     /**
269      * Sets the name of the graph to generate. Since JRobin outputs GIFs, PNGs,
270      * and JPEGs it's recommended that the filename end in either .gif,
271      * .png or .jpg. JRobin does not enforce this, however. If the filename is
272      * set to '-' the image will be created only in memory (no file will be created).
273      * PNG and GIF formats are recommended but JPEGs should be avoided.
274      *
275      * @param filename Path to the image file
276      */

277     public void setFilename(String filename) {
278         this.filename = filename;
279     }
280
281     /**
282      * Configures x-axis grid and labels. The x-axis label is quite complex to configure.
283      * So if you don't have very special needs, you can rely on the autoconfiguration to
284      * get this right.
285      * <p/>
286      * Otherwise, you have to configure three elements making up the x-axis labels
287      * and grid. The base grid, the major grid and the labels.
288      * The configuration is based on the idea that you first specify a well
289      * known amount of time and then say how many times
290      * it has to pass between each minor/major grid line or label. For the label
291      * you have to define two additional items: The precision of the label
292      * in seconds and the format used to generate the text
293      * of the label.
294      * <p/>
295      * For example, if you wanted a graph with a base grid every 10 minutes and a major
296      * one every hour, with labels every hour you would use the following
297      * x-axis definition.
298      * <p/>
299      * <pre>
300      * setTimeAxis(RrdGraphConstants.MINUTE, 10,
301      *             RrdGraphConstants.HOUR, 1,
302      *             RrdGraphConstants.HOUR, 1,
303      *             0, "%H:%M")
304      * </pre>
305      * <p/>
306      * The precision in this example is 0 because the %X format is exact.
307      * If the label was the name of the day, we would have had a precision
308      * of 24 hours, because when you say something like 'Monday' you mean
309      * the whole day and not Monday morning 00:00. Thus the label should
310      * be positioned at noon. By defining a precision of 24 hours or
311      * rather 86400 seconds, you make sure that this happens.
312      *
313      * @param minorUnit        Minor grid unit. Minor grid, major grid and label units
314      *                         can be one of the following constants defined in
315      *                         {@link RrdGraphConstants}: {@link RrdGraphConstants#SECOND SECOND},
316      *                         {@link RrdGraphConstants#MINUTE MINUTE}, {@link RrdGraphConstants#HOUR HOUR},
317      *                         {@link RrdGraphConstants#DAY DAY}, {@link RrdGraphConstants#WEEK WEEK},
318      *                         {@link RrdGraphConstants#MONTH MONTH}, {@link RrdGraphConstants#YEAR YEAR}.
319      * @param minorUnitCount   Number of minor grid units between minor grid lines.
320      * @param majorUnit        Major grid unit.
321      * @param majorUnitCount   Number of major grid units between major grid lines.
322      * @param labelUnit        Label unit.
323      * @param labelUnitCount   Number of label units between labels.
324      * @param labelSpan        Label precision
325      * @param simpleDateFormat Date format (SimpleDateFormat pattern of strftime-like pattern)
326      */

327     public void setTimeAxis(int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
328                             int labelUnit, int labelUnitCount, int labelSpan, String simpleDateFormat) {
329         timeAxisSetting = new TimeAxisSetting(minorUnit, minorUnitCount, majorUnit, majorUnitCount,
330                 labelUnit, labelUnitCount, labelSpan, simpleDateFormat);
331     }
332
333     /**
334      * Sets vertical axis grid and labels. Makes vertical grid lines appear
335      * at gridStep interval. Every labelFactor*gridStep, a major grid line is printed,
336      * along with label showing the value of the grid line.
337      *
338      * @param gridStep    Minor grid step
339      * @param labelFactor Specifies how many minor minor grid steps will appear between labels
340      *                    (major grid lines)
341      */

342     public void setValueAxis(double gridStep, int labelFactor) {
343         valueAxisSetting = new ValueAxisSetting(gridStep, labelFactor);
344     }
345
346     /**
347      * Places Y grid dynamically based on graph Y range. Algorithm ensures
348      * that you always have grid, that there are enough but not too many
349      * grid lines and the grid is metric. That is grid lines are placed
350      * every 1, 2, 5 or 10 units.
351      *
352      * @param altYGrid trueif Y grid should be calculated dynamically (defaults to false)
353      */

354     public void setAltYGrid(boolean altYGrid) {
355         this.altYGrid = altYGrid;
356     }
357
358     /**
359      * Use this method to turn off minor grid lines (printed by default)
360      *
361      * @param noMinorGrid true, to turn off, false to turn on (default)
362      */

363     public void setNoMinorGrid(boolean noMinorGrid) {
364         this.noMinorGrid = noMinorGrid;
365     }
366
367     /**
368      * Use this method to request MRTG-like graph (false by default)
369      *
370      * @param altYMrtg true, to create MRTG-like graph, false otherwise (default)
371      */

372     public void setAltYMrtg(boolean altYMrtg) {
373         this.altYMrtg = altYMrtg;
374     }
375
376     /**
377      * Computes Y range based on function absolute minimum and maximum
378      * values. Default algorithm uses predefined set of ranges.  This is
379      * good in many cases but it fails miserably when you need to graph
380      * something like 260 + 0.001 * sin(x). Default algorithm will use Y
381      * range from 250 to 300 and on the graph you will see almost straight
382      * line. With --alt-autoscale Y range will be from slightly less the
383      * 260 - 0.001 to slightly more then 260 + 0.001 and periodic behavior
384      * will be seen.
385      *
386      * @param altAutoscale true to request alternative autoscaling, false otherwise
387      *                     (default).
388      */

389     public void setAltAutoscale(boolean altAutoscale) {
390         this.altAutoscale = altAutoscale;
391     }
392
393     /**
394      * Computes Y range based on function absolute minimum and maximum
395      * values. Where setAltAutoscale(true) will modify both the absolute maximum AND
396      * minimum values, this option will only affect the maximum value. The
397      * minimum value, if not defined elsewhere, will be 0. This
398      * option can be useful when graphing router traffic when the WAN line
399      * uses compression, and thus the throughput may be higher than the
400      * WAN line speed.
401      *
402      * @param altAutoscaleMax true to request alternative autoscaling, false
403      *                        otherwise (default)
404      */

405     public void setAltAutoscaleMax(boolean altAutoscaleMax) {
406         this.altAutoscaleMax = altAutoscaleMax;
407     }
408
409     /**
410      * Sets the 10**unitsExponent scaling of the y-axis values. Normally
411      * values will be scaled to the appropriate units (k, M, etc.). However
412      * you may wish to display units always in k (Kilo, 10e3) even if
413      * the data is in the M (Mega, 10e6) range for instance.  Value should
414      * be an integer which is a multiple of 3 between -18 and 18, inclu-
415      * sive. It is the exponent on the units you which to use.  For example,
416      * use 3 to display the y-axis values in k (Kilo, 10e3, thou-
417      * sands), use -6 to display the y-axis values in u (Micro, 10e-6,
418      * millionths). Use a value of 0 to prevent any scaling of the y-axis
419      * values.
420      *
421      * @param unitsExponent
422      */

423     public void setUnitsExponent(int unitsExponent) {
424         this.unitsExponent = unitsExponent;
425     }
426
427     /**
428      * Sets the character width on the left side of the graph for
429      * y-axis values.
430      *
431      * @param unitsLength Number of characters on the left side of the graphs
432      *                    reserved for vertical axis labels.
433      */

434     public void setUnitsLength(int unitsLength) {
435         this.unitsLength = unitsLength;
436     }
437
438     /**
439      * Sets vertical label on the left side of the graph. This is normally used
440      * to specify the units used.
441      *
442      * @param verticalLabel Vertical axis label
443      */

444     public void setVerticalLabel(String verticalLabel) {
445         this.verticalLabel = verticalLabel;
446     }
447
448     /**
449      * Sets width of the drawing area within the graph. This affects the total
450      * size of the image.
451      *
452      * @param width Width of the drawing area.
453      */

454     public void setWidth(int width) {
455         this.width = width;
456     }
457
458     /**
459      * Sets height of the drawing area within the graph. This affects the total
460      * size of the image.
461      *
462      * @param height Height of the drawing area.
463      */

464     public void setHeight(int height) {
465         this.height = height;
466     }
467
468     /**
469      * Creates interlaced GIF image (currently not supported,
470      * method is present only for RRDTool comaptibility).
471      *
472      * @param interlaced trueif GIF image should be interlaced.
473      */

474     public void setInterlaced(boolean interlaced) {
475         this.interlaced = interlaced;
476     }
477
478     /**
479      * Creates additional image information.
480      * After the image has been created, the graph function uses imageInfo
481      * format string (printf-like) to create output similar to
482      * the {@link #print(String, String, String)} function.
483      * The format string is supplied with the following parameters:
484      * filename, xsize and ysize (in that particular order).
485      * <p/>
486      * For example, in order to generate an IMG tag
487      * suitable for including the graph into a web page, the command
488      * would look like this:
489      * <pre>
490      * setImageInfo(&quot;&lt;IMG SRC='/img/%s' WIDTH='%d' HEIGHT='%d' ALT='Demo'&gt;&quot;);
491      * </pre>
492      *
493      * @param imageInfo Image info format. Use %s placeholder for filename, %d placeholder for
494      *                  image width and height.
495      */

496     public void setImageInfo(String imageInfo) {
497         this.imageInfo = imageInfo;
498     }
499
500     /**
501      * Sets image format.
502      *
503      * @param imageFormat "PNG""GIF" or "JPG".
504      */

505     public void setImageFormat(String imageFormat) {
506         this.imageFormat = imageFormat;
507     }
508
509     /**
510      * Sets background image - currently, only PNG images can be used as background.
511      *
512      * @param backgroundImage Path to background image
513      */

514     public void setBackgroundImage(String backgroundImage) {
515         this.backgroundImage = backgroundImage;
516     }
517
518     /**
519      * Sets overlay image - currently, only PNG images can be used as overlay. Overlay image is
520      * printed on the top of the image, once it is completely created.
521      *
522      * @param overlayImage Path to overlay image
523      */

524     public void setOverlayImage(String overlayImage) {
525         this.overlayImage = overlayImage;
526     }
527
528     /**
529      * Sets unit to be displayed on y axis. It is wise to use only short units on graph, however.
530      *
531      * @param unit Unit description
532      */

533     public void setUnit(String unit) {
534         this.unit = unit;
535     }
536
537     /**
538      * Creates graph only if the current graph is out of date or not existent.
539      *
540      * @param lazy trueif graph should be 'lazy', false otherwise (defualt)
541      */

542     public void setLazy(boolean lazy) {
543         this.lazy = lazy;
544     }
545
546     /**
547      * Sets the lower limit of a graph. But rather, this is the
548      * maximum lower bound of a graph. For example, the value -100 will
549      * result in a graph that has a lower limit of -100 or less.  Use this
550      * method to expand graphs down.
551      *
552      * @param minValue Minimal value displayed on the graph
553      */

554     public void setMinValue(double minValue) {
555         this.minValue = minValue;
556     }
557
558     /**
559      * Defines the value normally located at the upper border of the
560      * graph. If the graph contains higher values, the upper border will
561      * move upwards to accommodate these values as well.
562      * <p/>
563      * If you want to define an upper-limit which will not move in any
564      * event you have to use {@link #setRigid(boolean)} method as well.
565      *
566      * @param maxValue Maximal value displayed on the graph.
567      */

568     public void setMaxValue(double maxValue) {
569         this.maxValue = maxValue;
570     }
571
572     /**
573      * Sets rigid boundaries mode. Normally JRObin will automatically expand
574      * the lower and upper limit if the graph contains a value outside the
575      * valid range. With the <code>true</code> argument you can disable this behavior.
576      *
577      * @param rigid true if uper and lower limits should not be expanded to accomodate
578      *              values outside of the specified range. False otherwise (default).
579      */

580     public void setRigid(boolean rigid) {
581         this.rigid = rigid;
582     }
583
584     /**
585      * Sets default base for magnitude scaling. If you are graphing memory
586      * (and NOT network traffic) this switch should be set to 1024 so that 1Kb is 1024 byte.
587      * For traffic measurement, 1 kb/s is 1000 b/s.
588      *
589      * @param base Base value (defaults to 1000.0)
590      */

591     public void setBase(double base) {
592         this.base = base;
593     }
594
595     /**
596      * Sets logarithmic y-axis scaling.
597      *
598      * @param logarithmic truefor logarithmic scaling, false otherwise (default).
599      */

600     public void setLogarithmic(boolean logarithmic) {
601         this.logarithmic = logarithmic;
602     }
603
604     /**
605      * Overrides the colors for the standard elements of the graph. The colorTag
606      * must be one of the following constants defined in the
607      * {@link RrdGraphConstants}:
608      * {@link RrdGraphConstants#COLOR_BACK COLOR_BACK} background,
609      * {@link RrdGraphConstants#COLOR_CANVAS COLOR_CANVAS} canvas,
610      * {@link RrdGraphConstants#COLOR_SHADEA COLOR_SHADEA} left/top border,
611      * {@link RrdGraphConstants#COLOR_SHADEB COLOR_SHADEB} right/bottom border,
612      * {@link RrdGraphConstants#COLOR_GRID COLOR_GRID} major grid,
613      * {@link RrdGraphConstants#COLOR_MGRID COLOR_MGRID} minor grid,
614      * {@link RrdGraphConstants#COLOR_FONT COLOR_FONT} font,
615      * {@link RrdGraphConstants#COLOR_FRAME COLOR_FRAME} axis of the graph,
616      * {@link RrdGraphConstants#COLOR_ARROW COLOR_ARROW} arrow. This method can
617      * be called multiple times to set several colors.
618      * 
619      * @param colorTag
620      *            Color tag, as explained above.
621      * @param color
622      *            Any color (paint) you like
623      * @throws RrdException
624      *             Thrown if invalid colorTag is supplied.
625      */

626     public void setColor(int colorTag, Paint color) throws RrdException {
627         if (colorTag >= 0 && colorTag < colors.length) {
628             colors[colorTag] = color;
629         } else {
630             throw new RrdException("Invalid color index specified: " + colorTag);
631         }
632     }
633
634     /**
635      * Overrides the colors for the standard elements of the graph by element name.
636      * See {@link #setColor(int, java.awt.Paint)} for full explanation.
637      *
638      * @param colorName One of the following strings: "BACK""CANVAS""SHADEA""SHADEB",
639      *                  "GRID""MGRID""FONT""FRAME""ARROW"
640      * @param color     Any color (paint) you like
641      * @throws RrdException Thrown if invalid element name is supplied.
642      */

643     public void setColor(String colorName, Paint color) throws RrdException {
644         setColor(getColorTagByName(colorName), color);
645     }
646
647     private static int getColorTagByName(String colorName) throws RrdException {
648         for (int i = 0; i < COLOR_NAMES.length; i++) {
649             if (COLOR_NAMES[i].equalsIgnoreCase(colorName)) {
650                 return i;
651             }
652         }
653         throw new RrdException("Unknown color name specified: " + colorName);
654     }
655
656     /**
657      * Suppress generation of legend, only render the graph.
658      *
659      * @param noLegend true if graph legend should be omitted. False otherwise (default).
660      */

661     public void setNoLegend(boolean noLegend) {
662         this.noLegend = noLegend;
663     }
664
665     /**
666      * Suppresses anything but the graph, works only for height < 64.
667      *
668      * @param onlyGraph true if only graph should be created, false otherwise (default).
669      */

670     public void setOnlyGraph(boolean onlyGraph) {
671         this.onlyGraph = onlyGraph;
672     }
673
674     /**
675      * Force the generation of HRULE and VRULE legend even if those HRULE
676      * or VRULE will not be drawn because out of graph boundaries.
677      *
678      * @param forceRulesLegend true if rule legend should be always printed,
679      *                         false otherwise (default).
680      */

681     public void setForceRulesLegend(boolean forceRulesLegend) {
682         this.forceRulesLegend = forceRulesLegend;
683     }
684
685     /**
686      * Defines a title to be written into the graph.
687      *
688      * @param title Graph title.
689      */

690     public void setTitle(String title) {
691         this.title = title;
692     }
693
694     /**
695      * Suggests which time step should be used by JRobin while processing data from RRD files.
696      *
697      * @param step Desired time step (don't use this method if you don't know what you're doing).
698      */

699     public void setStep(long step) {
700         this.step = step;
701     }
702
703     /**
704      * Get the default small font for graphing.
705      * @return the font
706      */

707     public Font getSmallFont() {
708         return this.smallFont;
709     }
710
711     /**
712      * Get the default large font for graphing.
713      * @return the font
714      */

715     public Font getLargeFont() {
716         return this.largeFont;
717     }
718     
719
720     /**
721      * Sets default font for graphing. Note that JRobin will behave unpredictably if proportional
722      * font is selected.
723      *
724      * @param smallFont Default font for graphing. Use only monospaced fonts.
725      */

726     public void setSmallFont(Font smallFont) {
727         this.smallFont = smallFont;
728     }
729
730     /**
731      * Sets title font.
732      *
733      * @param largeFont Font to be used for graph title.
734      */

735     public void setLargeFont(Font largeFont) {
736         this.largeFont = largeFont;
737     }
738
739     /**
740      * Defines virtual datasource. This datasource can then be used
741      * in other methods like {@link #datasource(String, String)} or
742      * {@link #gprint(String, String, String)}.
743      *
744      * @param name      Source name
745      * @param rrdPath   Path to RRD file
746      * @param dsName    Datasource name in the specified RRD file
747      * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
748      */

749     public void datasource(String name, String rrdPath, String dsName, String consolFun) {
750         sources.add(new Def(name, rrdPath, dsName, consolFun));
751     }
752
753     /**
754      * Defines virtual datasource. This datasource can then be used
755      * in other methods like {@link #datasource(String, String)} or
756      * {@link #gprint(String, String, String)}.
757      *
758      * @param name      Source name
759      * @param rrdPath   Path to RRD file
760      * @param dsName    Datasource name in the specified RRD file
761      * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
762      * @param backend   Backend to be used while fetching data from a RRD file.
763      */

764     public void datasource(String name, String rrdPath, String dsName, String consolFun, String backend) {
765         sources.add(new Def(name, rrdPath, dsName, consolFun, backend));
766     }
767
768     /**
769      * Create a new virtual datasource by evaluating a mathematical
770      * expression, specified in Reverse Polish Notation (RPN).
771      *
772      * @param name          Source name
773      * @param rpnExpression RPN expression.
774      */

775     public void datasource(String name, String rpnExpression) {
776         sources.add(new CDef(name, rpnExpression));
777     }
778
779     /**
780      * Creates a new (static) virtual datasource. The value of the datasource is constant. This value is
781      * evaluated by applying the given consolidation function to another virtual datasource.
782      *
783      * @param name      Source name
784      * @param defName   Other source name
785      * @param consolFun Consolidation function to be applied to other datasource.
786      */

787     public void datasource(String name, String defName, String consolFun) {
788         sources.add(new SDef(name, defName, consolFun));
789     }
790
791     /**
792      * Creates a new (plottable) datasource. Datasource values are obtained from the given plottable
793      * object.
794      *
795      * @param name      Source name.
796      * @param plottable Plottable object.
797      */

798     public void datasource(String name, Plottable plottable) {
799         sources.add(new PDef(name, plottable));
800     }
801
802     /**
803      * Calculates the chosen consolidation function CF over the given datasource
804      * and creates the result by using the given format string.  In
805      * the format string there should be a '%[l]f', '%[l]g' or '%[l]e' marker in
806      * the place where the number should be printed.
807      * <p/>
808      * If an additional '%s' is found AFTER the marker, the value will be
809      * scaled and an appropriate SI magnitude unit will be printed in
810      * place of the '%s' marker. The scaling will take the '--base' argument
811      * into consideration!
812      * <p/>
813      * If a '%S' is used instead of a '%s', then instead of calculating
814      * the appropriate SI magnitude unit for this value, the previously
815      * calculated SI magnitude unit will be used.  This is useful if you
816      * want all the values in a print statement to have the same SI magnitude
817      * unit.  If there was no previous SI magnitude calculation made,
818      * then '%S' behaves like a '%s', unless the value is 0, in which case
819      * it does not remember a SI magnitude unit and a SI magnitude unit
820      * will only be calculated when the next '%s' is seen or the next '%S'
821      * for a non-zero value.
822      * <p/>
823      * Print results are collected in the {@link RrdGraphInfo} object which is retrieved
824      * from the {@link RrdGraph object} once the graph is created.
825      *
826      * @param srcName   Virtual source name
827      * @param consolFun Consolidation function to be applied to the source
828      * @param format    Format string (like "average = %10.3f %s")
829      */

830     public void print(String srcName, String consolFun, String format) {
831         comments.add(new PrintText(srcName, consolFun, format, false));
832     }
833
834     /**
835      * This method does basically the same thing as {@link #print(String, String, String)},
836      * but the result is printed on the graph itself, below the chart area.
837      *
838      * @param srcName   Virtual source name
839      * @param consolFun Consolidation function to be applied to the source
840      * @param format    Format string (like "average = %10.3f %s")
841      */

842     public void gprint(String srcName, String consolFun, String format) {
843         comments.add(new PrintText(srcName, consolFun, format, true));
844     }
845
846     /**
847      * Comment to be printed on the graph.
848      *
849      * @param text Comment text
850      */

851     public void comment(String text) {
852         comments.add(new CommentText(text));
853     }
854
855     /**
856      * Draws a horizontal rule into the graph and optionally adds a legend
857      *
858      * @param value  Position of the rule
859      * @param color  Rule color
860      * @param legend Legend text. If null, legend text will be omitted.
861      */

862     public void hrule(double value, Paint color, String legend) {
863         hrule(value, color, legend, 1.0F);
864     }
865
866     /**
867      * Draws a horizontal rule into the graph and optionally adds a legend
868      *
869      * @param value  Position of the rule
870      * @param color  Rule color
871      * @param legend Legend text. If null, legend text will be omitted.
872      * @param width  Rule width
873      */

874     public void hrule(double value, Paint color, String legend, float width) {
875         LegendText legendText = new LegendText(color, legend);
876         comments.add(legendText);
877         plotElements.add(new HRule(value, color, legendText, width));
878     }
879
880     /**
881      * Draws a vertical rule into the graph and optionally adds a legend
882      *
883      * @param timestamp Position of the rule (seconds since epoch)
884      * @param color     Rule color
885      * @param legend    Legend text. Use null to omit the text.
886      */

887     public void vrule(long timestamp, Paint color, String legend) {
888         vrule(timestamp, color, legend, 1.0F);
889     }
890
891     /**
892      * Draws a vertical rule into the graph and optionally adds a legend
893      *
894      * @param timestamp Position of the rule (seconds since epoch)
895      * @param color     Rule color
896      * @param legend    Legend text. Use null to omit the text.
897      * @param width     Rule width
898      */

899     public void vrule(long timestamp, Paint color, String legend, float width) {
900         LegendText legendText = new LegendText(color, legend);
901         comments.add(legendText);
902         plotElements.add(new VRule(timestamp, color, legendText, width));
903     }
904
905     /**
906      * Plots requested data as a line, using the color and the line width specified.
907      *
908      * @param srcName Virtual source name
909      * @param color   Line color
910      * @param legend  Legend text
911      * @param width   Line width (default: 1.0F)
912      */

913     public void line(String srcName, Paint color, String legend, float width) {
914         LegendText legendText = new LegendText(color, legend);
915         comments.add(legendText);
916         plotElements.add(new Line(srcName, color, width));
917     }
918
919     /**
920      * Plots requested data as a line, using the color specified. Line width is assumed to be
921      * 1.0F.
922      *
923      * @param srcName Virtual source name
924      * @param color   Line color
925      * @param legend  Legend text
926      */

927     public void line(String srcName, Paint color, String legend) {
928         line(srcName, color, legend, 1F);
929     }
930
931     /**
932      * Plots requested data in the form of the filled area starting from zero,
933      * using the color specified.
934      * 
935      * @param srcName
936      *            Virtual source name.
937      * @param color
938      *            Color of the filled area.
939      * @param legend
940      *            Legend text.
941      */

942     public void area(String srcName, Paint color, String legend) {
943         area(srcName, color);
944         if (legend.length() > 0) {
945             LegendText legendText = new LegendText(color, legend);
946             comments.add(legendText);
947         }
948     }
949
950     /**
951      * Plots requested data in the form of the filled area starting from zero,
952      * using the color specified.
953      * 
954      * @param srcName
955      *            Virtual source name.
956      * @param color
957      *            Color of the filled area.
958      */

959     public void area(String srcName, Paint color) {
960         plotElements.add(new Area(srcName, color));
961     }
962     
963     /**
964      * Does the same as {@link #line(String, java.awt.Paint, String)},
965      * but the graph gets stacked on top of the
966      * previous LINE, AREA or STACK graph. Depending on the type of the
967      * previous graph, the STACK will be either a LINE or an AREA.  This
968      * obviously implies that the first STACK must be preceded by an AREA
969      * or LINE.
970      * <p/>
971      * Note, that when you STACK onto *UNKNOWN* data, JRobin will not
972      * draw any graphics ... *UNKNOWN* is not zero.
973      *
974      * @param srcName Virtual source name
975      * @param color   Stacked graph color
976      * @param legend  Legend text
977      * @throws RrdException Thrown if this STACK has no previously defined AREA, STACK or LINE
978      *                      graph bellow it.
979      */

980     public void stack(String srcName, Paint color, String legend) throws RrdException {
981         // find parent AREA or LINE
982         SourcedPlotElement parent = null;
983         for (int i = plotElements.size() - 1; i >= 0; i--) {
984             PlotElement plotElement = plotElements.get(i);
985             if (plotElement instanceof SourcedPlotElement) {
986                 parent = (SourcedPlotElement) plotElement;
987                 break;
988             }
989         }
990         if (parent == null) {
991             throw new RrdException("You have to stack graph onto something (line or area)");
992         }
993         else {
994             LegendText legendText = new LegendText(color, legend);
995             comments.add(legendText);
996             plotElements.add(new Stack(parent, srcName, color));
997         }
998     }
999
1000     /**
1001      * Sets visibility of the X-axis grid.
1002      *
1003      * @param drawXGrid True if X-axis grid should be created (default), false otherwise.
1004      */

1005     public void setDrawXGrid(boolean drawXGrid) {
1006         this.drawXGrid = drawXGrid;
1007     }
1008
1009     /**
1010      * Sets visibility of the Y-axis grid.
1011      *
1012      * @param drawYGrid True if Y-axis grid should be created (default), false otherwise.
1013      */

1014     public void setDrawYGrid(boolean drawYGrid) {
1015         this.drawYGrid = drawYGrid;
1016     }
1017
1018     /**
1019      * Sets image quality. Relevant only for JPEG images.
1020      *
1021      * @param imageQuality (0F=worst, 1F=best).
1022      */

1023     public void setImageQuality(float imageQuality) {
1024         this.imageQuality = imageQuality;
1025     }
1026
1027     /**
1028      * Controls if the chart area of the image should be antialiased or not.
1029      *
1030      * @param antiAliasing use true to turn antialiasing on, false to turn it off (default)
1031      */

1032     public void setAntiAliasing(boolean antiAliasing) {
1033         this.antiAliasing = antiAliasing;
1034     }
1035
1036     /**
1037      * Shows or hides graph signature (gator) in the top right corner of the graph
1038      *
1039      * @param showSignature trueif signature should be seen (default), false otherwise
1040      */

1041     public void setShowSignature(boolean showSignature) {
1042         this.showSignature = showSignature;
1043     }
1044
1045     /**
1046      * Sets first day of the week.
1047      *
1048      * @param firstDayOfWeek One of the following constants:
1049      *                       {@link RrdGraphConstants#MONDAY MONDAY},
1050      *                       {@link RrdGraphConstants#TUESDAY TUESDAY},
1051      *                       {@link RrdGraphConstants#WEDNESDAY WEDNESDAY},
1052      *                       {@link RrdGraphConstants#THURSDAY THURSDAY},
1053      *                       {@link RrdGraphConstants#FRIDAY FRIDAY},
1054      *                       {@link RrdGraphConstants#SATURDAY SATURDAY},
1055      *                       {@link RrdGraphConstants#SUNDAY SUNDAY}
1056      */

1057     public void setFirstDayOfWeek(int firstDayOfWeek) {
1058         this.firstDayOfWeek = firstDayOfWeek;
1059     }
1060
1061     // helper methods
1062
1063     int printStatementCount() {
1064         int count = 0;
1065         for (CommentText comment : comments) {
1066             if (comment instanceof PrintText) {
1067                 if (comment.isPrint()) {
1068                     count++;
1069                 }
1070             }
1071         }
1072         return count;
1073     }
1074
1075     boolean shouldPlot() {
1076         if (plotElements.size() > 0) {
1077             return true;
1078         }
1079         for (CommentText comment : comments) {
1080             if (comment.isValidGraphElement()) {
1081                 return true;
1082             }
1083         }
1084         return false;
1085     }
1086 }
1087