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 * (C) Copyright 2003-2005, by Sasa Markovic.
009 *
010 * Developers: Sasa Markovic (saxon@jrobin.org)
011 *
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 org.jrobin.core.RrdException;
028 import org.jrobin.core.Util;
029 import org.jrobin.core.XmlTemplate;
030 import org.w3c.dom.Node;
031 import org.xml.sax.InputSource;
032
033 import java.awt.*;
034 import java.io.File;
035 import java.io.IOException;
036
037 /**
038 * Class used to create an arbitrary number of RrdGraphDef (graph definition) objects
039 * from a single XML template. XML template can be supplied as an XML InputSource,
040 * XML file or XML formatted string.<p>
041 * <p/>
042 * Here is an example of a properly formatted XML template with all available options in it
043 * (unwanted options can be removed/ignored):<p>
044 * <pre>
045 * <rrd_graph_def>
046 * <!-- use '-' to represent in-memory graph -->
047 * <filename>test.png</filename>
048 * <!--
049 * starting and ending timestamps can be specified by
050 * using at-style time specification, or by specifying
051 * exact timestamps since epoch (without milliseconds)
052 * -->
053 * <span>
054 * <start>now - 1d</start>
055 * <end>now</end>
056 * </span>
057 * <options>
058 * <!--
059 * specify 'true' if you want to use RrdDbPool while
060 * creating graph
061 * -->
062 * <use_pool>false</use_pool>
063 * <anti_aliasing>true</anti_aliasing>
064 * <time_grid>
065 * <show_grid>true</show_grid>
066 * <!-- allowed units: second, minute, hour, day, week, month, year -->
067 * <minor_grid_unit>minute</minor_grid_unit>
068 * <minor_grid_unit_count>60</minor_grid_unit_count>
069 * <major_grid_unit>hour</major_grid_unit>
070 * <major_grid_unit_count>2</major_grid_unit_count>
071 * <label_unit>hour</label_unit>
072 * <label_unit_count>2</label_unit_count>
073 * <label_span>1200</label_span>
074 * <!-- use SimpleDateFormat or strftime-like format to format labels -->
075 * <label_format>dd-MMM-yy</label_format>
076 * </time_grid>
077 * <value_grid>
078 * <show_grid>true</show_grid>
079 * <grid_step>100.0</grid_step>
080 * <label_factor>5</label_factor>
081 * </value_grid>
082 * <no_minor_grid>true</no_minor_grid>
083 * <alt_y_grid>true</alt_y_grid>
084 * <alt_y_mrtg>true</alt_y_mrtg>
085 * <alt_autoscale>true</alt_autoscale>
086 * <alt_autoscale_max>true</alt_autoscale_max>
087 * <units_exponent>3</units_exponent>
088 * <units_length>13</units_length>
089 * <vertical_label>Speed (kbits/sec)</vertical_label>
090 * <width>444</width>
091 * <height>222</height>
092 * <interlaced>true</interlaced>
093 * <image_info>filename = %s, width=%d, height=%d</image_info>
094 * <image_format>png</image_format>
095 * <image_quality>0.8</image_quality>
096 * <background_image>luka.png</background_image>
097 * <overlay_image>luka.png</overlay_image>
098 * <unit>kilos</unit>
099 * <lazy>false</lazy>
100 * <min_value>0</min_value>
101 * <max_value>5000</max_value>
102 * <rigid>true</rigid>
103 * <base>1000</base>
104 * <logarithmic>false</logarithmic>
105 * <colors>
106 * <canvas>#FFFFFF</canvas>
107 * <back>#FFFFFF</back>
108 * <shadea>#AABBCC</shadea>
109 * <shadeb>#DDDDDD</shadeb>
110 * <grid>#FF0000</grid>
111 * <mgrid>#00FF00</mgrid>
112 * <font>#FFFFFF</font>
113 * <frame>#EE00FF</frame>
114 * <arrow>#FF0000</arrow>
115 * </colors>
116 * <no_legend>false</no_legend>
117 * <only_graph>false</only_graph>
118 * <force_rules_legend>false</force_rules_legend>
119 * <title>This is a title</title>
120 * <step>300</step>
121 * <fonts>
122 * <small_font>
123 * <name>Courier</name>
124 * <style>bold italic</style>
125 * <size>12</size>
126 * </small_font>
127 * <large_font>
128 * <name>Courier</name>
129 * <style>plain</style>
130 * <size>11</size>
131 * </large_font>
132 * </fonts>
133 * <first_day_of_week>SUNDAY</first_day_of_week>
134 * </options>
135 * <datasources>
136 * <def>
137 * <name>x</name>
138 * <rrd>test.rrd</rrd>
139 * <source>sun</source>
140 * <cf>AVERAGE</cf>
141 * <backend>FILE</backend>
142 * </def>
143 * <def>
144 * <name>y</name>
145 * <rrd>test.rrd</rrd>
146 * <source>shade</source>
147 * <cf>AVERAGE</cf>
148 * </def>
149 * <cdef>
150 * <name>x_plus_y</name>
151 * <rpn>x,y,+</rpn>
152 * </cdef>
153 * <cdef>
154 * <name>x_minus_y</name>
155 * <rpn>x,y,-</rpn>
156 * </cdef>
157 * <sdef>
158 * <name>x_avg</name>
159 * <source>x</source>
160 * <cf>AVERAGE</cf>
161 * </sdef>
162 * <sdef>
163 * <name>y_max</name>
164 * <source>y</source>
165 * <cf>MAX</cf>
166 * </sdef>
167 * </datasources>
168 * <graph>
169 * <area>
170 * <datasource>x</datasource>
171 * <color>#FF0000</color>
172 * <legend>X value\r</legend>
173 * </area>
174 * <stack>
175 * <datasource>y</datasource>
176 * <color>#00FF00</color>
177 * <legend>Y value\r</legend>
178 * </stack>
179 * <line>
180 * <datasource>x</datasource>
181 * <color>#FF0000</color>
182 * <legend>X value\r</legend>
183 * <width>2</width>
184 * </line>
185 * <print>
186 * <datasource>x</datasource>
187 * <cf>AVERAGE</cf>
188 * <format>Average is %7.3f\c</format>
189 * </print>
190 * <gprint>
191 * <datasource>y</datasource>
192 * <cf>MAX</cf>
193 * <format>Max is %7.3f\c</format>
194 * </gprint>
195 * <hrule>
196 * <value>1250</value>
197 * <color>#0000FF</color>
198 * <legend>This is a horizontal rule</legend>
199 * </hrule>
200 * <vrule>
201 * <time>now-6h</time>
202 * <color>#0000FF</color>
203 * <legend>This is a vertical rule</legend>
204 * </vrule>
205 * <comment>Simple comment</comment>
206 * <comment>One more comment\c</comment>
207 * </graph>
208 * </rrd_graph_def>
209 * </pre>
210 * Notes on the template syntax:<p>
211 * <ul>
212 * <li>There is a strong relation between the XML template syntax and the syntax of
213 * {@link RrdGraphDef} class methods. If you are not sure what some XML tag means, check javadoc
214 * for the corresponding class method.
215 * <li>hard-coded timestamps in templates should be long integeres
216 * (like: 1000243567) or at-style formatted strings
217 * <li>whitespaces are not harmful
218 * <li>use <code>true</code>, <code>on</code>, <code>yes</code>, <code>y</code>,
219 * or <code>1</code> to specify boolean <code>true</code> value (anything else will
220 * be treated as <code>false</code>).
221 * <li>floating point values: anything that cannot be parsed will be treated as Double.NaN
222 * (like: U, unknown, 12r.23)
223 * <li>use #RRGGBB or #RRGGBBAA format to specify colors.
224 * <li>valid font styles are: PLAIN, ITALIC, BOLD, BOLDITALIC
225 * <li>comments are allowed.
226 * </ul>
227 * Any template value (text between <code><some_tag></code> and
228 * <code></some_tag></code>) can be replaced with
229 * a variable of the following form: <code>${variable_name}</code>. Use
230 * {@link XmlTemplate#setVariable(String, String) setVariable()}
231 * methods from the base class to replace
232 * template variables with real values at runtime.<p>
233 * <p/>
234 * Typical usage scenario:<p>
235 * <ul>
236 * <li>Create your XML template and save it to a file (template.xml, for example)
237 * <li>Replace template values with variables if you want to change them during runtime.
238 * For example, time span should not be hard-coded in the template - you probably want to create
239 * many different graphs with different time spans from the same XML template.
240 * For example, your XML template could start with:
241 * <pre>
242 * <rrd_graph_def>
243 * ...
244 * <span>
245 * <start>${start}</start>
246 * <end>${end}</end>
247 * </span>
248 * ...
249 * </pre>
250 * <li>In your Java code, create RrdGraphDefTemplate object using your XML template file:
251 * <pre>
252 * RrdGraphDefTemplate t = new RrdGraphDefTemplate(new File(template.xml));
253 * </pre>
254 * <li>Then, specify real values for template variables:
255 * <pre>
256 * t.setVariable("start", new GregorianCalendar(2004, 2, 25));
257 * t.setVariable("end", new GregorianCalendar(2004, 2, 26));
258 * </pre>
259 * <li>Once all template variables are set, just use the template object to create RrdGraphDef
260 * object. This object is actually used to create JRobin grahps:
261 * <pre>
262 * RrdGraphDef gdef = t.getRrdGraphDef();
263 * RrdGraph g = new RrdGraph(gdef);
264 * </pre>
265 * </ul>
266 * You should create new RrdGraphDefTemplate object only once for each XML template. Single template
267 * object can be reused to create as many RrdGraphDef objects as needed, with different values
268 * specified for template variables. XML synatax check is performed only once - the first graph
269 * definition object gets created relatively slowly, but it will be created much faster next time.
270 */
271 public class RrdGraphDefTemplate extends XmlTemplate implements RrdGraphConstants {
272 static final Color BLIND_COLOR = new Color(0, 0, 0, 0);
273
274 private RrdGraphDef rrdGraphDef;
275
276 /**
277 * Creates template object from any parsable XML source
278 *
279 * @param inputSource XML source
280 * @throws IOException thrown in case of I/O error
281 * @throws RrdException usually thrown in case of XML related error
282 */
283 public RrdGraphDefTemplate(InputSource inputSource) throws IOException, RrdException {
284 super(inputSource);
285 }
286
287 /**
288 * Creates template object from the file containing XML template code
289 *
290 * @param xmlFile file containing XML template
291 * @throws IOException thrown in case of I/O error
292 * @throws RrdException usually thrown in case of XML related error
293 */
294 public RrdGraphDefTemplate(File xmlFile) throws IOException, RrdException {
295 super(xmlFile);
296 }
297
298 /**
299 * Creates template object from the string containing XML template code
300 *
301 * @param xmlString string containing XML template
302 * @throws IOException thrown in case of I/O error
303 * @throws RrdException usually thrown in case of XML related error
304 */
305 public RrdGraphDefTemplate(String xmlString) throws IOException, RrdException {
306 super(xmlString);
307 }
308
309 /**
310 * Creates RrdGraphDef object which can be used to create RrdGraph
311 * object (actual JRobin graphs). Before this method is called, all template variables (if any)
312 * must be resolved (replaced with real values).
313 * See {@link XmlTemplate#setVariable(String, String) setVariable()} method information to
314 * understand how to supply values for template variables.
315 *
316 * @return Graph definition which can be used to create RrdGraph object (actual JRobin graphs)
317 * @throws RrdException Thrown if parsed XML template contains invalid (unrecognized) tags
318 */
319 public RrdGraphDef getRrdGraphDef() throws RrdException {
320 // basic check
321 if (!root.getTagName().equals("rrd_graph_def")) {
322 throw new RrdException("XML definition must start with <rrd_graph_def>");
323 }
324 validateTagsOnlyOnce(root, new String[] {"filename", "span", "options", "datasources", "graph"});
325 rrdGraphDef = new RrdGraphDef();
326 // traverse all nodes
327 Node[] childNodes = getChildNodes(root);
328 for (Node childNode : childNodes) {
329 String nodeName = childNode.getNodeName();
330 if (nodeName.equals("filename")) {
331 resolveFilename(childNode);
332 }
333 // SPAN
334 else if (nodeName.equals("span")) {
335 resolveSpan(childNode);
336 }
337 // OPTIONS
338 else if (nodeName.equals("options")) {
339 resolveOptions(childNode);
340 }
341 // DATASOURCES
342 else if (nodeName.equals("datasources")) {
343 resolveDatasources(childNode);
344 }
345 // GRAPH ELEMENTS
346 else if (nodeName.equals("graph")) {
347 resolveGraphElements(childNode);
348 }
349 }
350 return rrdGraphDef;
351 }
352
353 private void resolveGraphElements(Node graphNode) throws RrdException {
354 validateTagsOnlyOnce(graphNode, new String[] {"area*", "line*", "stack*",
355 "print*", "gprint*", "hrule*", "vrule*", "comment*"});
356 Node[] childNodes = getChildNodes(graphNode);
357 for (Node childNode : childNodes) {
358 String nodeName = childNode.getNodeName();
359 if (nodeName.equals("area")) {
360 resolveArea(childNode);
361 }
362 else if (nodeName.equals("line")) {
363 resolveLine(childNode);
364 }
365 else if (nodeName.equals("stack")) {
366 resolveStack(childNode);
367 }
368 else if (nodeName.equals("print")) {
369 resolvePrint(childNode, false);
370 }
371 else if (nodeName.equals("gprint")) {
372 resolvePrint(childNode, true);
373 }
374 else if (nodeName.equals("hrule")) {
375 resolveHRule(childNode);
376 }
377 else if (nodeName.equals("vrule")) {
378 resolveVRule(childNode);
379 }
380 else if (nodeName.equals("comment")) {
381 rrdGraphDef.comment(getValue(childNode));
382 }
383 }
384 }
385
386 private void resolveVRule(Node parentNode) throws RrdException {
387 validateTagsOnlyOnce(parentNode, new String[] {"time", "color", "legend"});
388 long timestamp = Long.MIN_VALUE;
389 Paint color = null;
390 String legend = null;
391 Node[] childNodes = getChildNodes(parentNode);
392 for (Node childNode : childNodes) {
393 String nodeName = childNode.getNodeName();
394 if (nodeName.equals("time")) {
395 timestamp = Util.getTimestamp(getValue(childNode));
396 }
397 else if (nodeName.equals("color")) {
398 color = getValueAsColor(childNode);
399 }
400 else if (nodeName.equals("legend")) {
401 legend = getValue(childNode);
402 }
403 }
404 if (timestamp != Long.MIN_VALUE && color != null) {
405 rrdGraphDef.vrule(timestamp, color, legend);
406 }
407 else {
408 throw new RrdException("Incomplete VRULE settings");
409 }
410 }
411
412 private void resolveHRule(Node parentNode) throws RrdException {
413 validateTagsOnlyOnce(parentNode, new String[] {"value", "color", "legend"});
414 double value = Double.NaN;
415 Paint color = null;
416 String legend = null;
417 Node[] childNodes = getChildNodes(parentNode);
418 for (Node childNode : childNodes) {
419 String nodeName = childNode.getNodeName();
420 if (nodeName.equals("value")) {
421 value = getValueAsDouble(childNode);
422 }
423 else if (nodeName.equals("color")) {
424 color = getValueAsColor(childNode);
425 }
426 else if (nodeName.equals("legend")) {
427 legend = getValue(childNode);
428 }
429 }
430 if (!Double.isNaN(value) && color != null) {
431 rrdGraphDef.hrule(value, color, legend);
432 }
433 else {
434 throw new RrdException("Incomplete HRULE settings");
435 }
436 }
437
438 private void resolvePrint(Node parentNode, boolean isInGraph) throws RrdException {
439 validateTagsOnlyOnce(parentNode, new String[] {"datasource", "cf", "format"});
440 String datasource = null, cf = null, format = null;
441 Node[] childNodes = getChildNodes(parentNode);
442 for (Node childNode : childNodes) {
443 String nodeName = childNode.getNodeName();
444 if (nodeName.equals("datasource")) {
445 datasource = getValue(childNode);
446 }
447 else if (nodeName.equals("cf")) {
448 cf = getValue(childNode);
449 }
450 else if (nodeName.equals("format")) {
451 format = getValue(childNode);
452 }
453 }
454 if (datasource != null && cf != null && format != null) {
455 if (isInGraph) {
456 rrdGraphDef.gprint(datasource, cf, format);
457 }
458 else {
459 rrdGraphDef.print(datasource, cf, format);
460 }
461 }
462 else {
463 throw new RrdException("Incomplete " + (isInGraph ? "GRPINT" : "PRINT") + " settings");
464 }
465 }
466
467 private void resolveStack(Node parentNode) throws RrdException {
468 validateTagsOnlyOnce(parentNode, new String[] {"datasource", "color", "legend"});
469 String datasource = null, legend = null;
470 Paint color = null;
471 Node[] childNodes = getChildNodes(parentNode);
472 for (Node childNode : childNodes) {
473 String nodeName = childNode.getNodeName();
474 if (nodeName.equals("datasource")) {
475 datasource = getValue(childNode);
476 }
477 else if (nodeName.equals("color")) {
478 color = getValueAsColor(childNode);
479 }
480 else if (nodeName.equals("legend")) {
481 legend = getValue(childNode);
482 }
483 }
484 if (datasource != null) {
485 if (color != null) {
486 rrdGraphDef.stack(datasource, color, legend);
487 }
488 else {
489 rrdGraphDef.stack(datasource, BLIND_COLOR, legend);
490 }
491 }
492 else {
493 throw new RrdException("Incomplete STACK settings");
494 }
495 }
496
497 private void resolveLine(Node parentNode) throws RrdException {
498 validateTagsOnlyOnce(parentNode, new String[] {"datasource", "color", "legend", "width"});
499 String datasource = null, legend = null;
500 Paint color = null;
501 float width = 1.0F;
502 Node[] childNodes = getChildNodes(parentNode);
503 for (Node childNode : childNodes) {
504 String nodeName = childNode.getNodeName();
505 if (nodeName.equals("datasource")) {
506 datasource = getValue(childNode);
507 }
508 else if (nodeName.equals("color")) {
509 color = getValueAsColor(childNode);
510 }
511 else if (nodeName.equals("legend")) {
512 legend = getValue(childNode);
513 }
514 else if (nodeName.equals("width")) {
515 width = (float) getValueAsDouble(childNode);
516 }
517 }
518 if (datasource != null) {
519 if (color != null) {
520 rrdGraphDef.line(datasource, color, legend, width);
521 }
522 else {
523 rrdGraphDef.line(datasource, BLIND_COLOR, legend, width);
524 }
525 }
526 else {
527 throw new RrdException("Incomplete LINE settings");
528 }
529 }
530
531 private void resolveArea(Node parentNode) throws RrdException {
532 validateTagsOnlyOnce(parentNode, new String[] {"datasource", "color", "legend"});
533 String datasource = null, legend = null;
534 Paint color = null;
535 Node[] childNodes = getChildNodes(parentNode);
536 for (Node childNode : childNodes) {
537 String nodeName = childNode.getNodeName();
538 if (nodeName.equals("datasource")) {
539 datasource = getValue(childNode);
540 }
541 else if (nodeName.equals("color")) {
542 color = getValueAsColor(childNode);
543 }
544 else if (nodeName.equals("legend")) {
545 legend = getValue(childNode);
546 }
547 }
548 if (datasource != null) {
549 if (color != null) {
550 rrdGraphDef.area(datasource, color, legend);
551 }
552 else {
553 rrdGraphDef.area(datasource, BLIND_COLOR, legend);
554 }
555 }
556 else {
557 throw new RrdException("Incomplete AREA settings");
558 }
559 }
560
561 private void resolveDatasources(Node datasourcesNode) throws RrdException {
562 validateTagsOnlyOnce(datasourcesNode, new String[] {"def*", "cdef*", "sdef*"});
563 Node[] childNodes = getChildNodes(datasourcesNode);
564 for (Node childNode : childNodes) {
565 String nodeName = childNode.getNodeName();
566 if (nodeName.equals("def")) {
567 resolveDef(childNode);
568 }
569 else if (nodeName.equals("cdef")) {
570 resolveCDef(childNode);
571 }
572 else if (nodeName.equals("sdef")) {
573 resolveSDef(childNode);
574 }
575 }
576 }
577
578 private void resolveSDef(Node parentNode) throws RrdException {
579 validateTagsOnlyOnce(parentNode, new String[] {"name", "source", "cf"});
580 String name = null, source = null, cf = null;
581 Node[] childNodes = getChildNodes(parentNode);
582 for (Node childNode : childNodes) {
583 String nodeName = childNode.getNodeName();
584 if (nodeName.equals("name")) {
585 name = getValue(childNode);
586 }
587 else if (nodeName.equals("source")) {
588 source = getValue(childNode);
589 }
590 else if (nodeName.equals("cf")) {
591 cf = getValue(childNode);
592 }
593 }
594 if (name != null && source != null && cf != null) {
595 rrdGraphDef.datasource(name, source, cf);
596 }
597 else {
598 throw new RrdException("Incomplete SDEF settings");
599 }
600 }
601
602 private void resolveCDef(Node parentNode) throws RrdException {
603 validateTagsOnlyOnce(parentNode, new String[] {"name", "rpn"});
604 String name = null, rpn = null;
605 Node[] childNodes = getChildNodes(parentNode);
606 for (Node childNode : childNodes) {
607 String nodeName = childNode.getNodeName();
608 if (nodeName.equals("name")) {
609 name = getValue(childNode);
610 }
611 else if (nodeName.equals("rpn")) {
612 rpn = getValue(childNode);
613 }
614 }
615 if (name != null && rpn != null) {
616 rrdGraphDef.datasource(name, rpn);
617 }
618 else {
619 throw new RrdException("Incomplete CDEF settings");
620 }
621 }
622
623 private void resolveDef(Node parentNode) throws RrdException {
624 validateTagsOnlyOnce(parentNode, new String[] {"name", "rrd", "source", "cf", "backend"});
625 String name = null, rrd = null, source = null, cf = null, backend = null;
626 Node[] childNodes = getChildNodes(parentNode);
627 for (Node childNode : childNodes) {
628 String nodeName = childNode.getNodeName();
629 if (nodeName.equals("name")) {
630 name = getValue(childNode);
631 }
632 else if (nodeName.equals("rrd")) {
633 rrd = getValue(childNode);
634 }
635 else if (nodeName.equals("source")) {
636 source = getValue(childNode);
637 }
638 else if (nodeName.equals("cf")) {
639 cf = getValue(childNode);
640 }
641 else if (nodeName.equals("backend")) {
642 backend = getValue(childNode);
643 }
644 }
645 if (name != null && rrd != null && source != null && cf != null) {
646 rrdGraphDef.datasource(name, rrd, source, cf, backend);
647 }
648 else {
649 throw new RrdException("Incomplete DEF settings");
650 }
651 }
652
653 private void resolveFilename(Node filenameNode) {
654 String filename = getValue(filenameNode);
655 rrdGraphDef.setFilename(filename);
656 }
657
658 private void resolveSpan(Node spanNode) throws RrdException {
659 validateTagsOnlyOnce(spanNode, new String[] {"start", "end"});
660 String startStr = getChildValue(spanNode, "start");
661 String endStr = getChildValue(spanNode, "end");
662 long[] span = Util.getTimestamps(startStr, endStr);
663 rrdGraphDef.setStartTime(span[0]);
664 rrdGraphDef.setEndTime(span[1]);
665 }
666
667 private void resolveOptions(Node rootOptionNode) throws RrdException {
668 validateTagsOnlyOnce(rootOptionNode, new String[] {
669 "anti_aliasing", "use_pool", "time_grid", "value_grid", "alt_y_grid", "alt_y_mrtg",
670 "no_minor_grid", "alt_autoscale", "alt_autoscale_max", "units_exponent", "units_length",
671 "vertical_label", "width", "height", "interlaced", "image_info", "image_format",
672 "image_quality", "background_image", "overlay_image", "unit", "lazy",
673 "min_value", "max_value", "rigid", "base", "logarithmic", "colors",
674 "no_legend", "only_graph", "force_rules_legend", "title", "step", "fonts",
675 "first_day_of_week", "signature"
676 });
677 Node[] optionNodes = getChildNodes(rootOptionNode);
678 for (Node optionNode : optionNodes) {
679 String option = optionNode.getNodeName();
680 if (option.equals("use_pool")) {
681 rrdGraphDef.setPoolUsed(getValueAsBoolean(optionNode));
682 }
683 else if (option.equals("anti_aliasing")) {
684 rrdGraphDef.setAntiAliasing(getValueAsBoolean(optionNode));
685 }
686 else if (option.equals("time_grid")) {
687 resolveTimeGrid(optionNode);
688 }
689 else if (option.equals("value_grid")) {
690 resolveValueGrid(optionNode);
691 }
692 else if (option.equals("no_minor_grid")) {
693 rrdGraphDef.setNoMinorGrid(getValueAsBoolean(optionNode));
694 }
695 else if (option.equals("alt_y_grid")) {
696 rrdGraphDef.setAltYGrid(getValueAsBoolean(optionNode));
697 }
698 else if (option.equals("alt_y_mrtg")) {
699 rrdGraphDef.setAltYMrtg(getValueAsBoolean(optionNode));
700 }
701 else if (option.equals("alt_autoscale")) {
702 rrdGraphDef.setAltAutoscale(getValueAsBoolean(optionNode));
703 }
704 else if (option.equals("alt_autoscale_max")) {
705 rrdGraphDef.setAltAutoscaleMax(getValueAsBoolean(optionNode));
706 }
707 else if (option.equals("units_exponent")) {
708 rrdGraphDef.setUnitsExponent(getValueAsInt(optionNode));
709 }
710 else if (option.equals("units_length")) {
711 rrdGraphDef.setUnitsLength(getValueAsInt(optionNode));
712 }
713 else if (option.equals("vertical_label")) {
714 rrdGraphDef.setVerticalLabel(getValue(optionNode));
715 }
716 else if (option.equals("width")) {
717 rrdGraphDef.setWidth(getValueAsInt(optionNode));
718 }
719 else if (option.equals("height")) {
720 rrdGraphDef.setHeight(getValueAsInt(optionNode));
721 }
722 else if (option.equals("interlaced")) {
723 rrdGraphDef.setInterlaced(getValueAsBoolean(optionNode));
724 }
725 else if (option.equals("image_info")) {
726 rrdGraphDef.setImageInfo(getValue(optionNode));
727 }
728 else if (option.equals("image_format")) {
729 rrdGraphDef.setImageFormat(getValue(optionNode));
730 }
731 else if (option.equals("image_quality")) {
732 rrdGraphDef.setImageQuality((float) getValueAsDouble(optionNode));
733 }
734 else if (option.equals("background_image")) {
735 rrdGraphDef.setBackgroundImage(getValue(optionNode));
736 }
737 else if (option.equals("overlay_image")) {
738 rrdGraphDef.setOverlayImage(getValue(optionNode));
739 }
740 else if (option.equals("unit")) {
741 rrdGraphDef.setUnit(getValue(optionNode));
742 }
743 else if (option.equals("lazy")) {
744 rrdGraphDef.setLazy(getValueAsBoolean(optionNode));
745 }
746 else if (option.equals("min_value")) {
747 rrdGraphDef.setMinValue(getValueAsDouble(optionNode));
748 }
749 else if (option.equals("max_value")) {
750 rrdGraphDef.setMaxValue(getValueAsDouble(optionNode));
751 }
752 else if (option.equals("rigid")) {
753 rrdGraphDef.setRigid(getValueAsBoolean(optionNode));
754 }
755 else if (option.equals("base")) {
756 rrdGraphDef.setBase(getValueAsDouble(optionNode));
757 }
758 else if (option.equals("logarithmic")) {
759 rrdGraphDef.setLogarithmic(getValueAsBoolean(optionNode));
760 }
761 else if (option.equals("colors")) {
762 resolveColors(optionNode);
763 }
764 else if (option.equals("no_legend")) {
765 rrdGraphDef.setNoLegend(getValueAsBoolean(optionNode));
766 }
767 else if (option.equals("only_graph")) {
768 rrdGraphDef.setOnlyGraph(getValueAsBoolean(optionNode));
769 }
770 else if (option.equals("force_rules_legend")) {
771 rrdGraphDef.setForceRulesLegend(getValueAsBoolean(optionNode));
772 }
773 else if (option.equals("title")) {
774 rrdGraphDef.setTitle(getValue(optionNode));
775 }
776 else if (option.equals("step")) {
777 rrdGraphDef.setStep(getValueAsLong(optionNode));
778 }
779 else if (option.equals("fonts")) {
780 resolveFonts(optionNode);
781 }
782 else if (option.equals("first_day_of_week")) {
783 int dayIndex = resolveFirstDayOfWeek(getValue(optionNode));
784 rrdGraphDef.setFirstDayOfWeek(dayIndex);
785 }
786 else if (option.equals("signature")) {
787 rrdGraphDef.setShowSignature(getValueAsBoolean(optionNode));
788 }
789 }
790 }
791
792 private int resolveFirstDayOfWeek(String firstDayOfWeek) throws RrdException {
793 if (firstDayOfWeek.equalsIgnoreCase("sunday")) {
794 return SUNDAY;
795 }
796 else if (firstDayOfWeek.equalsIgnoreCase("monday")) {
797 return MONDAY;
798 }
799 else if (firstDayOfWeek.equalsIgnoreCase("tuesday")) {
800 return TUESDAY;
801 }
802 else if (firstDayOfWeek.equalsIgnoreCase("wednesday")) {
803 return WEDNESDAY;
804 }
805 else if (firstDayOfWeek.equalsIgnoreCase("thursday")) {
806 return THURSDAY;
807 }
808 else if (firstDayOfWeek.equalsIgnoreCase("friday")) {
809 return FRIDAY;
810 }
811 else if (firstDayOfWeek.equalsIgnoreCase("saturday")) {
812 return SATURDAY;
813 }
814 throw new RrdException("Never heard for this day of week: " + firstDayOfWeek);
815 }
816
817 private void resolveFonts(Node parentNode) throws RrdException {
818 validateTagsOnlyOnce(parentNode, new String[] {"small_font", "large_font"});
819 Node[] childNodes = getChildNodes(parentNode);
820 for (Node childNode : childNodes) {
821 String nodeName = childNode.getNodeName();
822 if (nodeName.equals("small_font")) {
823 rrdGraphDef.setSmallFont(resolveFont(childNode));
824 }
825 else if (nodeName.equals("large_font")) {
826 rrdGraphDef.setLargeFont(resolveFont(childNode));
827 }
828 }
829 }
830
831 private Font resolveFont(Node parentNode) throws RrdException {
832 validateTagsOnlyOnce(parentNode, new String[] {"name", "style", "size"});
833 String name = null, style = null;
834 int size = 0;
835 Node[] childNodes = getChildNodes(parentNode);
836 for (Node childNode : childNodes) {
837 String nodeName = childNode.getNodeName();
838 if (nodeName.equals("name")) {
839 name = getValue(childNode);
840 }
841 else if (nodeName.equals("style")) {
842 style = getValue(childNode).toLowerCase();
843 }
844 else if (nodeName.equals("size")) {
845 size = getValueAsInt(childNode);
846 }
847 }
848 if (name != null && style != null && size > 0) {
849 boolean isItalic = style.contains("italic"), isBold = style.contains("bold");
850 int fstyle = Font.PLAIN;
851 if (isItalic && isBold) {
852 fstyle = Font.BOLD + Font.ITALIC;
853 }
854 else if (isItalic) {
855 fstyle = Font.ITALIC;
856 }
857 else if (isBold) {
858 fstyle = Font.BOLD;
859 }
860 return new Font(name, fstyle, size);
861 }
862 else {
863 throw new RrdException("Incomplete font specification");
864 }
865 }
866
867 private void resolveColors(Node parentNode) throws RrdException {
868 validateTagsOnlyOnce(parentNode, COLOR_NAMES);
869 Node[] childNodes = getChildNodes(parentNode);
870 for (Node childNode : childNodes) {
871 String colorName = childNode.getNodeName();
872 rrdGraphDef.setColor(colorName, getValueAsColor(childNode));
873 }
874 }
875
876 private void resolveValueGrid(Node parentNode) throws RrdException {
877 validateTagsOnlyOnce(parentNode, new String[] {"show_grid", "grid_step", "label_factor"});
878 boolean showGrid = true;
879 double gridStep = Double.NaN;
880 int NOT_SET = Integer.MIN_VALUE, labelFactor = NOT_SET;
881 Node[] childNodes = getChildNodes(parentNode);
882 for (Node childNode : childNodes) {
883 String nodeName = childNode.getNodeName();
884 if (nodeName.equals("show_grid")) {
885 showGrid = getValueAsBoolean(childNode);
886 }
887 else if (nodeName.equals("grid_step")) {
888 gridStep = getValueAsDouble(childNode);
889 }
890 else if (nodeName.equals("label_factor")) {
891 labelFactor = getValueAsInt(childNode);
892 }
893 }
894 rrdGraphDef.setDrawYGrid(showGrid);
895 if (!Double.isNaN(gridStep) && labelFactor != NOT_SET) {
896 rrdGraphDef.setValueAxis(gridStep, labelFactor);
897 }
898 else if (!Double.isNaN(gridStep) || labelFactor != NOT_SET) {
899 throw new RrdException("Incomplete value axis settings");
900 }
901 }
902
903 private void resolveTimeGrid(Node parentNode) throws RrdException {
904 validateTagsOnlyOnce(parentNode, new String[] {
905 "show_grid", "minor_grid_unit",
906 "minor_grid_unit_count", "major_grid_unit",
907 "major_grid_unit_count", "label_unit", "label_unit_count",
908 "label_span", "label_format"
909 });
910 boolean showGrid = true;
911 final int NOT_SET = Integer.MIN_VALUE;
912 int minorGridUnit = NOT_SET, minorGridUnitCount = NOT_SET,
913 majorGridUnit = NOT_SET, majorGridUnitCount = NOT_SET,
914 labelUnit = NOT_SET, labelUnitCount = NOT_SET, labelSpan = NOT_SET;
915 String labelFormat = null;
916 Node[] childNodes = getChildNodes(parentNode);
917 for (Node childNode : childNodes) {
918 String nodeName = childNode.getNodeName();
919 if (nodeName.equals("show_grid")) {
920 showGrid = getValueAsBoolean(childNode);
921 }
922 else if (nodeName.equals("minor_grid_unit")) {
923 minorGridUnit = resolveTimeUnit(getValue(childNode));
924 }
925 else if (nodeName.equals("minor_grid_unit_count")) {
926 minorGridUnitCount = getValueAsInt(childNode);
927 }
928 else if (nodeName.equals("major_grid_unit")) {
929 majorGridUnit = resolveTimeUnit(getValue(childNode));
930 }
931 else if (nodeName.equals("major_grid_unit_count")) {
932 majorGridUnitCount = getValueAsInt(childNode);
933 }
934 else if (nodeName.equals("label_unit")) {
935 labelUnit = resolveTimeUnit(getValue(childNode));
936 }
937 else if (nodeName.equals("label_unit_count")) {
938 labelUnitCount = getValueAsInt(childNode);
939 }
940 else if (nodeName.equals("label_span")) {
941 labelSpan = getValueAsInt(childNode);
942 }
943 else if (nodeName.equals("label_format")) {
944 labelFormat = getValue(childNode);
945 }
946 }
947 rrdGraphDef.setDrawXGrid(showGrid);
948 if (minorGridUnit != NOT_SET && minorGridUnitCount != NOT_SET &&
949 majorGridUnit != NOT_SET && majorGridUnitCount != NOT_SET &&
950 labelUnit != NOT_SET && labelUnitCount != NOT_SET && labelSpan != NOT_SET && labelFormat != null) {
951 rrdGraphDef.setTimeAxis(minorGridUnit, minorGridUnitCount, majorGridUnit, majorGridUnitCount,
952 labelUnit, labelUnitCount, labelSpan, labelFormat);
953 }
954 else if (minorGridUnit != NOT_SET || minorGridUnitCount != NOT_SET ||
955 majorGridUnit != NOT_SET || majorGridUnitCount != NOT_SET ||
956 labelUnit != NOT_SET || labelUnitCount != NOT_SET || labelSpan != NOT_SET || labelFormat != null) {
957 throw new RrdException("Incomplete time axis settings");
958 }
959 }
960
961 private int resolveTimeUnit(String unit) throws RrdException {
962 if (unit.equalsIgnoreCase("second")) {
963 return RrdGraphConstants.SECOND;
964 }
965 else if (unit.equalsIgnoreCase("minute")) {
966 return RrdGraphConstants.MINUTE;
967 }
968 else if (unit.equalsIgnoreCase("hour")) {
969 return RrdGraphConstants.HOUR;
970 }
971 else if (unit.equalsIgnoreCase("day")) {
972 return RrdGraphConstants.DAY;
973 }
974 else if (unit.equalsIgnoreCase("week")) {
975 return RrdGraphConstants.WEEK;
976 }
977 else if (unit.equalsIgnoreCase("month")) {
978 return RrdGraphConstants.MONTH;
979 }
980 else if (unit.equalsIgnoreCase("year")) {
981 return RrdGraphConstants.YEAR;
982 }
983 throw new RrdException("Unknown time unit specified: " + unit);
984 }
985 }