001    /* ============================================================
002     * JRobin : Pure java implementation of RRDTool's functionality
003     * ============================================================
004     *
005     * Project Info:  http://www.jrobin.org
006     * Project Lead:  Sasa Markovic (saxon@jrobin.org)
007     *
008     * Developers:    Sasa Markovic (saxon@jrobin.org)
009     *
010     *
011     * (C) Copyright 2003-2005, by Sasa Markovic.
012     *
013     * This library is free software; you can redistribute it and/or modify it under the terms
014     * of the GNU Lesser General Public License as published by the Free Software Foundation;
015     * either version 2.1 of the License, or (at your option) any later version.
016     *
017     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
018     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
019     * See the GNU Lesser General Public License for more details.
020     *
021     * You should have received a copy of the GNU Lesser General Public License along with this
022     * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
023     * Boston, MA 02111-1307, USA.
024     */
025    package org.jrobin.graph;
026    
027    import java.awt.Font;
028    import java.awt.Paint;
029    import java.io.File;
030    import java.io.InputStream;
031    import java.net.MalformedURLException;
032    import java.net.URL;
033    import java.util.ArrayList;
034    import java.util.List;
035    
036    import org.jrobin.core.RrdException;
037    import org.jrobin.core.Util;
038    import org.jrobin.data.Plottable;
039    
040    /**
041     * Class which should be used to define new JRobin graph. Once constructed and populated with data
042     * object of this class should be passed to the constructor of the {@link RrdGraph} class which
043     * will actually create the graph.
044     * <p/>
045     * The text printed below the actual graph can be formated by appending
046     * special escaped characters at the end of a text. When ever such a
047     * character occurs, all pending text is pushed onto the graph according to
048     * the character specified.
049     * <p/>
050     * Valid markers are: \j for justified, \l for left aligned, \r for right
051     * aligned and \c for centered.
052     * <p/>
053     * Normally there are two space characters inserted between every two
054     * items printed into the graph. The space following a string can be
055     * suppressed by putting a \g at the end of the string. The \g also squashes
056     * any space inside the string if it is at the very end of the string.
057     * This can be used in connection with %s to suppress empty unit strings.
058     * <p/>
059     * A special case is COMMENT:\s this inserts some additional vertical
060     * space before placing the next row of legends.
061     * <p/>
062     * When text has to be formated without special instructions from your
063     * side, RRDTool will automatically justify the text as soon as one string
064     * goes over the right edge. If you want to prevent the justification
065     * without forcing a newline, you can use the special tag \J at the end of
066     * the string to disable the auto justification.
067     */
068    public class RrdGraphDef implements RrdGraphConstants {
069            boolean poolUsed = false; // ok
070            boolean antiAliasing = false; // ok
071            String filename = RrdGraphConstants.IN_MEMORY_IMAGE; // ok
072            long startTime, endTime; // ok
073            TimeAxisSetting timeAxisSetting = null; // ok
074            ValueAxisSetting valueAxisSetting = null; // ok
075            boolean altYGrid = false; // ok
076            boolean noMinorGrid = false; // ok
077            boolean altYMrtg = false; // ok
078            boolean altAutoscale = false; // ok
079            boolean altAutoscaleMax = false; // ok
080            int unitsExponent = Integer.MAX_VALUE; // ok
081            int unitsLength = DEFAULT_UNITS_LENGTH; // ok
082            String verticalLabel = null; // ok
083            int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT; // ok
084            boolean interlaced = false; // ok
085            String imageInfo = null; // ok
086            String imageFormat = DEFAULT_IMAGE_FORMAT; // ok
087            float imageQuality = DEFAULT_IMAGE_QUALITY; // ok
088            String backgroundImage = null; // ok
089            String overlayImage = null; // ok
090            String unit = null; // ok
091            String signature = "Created with JRobin"; // ok
092            boolean lazy = false; // ok
093            double minValue = Double.NaN; // ok
094            double maxValue = Double.NaN; // ok
095            boolean rigid = false; // ok
096            double base = DEFAULT_BASE;  // ok
097            boolean logarithmic = false; // ok
098            Paint[] colors = new Paint[] {
099                            // 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 true, if 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 true, if 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 true, if 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 true, if 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 true, for 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 true, if 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    }