diff --git a/LICENSE.txt b/LICENSE.txt
index 0f0e5f953494058954e3b7c350f5782f630877b0..67a4d76b4847aeeed4538dc4ecf4f7d9d60a137e 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -264,13 +264,12 @@ Applications:
    See licenses/LICENSE-Apache2.0.txt
    See licenses/LICENSE-ECLIPSE-1.0.html
 
-   JRobin 1.6.0-1 (jrobin.jar):
+   RRD4J 3.5 (jrobin.jar):
    Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
    Copyright (c) 2011 The OpenNMS Group, Inc.
+   Copyright 2011 The RRD4J Authors.
+   See licenses/LICENSE-Apache2.0.txt
    See licenses/LICENSE-LGPLv2.1.txt
-   DeallocationHelper:
-   Copyright (c) 2006-2016 Julien Gouesse
-   See licenses/LICENSE-GPLv2.txt
 
    Ministreaming Lib (mstreaming.jar):
    By mihi.
diff --git a/apps/jrobin/java/src/engine/misc/DeallocationHelper.java b/apps/jrobin/java/src/engine/misc/DeallocationHelper.java
deleted file mode 100644
index 158b80b960a9fe4fc336ee1a10c718627052aa18..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/engine/misc/DeallocationHelper.java
+++ /dev/null
@@ -1,814 +0,0 @@
-/**
- * Copyright (c) 2006-2016 Julien Gouesse This program is free software; you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version. This program is distributed
- * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details. You should have received
- * a copy of the GNU General Public License along with this program; if not,
- * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-package engine.misc;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.CharBuffer;
-import java.nio.DoubleBuffer;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.nio.LongBuffer;
-import java.nio.ShortBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import net.i2p.I2PAppContext;
-import net.i2p.util.Log;
-
-
-/**
- * Helper to deallocate memory on the native heap allocated during the creation
- * of a direct byte buffer. It supports numerous virtual machines including
- * OpenJDK, Oracle/Sun Java, Android Dalvik Virtual Machine, Apache Harmony and
- * GNU Classpath. This class uses the syntax of Java 1.7 but it can work
- * correctly with Java 1.4 with a very few minor type changes when using the
- * maps and the collections. It relies on lots of implementation details but
- * it's robust enough to go on working (except when the implementors
- * intentionally use a very general class to store the buffers) despite minor
- * naming changes like those that occurred between Java 1.6 and Java 1.7. It
- * supports Java 1.9 despite the move of the cleaner from the package sun.misc
- * to jdk.internal.ref (in the module java.base). N.B: Releasing the native
- * memory of a sliced direct NIO buffer, the one of a direct NIO buffer created
- * with JNI or the one of any direct NIO buffer created by the virtual machine
- * or by a framework not under your control doesn't prevent the calls to methods
- * attempting to access such buffers. Those calls can throw an exception or
- * crash the virtual machine depending on the implementations.
- * 
- * @author Julien Gouesse
- */
-public class DeallocationHelper {
-
-    private final Log logger = I2PAppContext.getGlobalContext().logManager().getLog(DeallocationHelper.class);
-
-    /**
-     * tool responsible for releasing the native memory of a deallocatable byte
-     * buffer
-     */
-    public static abstract class Deallocator {
-
-        protected final Log logger = I2PAppContext.getGlobalContext().logManager().getLog(DeallocationHelper.class);
-
-        public Deallocator() {
-            super();
-        }
-
-        /**
-         * releases the native memory of a deallocatable byte buffer
-         * 
-         * @param directByteBuffer
-         *            deallocatable byte buffer
-         * 
-         * @return <code>true</code> if the deallocation is successful,
-         *         otherwise <code>false</code>
-         */
-        public abstract boolean run(final ByteBuffer directByteBuffer);
-    }
-
-    public static class OracleSunOpenJdkDeallocator extends Deallocator {
-
-        private Method directByteBufferCleanerMethod;
-
-        private Method cleanerCleanMethod;
-
-        public OracleSunOpenJdkDeallocator() {
-            super();
-            try {
-                final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
-                directByteBufferCleanerMethod = directByteBufferClass.getDeclaredMethod("cleaner");
-                /**
-                 * The return type is sun.misc.Cleaner in Java <= 1.8,
-                 * jdk.internal.ref.Cleaner in Java >= 1.9. Only the latter
-                 * implements the Runnable interface.
-                 */
-                final Class<?> cleanerClass = directByteBufferCleanerMethod.getReturnType();
-                if (Runnable.class.isAssignableFrom(cleanerClass)) {
-                    cleanerCleanMethod = Runnable.class.getDeclaredMethod("run");
-                } else {
-                    cleanerCleanMethod = cleanerClass.getDeclaredMethod("clean");
-                }
-            } catch (ClassNotFoundException | NoSuchMethodException e) {
-                logger.warn(
-                        "The initialization of the deallocator for Oracle Java, Sun Java and OpenJDK has failed", e);
-            }
-        }
-
-        @Override
-        public boolean run(final ByteBuffer directByteBuffer) {
-            boolean success = false;
-            if (directByteBufferCleanerMethod != null && cleanerCleanMethod != null) {
-                final boolean directByteBufferCleanerMethodWasAccessible = directByteBufferCleanerMethod.isAccessible();
-                final boolean cleanerCleanMethodWasAccessible = cleanerCleanMethod.isAccessible();
-                try {
-                    // according to the Java documentation, by default, a reflected object is not accessible
-                    directByteBufferCleanerMethod.setAccessible(true);
-                    final Object cleaner = directByteBufferCleanerMethod.invoke(directByteBuffer);
-                    if (cleaner != null) {
-                        cleanerCleanMethod.setAccessible(true);
-                        cleanerCleanMethod.invoke(cleaner);
-                        success = true;
-                    }
-                //} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                } catch (IllegalAccessException | RuntimeException | InvocationTargetException e) {
-                    // Replaced with RuntimeException for OpenJDK 9b181
-                    // throws a java.lang.reflect.InaccessibleObjectException extends RuntimeException which is only in Java 9
-                    // WARNING: An illegal reflective access operation has occurred
-                    // WARNING: Illegal reflective access by engine.misc.DeallocationHelper (file:/path/to/jrobin.jar) to field java.nio.DirectByteBuffer.att
-                    // WARNING: Please consider reporting this to the maintainers of engine.misc.DeallocationHelper
-                    // WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
-                    // WARNING: All illegal access operations will be denied in a future release
-                    // Thread terminated unexpectedly: Shutdown task net.i2p.router.web.StatSummarizer$Shutdown
-                    // java.lang.reflect.InaccessibleObjectException: Unable to make public void jdk.internal.ref.Cleaner.clean() accessible: module java.base does not "exports jdk.internal.ref" to unnamed module @381353a0
-                    // 	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
-                    // 	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
-                    // 	at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:198)
-                    // 	at java.base/java.lang.reflect.Method.setAccessible(Method.java:192)
-                    logger.warn("The deallocation of a direct NIO buffer has failed", e);
-                } finally {
-                    directByteBufferCleanerMethod.setAccessible(directByteBufferCleanerMethodWasAccessible);
-                    cleanerCleanMethod.setAccessible(cleanerCleanMethodWasAccessible);
-                }
-            }
-            return (success);
-        }
-    }
-
-    public static class AndroidDeallocator extends Deallocator {
-
-        private Method directByteBufferFreeMethod;
-
-        public AndroidDeallocator() {
-            super();
-            try {
-                final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
-                directByteBufferFreeMethod = directByteBufferClass.getDeclaredMethod("free");
-            } catch (ClassNotFoundException | NoSuchMethodException e) {
-                logger.warn("The initialization of the deallocator for Android has failed", e);
-            }
-        }
-
-        @Override
-        public boolean run(final ByteBuffer directByteBuffer) {
-            boolean success = false;
-            if (directByteBufferFreeMethod != null) {
-                final boolean directByteBufferFreeMethodWasAccessible = directByteBufferFreeMethod.isAccessible();
-                try {
-                    directByteBufferFreeMethod.setAccessible(true);
-                    directByteBufferFreeMethod.invoke(directByteBuffer);
-                    success = true;
-                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                    logger.warn("The deallocation of a direct NIO buffer has failed", e);
-                } finally {
-                    directByteBufferFreeMethod.setAccessible(directByteBufferFreeMethodWasAccessible);
-                }
-            }
-            return (success);
-        }
-    }
-
-    public static class GnuClasspathDeallocator extends Deallocator {
-
-        private Method vmDirectByteBufferFreeMethod;
-
-        private Field bufferAddressField;
-
-        public GnuClasspathDeallocator() {
-            super();
-            try {
-                final Class<?> vmDirectByteBufferClass = Class.forName("java.nio.VMDirectByteBuffer");
-                final Class<?> gnuClasspathPointerClass = Class.forName("gnu.classpath.Pointer");
-                vmDirectByteBufferFreeMethod = vmDirectByteBufferClass.getDeclaredMethod("free",
-                        gnuClasspathPointerClass);
-                bufferAddressField = Buffer.class.getDeclaredField("address");
-            } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException e) {
-                logger.warn("The initialization of the deallocator for GNU Classpath has failed", e);
-            }
-        }
-
-        @Override
-        public boolean run(final ByteBuffer directByteBuffer) {
-            boolean success = false;
-            if (vmDirectByteBufferFreeMethod != null && bufferAddressField != null) {
-                final boolean bufferAddressFieldWasAccessible = bufferAddressField.isAccessible();
-                final boolean vmDirectByteBufferFreeMethodWasAccessible = vmDirectByteBufferFreeMethod.isAccessible();
-                try {
-                    bufferAddressField.setAccessible(true);
-                    final Object address = bufferAddressField.get(directByteBuffer);
-                    if (address != null) {
-                        vmDirectByteBufferFreeMethod.setAccessible(true);
-                        vmDirectByteBufferFreeMethod.invoke(null, address);
-                        success = true;
-                    }
-                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                    logger.warn("The deallocation of a direct NIO buffer has failed", e);
-                } finally {
-                    bufferAddressField.setAccessible(bufferAddressFieldWasAccessible);
-                    vmDirectByteBufferFreeMethod.setAccessible(vmDirectByteBufferFreeMethodWasAccessible);
-                }
-            }
-            return (success);
-        }
-    }
-
-    public static class ApacheHarmonyDeallocator extends Deallocator {
-
-        private Method directByteBufferFreeMethod;
-
-        public ApacheHarmonyDeallocator() {
-            super();
-            try {
-                final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
-                directByteBufferFreeMethod = directByteBufferClass.getDeclaredMethod("free");
-            } catch (ClassNotFoundException | NoSuchMethodException e) {
-                logger.warn("The initialization of the deallocator for Apache Harmony has failed", e);
-            }
-        }
-
-        @Override
-        public boolean run(final ByteBuffer directByteBuffer) {
-            boolean success = false;
-            if (directByteBufferFreeMethod != null) {
-                final boolean directByteBufferFreeMethodWasAccessible = directByteBufferFreeMethod.isAccessible();
-                try {
-                    directByteBufferFreeMethod.setAccessible(true);
-                    directByteBufferFreeMethod.invoke(directByteBuffer);
-                    success = true;
-                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                    logger.warn("The deallocation of a direct NIO buffer has failed", e);
-                } finally {
-                    directByteBufferFreeMethod.setAccessible(directByteBufferFreeMethodWasAccessible);
-                }
-            }
-            return (success);
-        }
-    }
-
-    private Map<Class<?>, Field> attachmentOrByteBufferFieldMap;
-
-    private Set<Class<?>> deallocatableBufferClassSet;
-
-    private Deallocator deallocator;
-
-    /**
-     * Default constructor
-     */
-    public DeallocationHelper() {
-        this(false);
-    }
-
-    /**
-     * Main constructor
-     * 
-     * @param ignoreClassesAndFieldsHints
-     *            <code>true</code> if the known implementation details should
-     *            be ignored when looking for the classes and the fields used
-     *            for the native memory of the direct buffers (they are then
-     *            fully recomputed at runtime which is slower but safer),
-     *            otherwise <code>false</code>
-     */
-    public DeallocationHelper(final boolean ignoreClassesAndFieldsHints) {
-        super();
-        final List<Buffer> buffersToDelete = new ArrayList<>();
-        /**
-         * builds the map used to determine the names of the fields containing
-         * the direct byte buffers. The direct read only buffers and the sliced
-         * buffers and the direct buffers for other primitive types than bytes
-         * store their data into some direct byte buffers. Those direct byte
-         * buffers often are the only one accessing directly to the native
-         * memory. That's why it's necessary to find them when a developer
-         * passes a direct NIO buffer. The code below relies on numerous
-         * implementation details found in some classes not available in the
-         * public APIs, it's used to find the fields faster in most of the
-         * cases. The class names haven't changed since Java 1.4 unlike a few
-         * field names.
-         */
-        final Map<String, String> attachmentOrByteBufferFieldNameMap = new HashMap<>();
-        final String javaVendor = System.getProperty("java.vendor");
-        final String javaVersion = System.getProperty("java.version");
-        if (!ignoreClassesAndFieldsHints) {
-            if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation")) {
-                final String java14to16DirectBufferAttachmentFieldName = "viewedBuffer";
-                final String java17to19DirectBufferAttachmentFieldName = "att";
-                final String byteBufferAsNonByteBufferByteBufferFieldName = "bb";
-                final String[] directBufferClassnames = new String[] { "java.nio.DirectByteBuffer",
-                        "java.nio.DirectByteBufferR", "java.nio.DirectCharBufferRS", "java.nio.DirectCharBufferRU",
-                        "java.nio.DirectCharBufferS", "java.nio.DirectCharBufferU", "java.nio.DirectDoubleBufferRS",
-                        "java.nio.DirectDoubleBufferRU", "java.nio.DirectDoubleBufferS", "java.nio.DirectDoubleBufferU",
-                        "java.nio.DirectFloatBufferRS", "java.nio.DirectFloatBufferRU", "java.nio.DirectFloatBufferS",
-                        "java.nio.DirectFloatBufferU", "java.nio.DirectIntBufferRS", "java.nio.DirectIntBufferRU",
-                        "java.nio.DirectIntBufferS", "java.nio.DirectIntBufferU", "java.nio.DirectLongBufferRS",
-                        "java.nio.DirectLongBufferRU", "java.nio.DirectLongBufferS", "java.nio.DirectLongBufferU",
-                        "java.nio.DirectShortBufferRS", "java.nio.DirectShortBufferRU", "java.nio.DirectShortBufferS",
-                        "java.nio.DirectShortBufferU" };
-                final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.ByteBufferAsCharBufferB",
-                        "java.nio.ByteBufferAsCharBufferL", "java.nio.ByteBufferAsCharBufferRB",
-                        "java.nio.ByteBufferAsCharBufferRL", "java.nio.ByteBufferAsDoubleBufferB",
-                        "java.nio.ByteBufferAsDoubleBufferL", "java.nio.ByteBufferAsDoubleBufferRB",
-                        "java.nio.ByteBufferAsDoubleBufferRL", "java.nio.ByteBufferAsFloatBufferB",
-                        "java.nio.ByteBufferAsFloatBufferL", "java.nio.ByteBufferAsFloatBufferRB",
-                        "java.nio.ByteBufferAsFloatBufferRL", "java.nio.ByteBufferAsIntBufferB",
-                        "java.nio.ByteBufferAsIntBufferL", "java.nio.ByteBufferAsIntBufferRB",
-                        "java.nio.ByteBufferAsIntBufferRL", "java.nio.ByteBufferAsLongBufferB",
-                        "java.nio.ByteBufferAsLongBufferL", "java.nio.ByteBufferAsLongBufferRB",
-                        "java.nio.ByteBufferAsLongBufferRL", "java.nio.ByteBufferAsShortBufferB",
-                        "java.nio.ByteBufferAsShortBufferL", "java.nio.ByteBufferAsShortBufferRB",
-                        "java.nio.ByteBufferAsShortBufferRL" };
-                final String[] javaVersionElements = System.getProperty("java.version").split("\\.");
-                int indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-ea");
-                if (indexOfEarlyAccessSuffix != -1) {
-                    // drops the "-ea" suffix from the major version number for
-                    // an early access build
-                    javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix);
-                } else {
-                    indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-internal");
-                    if (indexOfEarlyAccessSuffix != -1) {
-                        // drops the "-internal" suffix from the major version number for
-                        // an early access build (Ubuntu)
-                        javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix);
-                    } else {
-                        indexOfEarlyAccessSuffix = javaVersionElements[0].lastIndexOf("-Ubuntu");
-                        if (indexOfEarlyAccessSuffix != -1) {
-                            // drops the "-Ubuntu suffix from the major version number for
-                            // an early access build (Ubuntu)
-                            javaVersionElements[0] = javaVersionElements[0].substring(0, indexOfEarlyAccessSuffix);
-                        }
-                    }
-                }
-                final int major, minor;
-                if (javaVersionElements.length >= 2) {
-                    major = Integer.parseInt(javaVersionElements[0]);
-                    int min;
-                    try {
-                        min = Integer.parseInt(javaVersionElements[1]);
-                    } catch (NumberFormatException nfe) {
-                        min = 7;
-                    }
-                    minor = min;
-                } else {
-                    major = 1;
-                    int min;
-                    try {
-                        min = Integer.parseInt(javaVersionElements[0]);
-                    } catch (NumberFormatException nfe) {
-                        min = 7;
-                    }
-                    minor = min;
-                }
-                final String directBufferAttachmentFieldName;
-                if (minor == 1 && major <= 6)
-                    directBufferAttachmentFieldName = java14to16DirectBufferAttachmentFieldName;
-                else
-                    directBufferAttachmentFieldName = java17to19DirectBufferAttachmentFieldName;
-                for (final String directBufferClassname : directBufferClassnames)
-                    attachmentOrByteBufferFieldNameMap.put(directBufferClassname, directBufferAttachmentFieldName);
-                for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames)
-                    attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname,
-                            byteBufferAsNonByteBufferByteBufferFieldName);
-            } else if (javaVendor.equals("The Android Project")) {
-                final String byteBufferAsNonByteBufferByteBufferFieldName = "byteBuffer";
-                final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.ByteBufferAsCharBuffer",
-                        "java.nio.ByteBufferAsDoubleBuffer", "java.nio.ByteBufferAsFloatBuffer",
-                        "java.nio.ByteBufferAsIntBuffer", "java.nio.ByteBufferAsLongBuffer",
-                        "java.nio.ByteBufferAsShortBuffer" };
-                for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames)
-                    attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname,
-                            byteBufferAsNonByteBufferByteBufferFieldName);
-            } else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc.")) {
-                final String byteBufferAsNonByteBufferByteBufferFieldName = "bb";
-                final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.CharViewBufferImpl",
-                        "java.nio.DoubleViewBufferImpl", "java.nio.FloatViewBufferImpl", "java.nio.IntViewBufferImpl",
-                        "java.nio.LongViewBufferImpl", "java.nio.ShortViewBufferImpl" };
-                for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames)
-                    attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname,
-                            byteBufferAsNonByteBufferByteBufferFieldName);
-            } else if (javaVendor.contains("Apache")) {
-                final String byteBufferAsNonByteBufferByteBufferFieldName = "byteBuffer";
-                final String[] byteBufferAsNonByteBufferClassnames = new String[] { "java.nio.CharToByteBufferAdapter",
-                        "java.nio.DoubleToByteBufferAdapter", "java.nio.FloatToByteBufferAdapter",
-                        "java.nio.IntToByteBufferAdapter", "java.nio.LongToByteBufferAdapter",
-                        "java.nio.ShortToByteBufferAdapter" };
-                for (final String byteBufferAsNonByteBufferClassname : byteBufferAsNonByteBufferClassnames)
-                    attachmentOrByteBufferFieldNameMap.put(byteBufferAsNonByteBufferClassname,
-                            byteBufferAsNonByteBufferByteBufferFieldName);
-            } else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM
-            } else if (javaVendor.contains("IBM")) {// TODO J9
-            }
-        }
-        // checks if these classes are in the class library
-        if (!attachmentOrByteBufferFieldNameMap.isEmpty()) {
-            final List<String> classnamesToRemove = new ArrayList<>();
-            for (final String classname : attachmentOrByteBufferFieldNameMap.keySet())
-                try {
-                    Class.forName(classname);
-                } catch (ClassNotFoundException cnfe) {
-                    classnamesToRemove.add(classname);
-                }
-            for (final String classnameToRemove : classnamesToRemove)
-                attachmentOrByteBufferFieldNameMap.remove(classnameToRemove);
-        }
-        // builds the map used to determine the fields containing the direct
-        // byte buffers
-        attachmentOrByteBufferFieldMap = new HashMap<>();
-        if (!attachmentOrByteBufferFieldNameMap.isEmpty())
-            for (final Entry<String, String> attachmentOrByteBufferFieldNameEntry : attachmentOrByteBufferFieldNameMap
-                    .entrySet()) {
-                final String classname = attachmentOrByteBufferFieldNameEntry.getKey();
-                final String fieldname = attachmentOrByteBufferFieldNameEntry.getValue();
-                try {
-                    final Class<?> bufferClass = Class.forName(classname);
-                    Field bufferField = null;
-                    Class<?> bufferIntermediaryClass = bufferClass;
-                    final List<Class<?>> intermediaryClassWithoutBufferList = new ArrayList<>();
-                    while (bufferIntermediaryClass != null) {
-                        try {
-                            bufferField = bufferIntermediaryClass.getDeclaredField(fieldname);
-                        } catch (NoSuchFieldException nsfe) {
-                            if (!bufferIntermediaryClass.equals(Object.class)
-                                    && !bufferIntermediaryClass.equals(Buffer.class))
-                                intermediaryClassWithoutBufferList.add(bufferIntermediaryClass);
-                        }
-                        bufferIntermediaryClass = bufferIntermediaryClass.getSuperclass();
-                    }
-                    if (bufferField == null) {
-                        final String superClassesMsg;
-                        if (intermediaryClassWithoutBufferList.isEmpty())
-                            superClassesMsg = "";
-                        else if (intermediaryClassWithoutBufferList.size() == 1)
-                            superClassesMsg = " and in its super class "
-                                    + intermediaryClassWithoutBufferList.get(0).getName();
-                        else {
-                            final StringBuilder builder = new StringBuilder();
-                            builder.append(" and in its super classes");
-                            int classIndex = 0;
-                            for (final Class<?> intermediaryClassWithoutBuffer : intermediaryClassWithoutBufferList) {
-                                builder.append(' ');
-                                builder.append(intermediaryClassWithoutBuffer.getName());
-                                if (classIndex < intermediaryClassWithoutBufferList.size() - 1)
-                                    builder.append(',');
-                                classIndex++;
-                            }
-                            superClassesMsg = builder.toString();
-                        }
-                        logger.warn("The field " + fieldname + " hasn't been found in the class " + classname
-                                + superClassesMsg);
-                    } else {// the field has been found, stores it into the map
-                        attachmentOrByteBufferFieldMap.put(bufferClass, bufferField);
-                    }
-                } catch (ClassNotFoundException cnfe) {// TODO The Java version
-                                                       // isn't very useful
-                                                       // under
-                                                       // Android as it is
-                                                       // always zero, rather
-                                                       // use
-                                                       // android.os.Build.VERSION.RELEASE
-                                                       // to show something
-                                                       // meaningful supported
-                                                       // since the API level 1
-                    final String msg = "The class " + classname
-                            + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
-                            + " Java version: " + javaVersion;
-                    logger.warn(msg, cnfe);
-                }
-            }
-        // if a known implementation has drastically changed or if the current
-        // implementation is unknown
-        if (attachmentOrByteBufferFieldNameMap.isEmpty()) {// detects everything
-                                                           // with the
-                                                           // reflection API
-                                                           // creates all
-                                                           // possible kinds of
-                                                           // direct NIO buffer
-                                                           // that can contain
-                                                           // buffers (sliced
-                                                           // buffers and views)
-            final ByteBuffer slicedBigEndianReadOnlyDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2)
-                    .order(ByteOrder.BIG_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice()
-                            .asReadOnlyBuffer();
-            final ByteBuffer slicedBigEndianReadWriteDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2)
-                    .order(ByteOrder.BIG_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice();
-            final CharBuffer bigEndianReadOnlyDirectCharBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asCharBuffer();
-            final CharBuffer bigEndianReadWriteDirectCharBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asCharBuffer();
-            final DoubleBuffer bigEndianReadOnlyDirectDoubleBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asDoubleBuffer();
-            final DoubleBuffer bigEndianReadWriteDirectDoubleBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asDoubleBuffer();
-            final FloatBuffer bigEndianReadOnlyDirectFloatBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asFloatBuffer();
-            final FloatBuffer bigEndianReadWriteDirectFloatBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asFloatBuffer();
-            final IntBuffer bigEndianReadOnlyDirectIntBuffer = ByteBuffer.allocateDirect(1).order(ByteOrder.BIG_ENDIAN)
-                    .asReadOnlyBuffer().asIntBuffer();
-            final IntBuffer bigEndianReadWriteDirectIntBuffer = ByteBuffer.allocateDirect(1).order(ByteOrder.BIG_ENDIAN)
-                    .asIntBuffer();
-            final LongBuffer bigEndianReadOnlyDirectLongBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asLongBuffer();
-            final LongBuffer bigEndianReadWriteDirectLongBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asLongBuffer();
-            final ShortBuffer bigEndianReadOnlyDirectShortBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asReadOnlyBuffer().asShortBuffer();
-            final ShortBuffer bigEndianReadWriteDirectShortBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.BIG_ENDIAN).asShortBuffer();
-            final ByteBuffer slicedLittleEndianReadOnlyDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2)
-                    .order(ByteOrder.LITTLE_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice()
-                            .asReadOnlyBuffer();
-            final ByteBuffer slicedLittleEndianReadWriteDirectByteBuffer = ((ByteBuffer) ByteBuffer.allocateDirect(2)
-                    .order(ByteOrder.LITTLE_ENDIAN).put((byte) 0).put((byte) 0).position(1).limit(2)).slice();
-            final CharBuffer littleEndianReadOnlyDirectCharBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asCharBuffer();
-            final CharBuffer littleEndianReadWriteDirectCharBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asCharBuffer();
-            final DoubleBuffer littleEndianReadOnlyDirectDoubleBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asDoubleBuffer();
-            final DoubleBuffer littleEndianReadWriteDirectDoubleBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer();
-            final FloatBuffer littleEndianReadOnlyDirectFloatBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asFloatBuffer();
-            final FloatBuffer littleEndianReadWriteDirectFloatBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer();
-            final IntBuffer littleEndianReadOnlyDirectIntBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asIntBuffer();
-            final IntBuffer littleEndianReadWriteDirectIntBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
-            final LongBuffer littleEndianReadOnlyDirectLongBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asLongBuffer();
-            final LongBuffer littleEndianReadWriteDirectLongBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asLongBuffer();
-            final ShortBuffer littleEndianReadOnlyDirectShortBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().asShortBuffer();
-            final ShortBuffer littleEndianReadWriteDirectShortBuffer = ByteBuffer.allocateDirect(1)
-                    .order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
-            final List<Buffer> buffers = new ArrayList<>();
-            buffers.add(slicedBigEndianReadOnlyDirectByteBuffer);
-            buffers.add(slicedBigEndianReadWriteDirectByteBuffer);
-            buffers.add(bigEndianReadOnlyDirectCharBuffer);
-            buffers.add(bigEndianReadWriteDirectCharBuffer);
-            buffers.add(bigEndianReadOnlyDirectDoubleBuffer);
-            buffers.add(bigEndianReadWriteDirectDoubleBuffer);
-            buffers.add(bigEndianReadOnlyDirectFloatBuffer);
-            buffers.add(bigEndianReadWriteDirectFloatBuffer);
-            buffers.add(bigEndianReadOnlyDirectIntBuffer);
-            buffers.add(bigEndianReadWriteDirectIntBuffer);
-            buffers.add(bigEndianReadOnlyDirectLongBuffer);
-            buffers.add(bigEndianReadWriteDirectLongBuffer);
-            buffers.add(bigEndianReadOnlyDirectShortBuffer);
-            buffers.add(bigEndianReadWriteDirectShortBuffer);
-            buffers.add(slicedLittleEndianReadOnlyDirectByteBuffer);
-            buffers.add(slicedLittleEndianReadWriteDirectByteBuffer);
-            buffers.add(littleEndianReadOnlyDirectCharBuffer);
-            buffers.add(littleEndianReadWriteDirectCharBuffer);
-            buffers.add(littleEndianReadOnlyDirectDoubleBuffer);
-            buffers.add(littleEndianReadWriteDirectDoubleBuffer);
-            buffers.add(littleEndianReadOnlyDirectFloatBuffer);
-            buffers.add(littleEndianReadWriteDirectFloatBuffer);
-            buffers.add(littleEndianReadOnlyDirectIntBuffer);
-            buffers.add(littleEndianReadWriteDirectIntBuffer);
-            buffers.add(littleEndianReadOnlyDirectLongBuffer);
-            buffers.add(littleEndianReadWriteDirectLongBuffer);
-            buffers.add(littleEndianReadOnlyDirectShortBuffer);
-            buffers.add(littleEndianReadWriteDirectShortBuffer);
-            // gets the fields to access the contained buffers
-            for (Buffer buffer : buffers) {
-                final Class<?> bufferClass = buffer.getClass();
-                if (!attachmentOrByteBufferFieldMap.containsKey(bufferClass)) {
-                    Field bufferField = null;
-                    Class<?> bufferIntermediaryClass = bufferClass;
-                    while (bufferIntermediaryClass != null && bufferField == null) {
-                        for (final Field field : bufferIntermediaryClass.getDeclaredFields()) {
-                            final boolean fieldWasAccessible = field.isAccessible();
-                            try {
-                                field.setAccessible(true);
-                                final Object fieldValue = field.get(buffer);
-                                if (fieldValue != null && fieldValue instanceof Buffer) {
-                                    bufferField = field;
-                                    break;
-                                }
-                            } catch (IllegalAccessException iae) {
-                                logger.warn("Cannot access the field " + field.getName()
-                                        + " of the class " + bufferIntermediaryClass.getName(), iae);
-                            } finally {
-                                field.setAccessible(fieldWasAccessible);
-                            }
-                        }
-                        bufferIntermediaryClass = bufferIntermediaryClass.getSuperclass();
-                    }
-                    if (bufferField != null)
-                        attachmentOrByteBufferFieldMap.put(bufferClass, bufferField);
-                }
-            }
-            // cleans the mess
-            buffersToDelete.addAll(buffers);
-        }
-        // builds the set of classes whose instances can be deallocated
-        deallocatableBufferClassSet = new HashSet<>();
-        if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation")
-                || javaVendor.equals("The Android Project")) {
-            Class<?> directByteBufferClass = null;
-            final String directByteBufferClassName = "java.nio.DirectByteBuffer";
-            try {
-                directByteBufferClass = Class.forName(directByteBufferClassName);
-            } catch (ClassNotFoundException cnfe) {
-                final String msg = "The class " + directByteBufferClassName
-                        + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
-                        + " Java version: " + javaVersion;
-                logger.warn(msg, cnfe);
-            }
-            if (directByteBufferClass != null)
-                deallocatableBufferClassSet.add(directByteBufferClass);
-        } else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc.")) {
-            Class<?> readOnlyDirectByteBufferClass = null;
-            final String readOnlyDirectByteBufferClassName = "java.nio.DirectByteBufferImpl.ReadOnly";
-            try {
-                readOnlyDirectByteBufferClass = Class.forName(readOnlyDirectByteBufferClassName);
-            } catch (ClassNotFoundException cnfe) {
-                final String msg = "The class " + readOnlyDirectByteBufferClassName
-                        + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
-                        + " Java version: " + javaVersion;
-                logger.warn(msg, cnfe);
-            }
-            if (readOnlyDirectByteBufferClass != null)
-                deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass);
-            Class<?> readWriteDirectByteBufferClass = null;
-            final String readWriteDirectByteBufferClassName = "java.nio.DirectByteBufferImpl.ReadWrite";
-            try {
-                readWriteDirectByteBufferClass = Class.forName(readWriteDirectByteBufferClassName);
-            } catch (ClassNotFoundException cnfe) {
-                final String msg = "The class " + readWriteDirectByteBufferClassName
-                        + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
-                        + " Java version: " + javaVersion;
-                logger.warn(msg, cnfe);
-            }
-            if (readWriteDirectByteBufferClass != null)
-                deallocatableBufferClassSet.add(readWriteDirectByteBufferClass);
-        } else if (javaVendor.contains("Apache")) {
-            Class<?> readOnlyDirectByteBufferClass = null;
-            final String readOnlyDirectByteBufferClassName = "java.nio.ReadOnlyDirectByteBuffer";
-            try {
-                readOnlyDirectByteBufferClass = Class.forName(readOnlyDirectByteBufferClassName);
-            } catch (ClassNotFoundException cnfe) {
-                final String msg = "The class " + readOnlyDirectByteBufferClassName
-                        + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
-                        + " Java version: " + javaVersion;
-                logger.warn(msg, cnfe);
-            }
-            if (readOnlyDirectByteBufferClass != null)
-                deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass);
-            Class<?> readWriteDirectByteBufferClass = null;
-            final String readWriteDirectByteBufferClassName = "java.nio.ReadWriteDirectByteBuffer";
-            try {
-                readWriteDirectByteBufferClass = Class.forName(readWriteDirectByteBufferClassName);
-            } catch (ClassNotFoundException cnfe) {
-                final String msg = "The class " + readWriteDirectByteBufferClassName
-                        + " hasn't been found while initializing the deallocator. Java vendor: " + javaVendor
-                        + " Java version: " + javaVersion;
-                logger.warn(msg, cnfe);
-            }
-            if (readWriteDirectByteBufferClass != null)
-                deallocatableBufferClassSet.add(readWriteDirectByteBufferClass);
-        } else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM
-        } else if (javaVendor.contains("IBM")) {// TODO J9
-        }
-        // if there is no known implementation class of the direct byte buffers
-        if (deallocatableBufferClassSet.isEmpty()) {// creates a read write
-                                                    // direct byte buffer
-            final ByteBuffer dummyReadWriteDirectByteBuffer = ByteBuffer.allocateDirect(1);
-            // gets its class
-            final Class<?> readWriteDirectByteBufferClass = dummyReadWriteDirectByteBuffer.getClass();
-            // stores this class
-            deallocatableBufferClassSet.add(readWriteDirectByteBufferClass);
-            // cleans the mess
-            buffersToDelete.add(dummyReadWriteDirectByteBuffer);
-            // creates a read only direct byte buffer
-            final ByteBuffer dummyReadOnlyDirectByteBuffer = ByteBuffer.allocateDirect(1).asReadOnlyBuffer();
-            // gets its class
-            final Class<?> readOnlyDirectByteBufferClass = dummyReadOnlyDirectByteBuffer.getClass();
-            // stores this class
-            deallocatableBufferClassSet.add(readOnlyDirectByteBufferClass);
-            // cleans the mess
-            buffersToDelete.add(dummyReadOnlyDirectByteBuffer);
-        }
-        // builds the deallocator responsible for releasing the native memory of
-        // a deallocatable byte buffer
-        if (javaVendor.equals("Sun Microsystems Inc.") || javaVendor.equals("Oracle Corporation"))
-            deallocator = new OracleSunOpenJdkDeallocator();
-        else if (javaVendor.equals("The Android Project"))
-            deallocator = new AndroidDeallocator();
-        else if (/* javaVendor.equals("Apple Inc.")|| */javaVendor.equals("Free Software Foundation, Inc."))
-            deallocator = new GnuClasspathDeallocator();
-        else if (javaVendor.contains("Apache"))
-            deallocator = new ApacheHarmonyDeallocator();
-        else if (javaVendor.equals("Jeroen Frijters")) {// TODO IKVM
-            deallocator = null;
-        } else if (javaVendor.contains("IBM")) {// TODO J9
-            deallocator = null;
-        } else
-            deallocator = null;
-        // final cleanup
-        for (final Buffer bufferToDelete : buffersToDelete)
-            deallocate(bufferToDelete);
-    }
-
-    public ByteBuffer findDeallocatableBuffer(Buffer buffer) {
-        final ByteBuffer deallocatableDirectByteBuffer;
-        // looks only for the direct buffers
-        if (buffer != null && buffer.isDirect()) {// looks for any contained
-                                                  // buffer in the passed buffer
-            final Class<?> bufferClass = buffer.getClass();
-            final Field attachmentOrByteBufferField = attachmentOrByteBufferFieldMap == null ? null
-                    : attachmentOrByteBufferFieldMap.get(bufferClass);
-            final Buffer attachmentBufferOrByteBuffer;
-            if (attachmentOrByteBufferField == null)
-                attachmentBufferOrByteBuffer = null;
-            else {
-                Object attachedObjectOrByteBuffer;
-                final boolean attachedObjectOrByteBufferFieldWasAccessible = attachmentOrByteBufferField.isAccessible();
-                try {
-                    attachmentOrByteBufferField.setAccessible(true);
-                    attachedObjectOrByteBuffer = attachmentOrByteBufferField.get(buffer);
-                } catch (IllegalArgumentException | IllegalAccessException iae) {
-                    attachedObjectOrByteBuffer = null;
-                } finally {
-                    attachmentOrByteBufferField.setAccessible(attachedObjectOrByteBufferFieldWasAccessible);
-                }
-                if (attachedObjectOrByteBuffer instanceof Buffer)
-                    attachmentBufferOrByteBuffer = (Buffer) attachedObjectOrByteBuffer;
-                else
-                    attachmentBufferOrByteBuffer = null;
-            }
-            // if there is no buffer inside the buffer given in input
-            if (attachmentBufferOrByteBuffer == null) {// if it's a direct byte
-                                                       // buffer and if it's an
-                                                       // instance of
-                                                       // a deallocatable buffer
-                                                       // class
-                if (buffer instanceof ByteBuffer && deallocatableBufferClassSet.contains(bufferClass))
-                    deallocatableDirectByteBuffer = (ByteBuffer) buffer;
-                else {// it's not a byte buffer or it's not a
-                      // deallocatable buffer
-                    deallocatableDirectByteBuffer = null;
-                    final String bufferClassName = bufferClass.getName();
-                    logger.warn("No deallocatable buffer has been found for an instance of the class "
-                            + bufferClassName + " whereas it is a direct NIO buffer");
-                }
-            } else {// the passed buffer contains another buffer, looks for a
-                    // deallocatable buffer inside it
-                deallocatableDirectByteBuffer = findDeallocatableBuffer(attachmentBufferOrByteBuffer);
-            }
-        } else {// there is no need to clean the heap based buffers
-            deallocatableDirectByteBuffer = null;
-        }
-        return deallocatableDirectByteBuffer;
-    }
-
-    public void deallocate(final Buffer buffer) {
-        if (deallocator != null) {
-            final ByteBuffer deallocatableBuffer = findDeallocatableBuffer(buffer);
-            if (deallocatableBuffer != null)
-                deallocator.run(deallocatableBuffer);
-        }
-    }
-
-    public Deallocator getDeallocator() {
-        return (deallocator);
-    }
-
-    public void setDeallocator(Deallocator deallocator) {
-        this.deallocator = deallocator;
-    }
-
-    public Map<Class<?>, Field> getAttachmentOrByteBufferFieldMap() {
-        return (attachmentOrByteBufferFieldMap);
-    }
-
-    public void setAttachmentOrByteBufferFieldMap(Map<Class<?>, Field> attachmentOrByteBufferFieldMap) {
-        this.attachmentOrByteBufferFieldMap = attachmentOrByteBufferFieldMap;
-    }
-
-    public Set<Class<?>> getDeallocatableBufferClassSet() {
-        return (deallocatableBufferClassSet);
-    }
-
-    public void setDeallocatableBufferClassSet(Set<Class<?>> deallocatableBufferClassSet) {
-        this.deallocatableBufferClassSet = deallocatableBufferClassSet;
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/ArcDef.java b/apps/jrobin/java/src/org/jrobin/core/ArcDef.java
deleted file mode 100644
index 0c420193a2e1545069c0890797c948099662db88..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/ArcDef.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-/**
- * Class to represent single archive definition within the RRD.
- * Archive definition consists of the following four elements:
- * <p>
- * <ul>
- * <li>consolidation function
- * <li>X-files factor
- * <li>number of steps
- * <li>number of rows.
- * </ul>
- * <p>
- * For the complete explanation of all archive definition parameters, see RRDTool's
- * <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-
-public class ArcDef implements ConsolFuns {
-	/**
-	 * array of valid consolidation function names
-	 */
-	public static final String CONSOL_FUNS[] = {CF_AVERAGE, CF_MAX, CF_MIN, CF_LAST};
-
-	private String consolFun;
-	private double xff;
-	private int steps, rows;
-
-	/**
-	 * Creates new archive definition object. This object should be passed as argument to
-	 * {@link RrdDef#addArchive(ArcDef) addArchive()} method of
-	 * {@link RrdDb RrdDb} object.
-	 * <p>
-	 * <p>For the complete explanation of all archive definition parameters, see RRDTool's
-	 * <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a></p>
-	 *
-	 * @param consolFun Consolidation function. Allowed values are "AVERAGE", "MIN",
-	 *                  "MAX" and "LAST" (these string constants are conveniently defined in the
-	 *                  {@link ConsolFuns} class).
-	 * @param xff	   X-files factor, between 0 and 1.
-	 * @param steps	 Number of archive steps.
-	 * @param rows	  Number of archive rows.
-	 * @throws RrdException Thrown if any parameter has illegal value.
-	 */
-	public ArcDef(final String consolFun, final double xff, final int steps, final int rows) throws RrdException {
-		this.consolFun = consolFun;
-		this.xff = xff;
-		this.steps = steps;
-		this.rows = rows;
-		validate();
-	}
-
-	/**
-	 * Returns consolidation function.
-	 *
-	 * @return Consolidation function.
-	 */
-	public String getConsolFun() {
-		return consolFun;
-	}
-
-	/**
-	 * Returns the X-files factor.
-	 *
-	 * @return X-files factor value.
-	 */
-	public double getXff() {
-		return xff;
-	}
-
-	/**
-	 * Returns the number of primary RRD steps which complete a single archive step.
-	 *
-	 * @return Number of steps.
-	 */
-	public int getSteps() {
-		return steps;
-	}
-
-	/**
-	 * Returns the number of rows (aggregated values) stored in the archive.
-	 *
-	 * @return Number of rows.
-	 */
-	public int getRows() {
-		return rows;
-	}
-
-	private void validate() throws RrdException {
-		if (!isValidConsolFun(consolFun)) {
-			throw new RrdException("Invalid consolidation function specified: " + consolFun);
-		}
-		if (Double.isNaN(xff) || xff < 0.0 || xff >= 1.0) {
-			throw new RrdException("Invalid xff, must be >= 0 and < 1: " + xff);
-		}
-		if (steps < 1 || rows < 2) {
-			throw new RrdException("Invalid steps/rows settings: " + steps + "/" + rows +
-					". Minimal values allowed are steps=1, rows=2");
-		}
-	}
-
-	/**
-	 * Returns string representing archive definition (RRDTool format).
-	 *
-	 * @return String containing all archive definition parameters.
-	 */
-	public String dump() {
-		return "RRA:" + consolFun + ":" + xff + ":" + steps + ":" + rows;
-	}
-
-	/**
-	 * Checks if two archive definitions are equal.
-	 * Archive definitions are considered equal if they have the same number of steps
-	 * and the same consolidation function. It is not possible to create RRD with two
-	 * equal archive definitions.
-	 *
-	 * @param obj Archive definition to compare with.
-	 * @return <code>true</code> if archive definitions are equal,
-	 *         <code>false</code> otherwise.
-	 */
-	public boolean equals(final Object obj) {
-		if (obj instanceof ArcDef) {
-			final ArcDef arcObj = (ArcDef) obj;
-			return consolFun.equals(arcObj.consolFun) && steps == arcObj.steps;
-		}
-		return false;
-	}
-
-	public int hashCode() {
-		return (consolFun.hashCode() + steps) * 53;
-	}
-
-	/**
-	 * Checks if function argument represents valid consolidation function name.
-	 *
-	 * @param consolFun Consolidation function to be checked
-	 * @return <code>true</code> if <code>consolFun</code> is valid consolidation function,
-	 *         <code>false</code> otherwise.
-	 */
-	public static boolean isValidConsolFun(final String consolFun) {
-		for (final String cFun : CONSOL_FUNS) {
-			if (cFun.equals(consolFun)) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	void setRows(final int rows) {
-		this.rows = rows;
-	}
-
-	boolean exactlyEqual(final ArcDef def) {
-		return consolFun.equals(def.consolFun) && xff == def.xff &&
-				steps == def.steps && rows == def.rows;
-	}
-
-	public String toString() {
-	    return "ArcDef@" + Integer.toHexString(hashCode()) + "[consolFun=" + consolFun + ",xff=" + xff + ",steps=" + steps + ",rows=" + rows + "]";
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/ArcState.java b/apps/jrobin/java/src/org/jrobin/core/ArcState.java
deleted file mode 100644
index 71f6dd297ae3036f44ac7b20efe5d9b3cb98f462..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/ArcState.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Class to represent internal RRD archive state for a single datasource. Objects of this
- * class are never manipulated directly, it's up to JRobin framework to manage
- * internal arcihve states.<p>
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class ArcState implements RrdUpdater {
-	private Archive parentArc;
-
-	private RrdDouble accumValue;
-	private RrdLong nanSteps;
-
-	ArcState(Archive parentArc, boolean shouldInitialize) throws IOException {
-		this.parentArc = parentArc;
-		accumValue = new RrdDouble(this);
-		nanSteps = new RrdLong(this);
-		if (shouldInitialize) {
-			Header header = parentArc.getParentDb().getHeader();
-			long step = header.getStep();
-			long lastUpdateTime = header.getLastUpdateTime();
-			long arcStep = parentArc.getArcStep();
-			long initNanSteps = (Util.normalize(lastUpdateTime, step) -
-					Util.normalize(lastUpdateTime, arcStep)) / step;
-			accumValue.set(Double.NaN);
-			nanSteps.set(initNanSteps);
-		}
-	}
-
-	String dump() throws IOException {
-		return "accumValue:" + accumValue.get() + " nanSteps:" + nanSteps.get() + "\n";
-	}
-
-	void setNanSteps(long value) throws IOException {
-		nanSteps.set(value);
-	}
-
-	/**
-	 * Returns the number of currently accumulated NaN steps.
-	 *
-	 * @return Number of currently accumulated NaN steps.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public long getNanSteps() throws IOException {
-		return nanSteps.get();
-	}
-
-	void setAccumValue(double value) throws IOException {
-		accumValue.set(value);
-	}
-
-	/**
-	 * Returns the value accumulated so far.
-	 *
-	 * @return Accumulated value
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public double getAccumValue() throws IOException {
-		return accumValue.get();
-	}
-
-	/**
-	 * Returns the Archive object to which this ArcState object belongs.
-	 *
-	 * @return Parent Archive object.
-	 */
-	public Archive getParent() {
-		return parentArc;
-	}
-
-	void appendXml(XmlWriter writer) throws IOException {
-		writer.startTag("ds");
-		writer.writeTag("value", accumValue.get());
-		writer.writeTag("unknown_datapoints", nanSteps.get());
-		writer.closeTag(); // ds
-	}
-
-	/**
-	 * Copies object's internal state to another ArcState object.
-	 *
-	 * @param other New ArcState object to copy state to
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if supplied argument is not an ArcState object
-	 */
-	public void copyStateTo(RrdUpdater other) throws IOException, RrdException {
-		if (!(other instanceof ArcState)) {
-			throw new RrdException(
-					"Cannot copy ArcState object to " + other.getClass().getName());
-		}
-		ArcState arcState = (ArcState) other;
-		arcState.accumValue.set(accumValue.get());
-		arcState.nanSteps.set(nanSteps.get());
-	}
-
-	/**
-	 * Returns the underlying storage (backend) object which actually performs all
-	 * I/O operations.
-	 *
-	 * @return I/O backend object
-	 */
-	public RrdBackend getRrdBackend() {
-		return parentArc.getRrdBackend();
-	}
-
-	/**
-	 * Required to implement RrdUpdater interface. You should never call this method directly.
-	 *
-	 * @return Allocator object
-	 */
-	public RrdAllocator getRrdAllocator() {
-		return parentArc.getRrdAllocator();
-	}
-
-    public String toString() {
-        return "ArcState@" + Integer.toHexString(hashCode()) + "[parentArc=" + parentArc + ",accumValue=" + accumValue + ",nanSteps=" + nanSteps + "]";
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/Archive.java b/apps/jrobin/java/src/org/jrobin/core/Archive.java
deleted file mode 100644
index 470bc75eb79a73dda32fb44adc327cf141f54bb8..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/Archive.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Class to represent single RRD archive in a RRD with its internal state.
- * Normally, you don't need methods to manipulate archive objects directly
- * because JRobin framework does it automatically for you.
- * <p>
- * Each archive object consists of three parts: archive definition, archive state objects
- * (one state object for each datasource) and round robin archives (one round robin for
- * each datasource). API (read-only) is provided to access each of theese parts.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class Archive implements RrdUpdater, ConsolFuns {
-	private RrdDb parentDb;
-	// definition
-	private RrdString consolFun;
-	private RrdDouble xff;
-	private RrdInt steps, rows;
-	// state
-	private Robin[] robins;
-	private ArcState[] states;
-
-	Archive(final RrdDb parentDb, final ArcDef arcDef) throws IOException {
-	    final boolean shouldInitialize = arcDef != null;
-		this.parentDb = parentDb;
-		consolFun = new RrdString(this, true);  // constant, may be cached
-		xff = new RrdDouble(this);
-		steps = new RrdInt(this, true);			// constant, may be cached
-		rows = new RrdInt(this, true);			// constant, may be cached
-		if (shouldInitialize) {
-			consolFun.set(arcDef.getConsolFun());
-			xff.set(arcDef.getXff());
-			steps.set(arcDef.getSteps());
-			rows.set(arcDef.getRows());
-		}
-		final int dsCount = parentDb.getHeader().getDsCount();
-		states = new ArcState[dsCount];
-		robins = new Robin[dsCount];
-		final int numRows = rows.get();
-		for (int i = 0; i < dsCount; i++) {
-			states[i] = new ArcState(this, shouldInitialize);
-			robins[i] = new Robin(this, numRows, shouldInitialize);
-		}
-	}
-
-	// read from XML
-	Archive(final RrdDb parentDb, final DataImporter reader, final int arcIndex) throws IOException, RrdException,RrdException {
-		this(parentDb, new ArcDef(
-				reader.getConsolFun(arcIndex), reader.getXff(arcIndex),
-				reader.getSteps(arcIndex), reader.getRows(arcIndex)));
-		final int dsCount = parentDb.getHeader().getDsCount();
-		for (int i = 0; i < dsCount; i++) {
-			// restore state
-			states[i].setAccumValue(reader.getStateAccumValue(arcIndex, i));
-			states[i].setNanSteps(reader.getStateNanSteps(arcIndex, i));
-			// restore robins
-			double[] values = reader.getValues(arcIndex, i);
-			robins[i].update(values);
-		}
-	}
-
-	/**
-	 * Returns archive time step in seconds. Archive step is equal to RRD step
-	 * multiplied with the number of archive steps.
-	 *
-	 * @return Archive time step in seconds
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public long getArcStep() throws IOException {
-	    final long step = parentDb.getHeader().getStep();
-		return step * steps.get();
-	}
-
-	String dump() throws IOException {
-	    final StringBuffer buffer = new StringBuffer("== ARCHIVE ==\n");
-		buffer.append("RRA:").append(consolFun.get()).append(":").append(xff.get()).append(":").append(steps.get()).
-				append(":").append(rows.get()).append("\n");
-		buffer.append("interval [").append(getStartTime()).append(", ").append(getEndTime()).append("]" + "\n");
-		for (int i = 0; i < robins.length; i++) {
-			buffer.append(states[i].dump());
-			buffer.append(robins[i].dump());
-		}
-		return buffer.toString();
-	}
-
-	RrdDb getParentDb() {
-		return parentDb;
-	}
-
-	public void archive(final int dsIndex, final double value, final long numStepUpdates) throws IOException {
-	    final Robin robin = robins[dsIndex];
-		final ArcState state = states[dsIndex];
-		final long step = parentDb.getHeader().getStep();
-		final long lastUpdateTime = parentDb.getHeader().getLastUpdateTime();
-		long updateTime = Util.normalize(lastUpdateTime, step) + step;
-		final long arcStep = getArcStep();
-        final String consolFunString = consolFun.get();
-        final int numSteps = steps.get();
-        final int numRows = rows.get();
-        final double xffValue = xff.get();
-
-        // finish current step
-		long numUpdates = numStepUpdates;
-		while (numUpdates > 0) {
-			accumulate(state, value, consolFunString);
-			numUpdates--;
-			if (updateTime % arcStep == 0) {
-                finalizeStep(state, robin, consolFunString, numSteps, xffValue);
-				break;
-			}
-			else {
-				updateTime += step;
-			}
-		}
-		// update robin in bulk
-		final int bulkUpdateCount = (int) Math.min(numUpdates / numSteps, (long) numRows);
-		robin.bulkStore(value, bulkUpdateCount);
-		// update remaining steps
-		final long remainingUpdates = numUpdates % numSteps;
-		for (long i = 0; i < remainingUpdates; i++) {
-			accumulate(state, value, consolFunString);
-		}
-	}
-
-	private void accumulate(final ArcState state, final double value, String consolFunString) throws IOException {
-		if (Double.isNaN(value)) {
-			state.setNanSteps(state.getNanSteps() + 1);
-		}
-		else {
-            final double accumValue = state.getAccumValue();
-            if (consolFunString.equals(CF_MIN)) {
-				final double minValue = Util.min(accumValue, value);
-				if (minValue != accumValue) {
-				    state.setAccumValue(minValue);
-				}
-			}
-			else if (consolFunString.equals(CF_MAX)) {
-				final double maxValue = Util.max(accumValue, value);
-				if (maxValue != accumValue) {
-				    state.setAccumValue(maxValue);
-				}
-			}
-			else if (consolFunString.equals(CF_LAST)) {
-				state.setAccumValue(value);
-			}
-			else if (consolFunString.equals(CF_AVERAGE)) {
-				state.setAccumValue(Util.sum(accumValue, value));
-			}
-		}
-	}
-
-	private void finalizeStep(final ArcState state, final Robin robin, final String consolFunString, final long numSteps, final double xffValue) throws IOException {
-	    final long nanSteps = state.getNanSteps();
-		//double nanPct = (double) nanSteps / (double) arcSteps;
-		double accumValue = state.getAccumValue();
-		if (nanSteps <= xffValue * numSteps && !Double.isNaN(accumValue)) {
-			if (consolFunString.equals(CF_AVERAGE)) {
-				accumValue /= (numSteps - nanSteps);
-			}
-			robin.store(accumValue);
-		} else {
-		    robin.store(Double.NaN);
-		}
-		state.setAccumValue(Double.NaN);
-		state.setNanSteps(0);
-	}
-
-	/**
-	 * Returns archive consolidation function ("AVERAGE", "MIN", "MAX" or "LAST").
-	 *
-	 * @return Archive consolidation function.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public String getConsolFun() throws IOException {
-		return consolFun.get();
-	}
-
-	/**
-	 * Returns archive X-files factor.
-	 *
-	 * @return Archive X-files factor (between 0 and 1).
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public double getXff() throws IOException {
-		return xff.get();
-	}
-
-	/**
-	 * Returns the number of archive steps.
-	 *
-	 * @return Number of archive steps.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public int getSteps() throws IOException {
-		return steps.get();
-	}
-
-	/**
-	 * Returns the number of archive rows.
-	 *
-	 * @return Number of archive rows.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public int getRows() throws IOException {
-		return rows.get();
-	}
-
-	/**
-	 * Returns current starting timestamp. This value is not constant.
-	 *
-	 * @return Timestamp corresponding to the first archive row
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public long getStartTime() throws IOException {
-	    final long endTime = getEndTime();
-		final long arcStep = getArcStep();
-		final long numRows = rows.get();
-		return endTime - (numRows - 1) * arcStep;
-	}
-
-	/**
-	 * Returns current ending timestamp. This value is not constant.
-	 *
-	 * @return Timestamp corresponding to the last archive row
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public long getEndTime() throws IOException {
-		final long arcStep = getArcStep();
-		final long lastUpdateTime = parentDb.getHeader().getLastUpdateTime();
-		return Util.normalize(lastUpdateTime, arcStep);
-	}
-
-	/**
-	 * Returns the underlying archive state object. Each datasource has its
-	 * corresponding ArcState object (archive states are managed independently
-	 * for each RRD datasource).
-	 *
-	 * @param dsIndex Datasource index
-	 * @return Underlying archive state object
-	 */
-	public ArcState getArcState(final int dsIndex) {
-		return states[dsIndex];
-	}
-
-	/**
-	 * Returns the underlying round robin archive. Robins are used to store actual
-	 * archive values on a per-datasource basis.
-	 *
-	 * @param dsIndex Index of the datasource in the RRD.
-	 * @return Underlying round robin archive for the given datasource.
-	 */
-	public Robin getRobin(final int dsIndex) {
-		return robins[dsIndex];
-	}
-
-	FetchData fetchData(final FetchRequest request) throws IOException, RrdException {
-	    final long arcStep = getArcStep();
-		final long fetchStart = Util.normalize(request.getFetchStart(), arcStep);
-		long fetchEnd = Util.normalize(request.getFetchEnd(), arcStep);
-		if (fetchEnd < request.getFetchEnd()) {
-			fetchEnd += arcStep;
-		}
-		final long startTime = getStartTime();
-		final long endTime = getEndTime();
-		String[] dsToFetch = request.getFilter();
-		if (dsToFetch == null) {
-			dsToFetch = parentDb.getDsNames();
-		}
-		final int dsCount = dsToFetch.length;
-		final int ptsCount = (int) ((fetchEnd - fetchStart) / arcStep + 1);
-		final long[] timestamps = new long[ptsCount];
-		final double[][] values = new double[dsCount][ptsCount];
-		final long matchStartTime = Math.max(fetchStart, startTime);
-		final long matchEndTime = Math.min(fetchEnd, endTime);
-		double[][] robinValues = null;
-		if (matchStartTime <= matchEndTime) {
-			// preload robin values
-		    final int matchCount = (int) ((matchEndTime - matchStartTime) / arcStep + 1);
-			final int matchStartIndex = (int) ((matchStartTime - startTime) / arcStep);
-			robinValues = new double[dsCount][];
-			for (int i = 0; i < dsCount; i++) {
-			    final int dsIndex = parentDb.getDsIndex(dsToFetch[i]);
-				robinValues[i] = robins[dsIndex].getValues(matchStartIndex, matchCount);
-			}
-		}
-		for (int ptIndex = 0; ptIndex < ptsCount; ptIndex++) {
-		    final long time = fetchStart + ptIndex * arcStep;
-		    timestamps[ptIndex] = time;
-			for (int i = 0; i < dsCount; i++) {
-				double value = Double.NaN;
-				if (time >= matchStartTime && time <= matchEndTime) {
-					// inbound time
-					final int robinValueIndex = (int) ((time - matchStartTime) / arcStep);
-					assert robinValues != null;
-					value = robinValues[i][robinValueIndex];
-				}
-				values[i][ptIndex] = value;
-			}
-		}
-		final FetchData fetchData = new FetchData(this, request);
-		fetchData.setTimestamps(timestamps);
-		fetchData.setValues(values);
-		return fetchData;
-	}
-
-	void appendXml(final XmlWriter writer) throws IOException {
-		writer.startTag("rra");
-		writer.writeTag("cf", consolFun.get());
-		writer.writeComment(getArcStep() + " seconds");
-		writer.writeTag("pdp_per_row", steps.get());
-		writer.writeTag("xff", xff.get());
-		writer.startTag("cdp_prep");
-		for (final ArcState state : states) {
-			state.appendXml(writer);
-		}
-		writer.closeTag(); // cdp_prep
-		writer.startTag("database");
-		final long startTime = getStartTime();
-		for (int i = 0; i < rows.get(); i++) {
-			final long time = startTime + i * getArcStep();
-			writer.writeComment(Util.getDate(time) + " / " + time);
-			writer.startTag("row");
-			for (final Robin robin : robins) {
-				writer.writeTag("v", robin.getValue(i));
-			}
-			writer.closeTag(); // row
-		}
-		writer.closeTag(); // database
-		writer.closeTag(); // rra
-	}
-
-	/**
-	 * Copies object's internal state to another Archive object.
-	 *
-	 * @param other New Archive object to copy state to
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if supplied argument is not an Archive object
-	 */
-	public void copyStateTo(final RrdUpdater other) throws IOException, RrdException {
-		if (!(other instanceof Archive)) {
-			throw new RrdException("Cannot copy Archive object to " + other.getClass().getName());
-		}
-		final Archive arc = (Archive) other;
-		if (!arc.consolFun.get().equals(consolFun.get())) {
-			throw new RrdException("Incompatible consolidation functions");
-		}
-		if (arc.steps.get() != steps.get()) {
-			throw new RrdException("Incompatible number of steps");
-		}
-		final int count = parentDb.getHeader().getDsCount();
-		for (int i = 0; i < count; i++) {
-		    final int j = Util.getMatchingDatasourceIndex(parentDb, i, arc.parentDb);
-			if (j >= 0) {
-				states[i].copyStateTo(arc.states[j]);
-				robins[i].copyStateTo(arc.robins[j]);
-			}
-		}
-	}
-
-	/**
-	 * Sets X-files factor to a new value.
-	 *
-	 * @param xff New X-files factor value. Must be &gt;= 0 and &lt; 1.
-	 * @throws RrdException Thrown if invalid value is supplied
-	 * @throws IOException  Thrown in case of I/O error
-	 */
-	public void setXff(final double xff) throws RrdException, IOException {
-		if (xff < 0D || xff >= 1D) {
-			throw new RrdException("Invalid xff supplied (" + xff + "), must be >= 0 and < 1");
-		}
-		this.xff.set(xff);
-	}
-
-	/**
-	 * Returns the underlying storage (backend) object which actually performs all
-	 * I/O operations.
-	 *
-	 * @return I/O backend object
-	 */
-	public RrdBackend getRrdBackend() {
-		return parentDb.getRrdBackend();
-	}
-
-	/**
-	 * Required to implement RrdUpdater interface. You should never call this method directly.
-	 *
-	 * @return Allocator object
-	 */
-	public RrdAllocator getRrdAllocator() {
-		return parentDb.getRrdAllocator();
-	}
-
-    public String toString() {
-        return "Archive@" + Integer.toHexString(hashCode()) + "[parentDb=" + parentDb + ",consolFun=" + consolFun + ",xff=" + xff + ",steps=" + steps + ",rows=" + rows + ",robins=" + robins + ",states=" + states + "]";
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/ConsolFuns.java b/apps/jrobin/java/src/org/jrobin/core/ConsolFuns.java
deleted file mode 100644
index ec9f4bd7253eca658b7a7538a911e34ae15d2050..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/ConsolFuns.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core;
-
-/**
- * Simple interface to represent available consolidation functions
- */
-public interface ConsolFuns {
-	/**
-	 * Constant to represent AVERAGE consolidation function
-	 */
-	public static final String CF_AVERAGE = "AVERAGE";
-
-	/**
-	 * Constant to represent MIN consolidation function
-	 */
-	public static final String CF_MIN = "MIN";
-
-	/**
-	 * Constant to represent MAX consolidation function
-	 */
-	public static final String CF_MAX = "MAX";
-
-	/**
-	 * Constant to represent LAST consolidation function
-	 */
-	public static final String CF_LAST = "LAST";
-
-	/**
-	 * Constant to represent FIRST consolidation function
-	 */
-	public static final String CF_FIRST = "FIRST";
-
-	/**
-	 * Constant to represent TOTAL consolidation function
-	 */
-	public static final String CF_TOTAL = "TOTAL";
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/DataImporter.java b/apps/jrobin/java/src/org/jrobin/core/DataImporter.java
deleted file mode 100644
index 9055005838bf687b3f028e8314487c47b182158d..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/DataImporter.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-import org.jrobin.core.RrdException;
-
-abstract class DataImporter {
-
-	// header
-	abstract String getVersion() throws RrdException, IOException;
-
-	abstract long getLastUpdateTime() throws RrdException, IOException;
-
-	abstract long getStep() throws RrdException, IOException;
-
-	abstract int getDsCount() throws RrdException, IOException;
-
-	abstract int getArcCount() throws RrdException, IOException;
-
-	// datasource
-	abstract String getDsName(int dsIndex) throws RrdException, IOException;
-
-	abstract String getDsType(int dsIndex) throws RrdException, IOException;
-
-	abstract long getHeartbeat(int dsIndex) throws RrdException, IOException;
-
-	abstract double getMinValue(int dsIndex) throws RrdException, IOException;
-
-	abstract double getMaxValue(int dsIndex) throws RrdException, IOException;
-
-	// datasource state
-	abstract double getLastValue(int dsIndex) throws RrdException, IOException;
-
-	abstract double getAccumValue(int dsIndex) throws RrdException, IOException;
-
-	abstract long getNanSeconds(int dsIndex) throws RrdException, IOException;
-
-	// archive
-	abstract String getConsolFun(int arcIndex) throws RrdException, IOException;
-
-	abstract double getXff(int arcIndex) throws RrdException, IOException;
-
-	abstract int getSteps(int arcIndex) throws RrdException, IOException;
-
-	abstract int getRows(int arcIndex) throws RrdException, IOException;
-
-	// archive state
-	abstract double getStateAccumValue(int arcIndex, int dsIndex) throws RrdException, IOException;
-
-	abstract int getStateNanSteps(int arcIndex, int dsIndex) throws RrdException, IOException;
-
-	abstract double[] getValues(int arcIndex, int dsIndex) throws RrdException, IOException,RrdException;
-
-	long getEstimatedSize() throws RrdException, IOException {
-		int dsCount = getDsCount();
-		int arcCount = getArcCount();
-		int rowCount = 0;
-		for (int i = 0; i < arcCount; i++) {
-			rowCount += getRows(i);
-		}
-		return RrdDef.calculateSize(dsCount, arcCount, rowCount);
-	}
-
-	void release() throws RrdException, IOException {
-		// NOP
-	}
-
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/core/Datasource.java b/apps/jrobin/java/src/org/jrobin/core/Datasource.java
deleted file mode 100644
index 3aca8d3d45572919642584c7d3b9631a6b401d94..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/Datasource.java
+++ /dev/null
@@ -1,497 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Class to represent single datasource within RRD. Each datasource object holds the
- * following information: datasource definition (once set, never changed) and
- * datasource state variables (changed whenever RRD gets updated).
- * <p>
- * Normally, you don't need to manipluate Datasource objects directly, it's up to
- * JRobin framework to do it for you.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-
-public class Datasource implements RrdUpdater, DsTypes {
-	private static final double MAX_32_BIT = Math.pow(2, 32);
-	private static final double MAX_64_BIT = Math.pow(2, 64);
-
-	private RrdDb parentDb;
-	// definition
-	private RrdString dsName, dsType;
-	private RrdLong heartbeat;
-	private RrdDouble minValue, maxValue;
-
-	// cache
-	private String m_primitiveDsName = null;
-	private String m_primitiveDsType = null;
-
-	// state variables
-	private RrdDouble lastValue;
-	private RrdLong nanSeconds;
-	private RrdDouble accumValue;
-
-	Datasource(final RrdDb parentDb, final DsDef dsDef) throws IOException {
-		boolean shouldInitialize = dsDef != null;
-		this.parentDb = parentDb;
-		dsName = new RrdString(this);
-		dsType = new RrdString(this);
-		heartbeat = new RrdLong(this);
-		minValue = new RrdDouble(this);
-		maxValue = new RrdDouble(this);
-		lastValue = new RrdDouble(this);
-		accumValue = new RrdDouble(this);
-		nanSeconds = new RrdLong(this);
-		if (shouldInitialize) {
-		    dsName.set(dsDef.getDsName());
-            m_primitiveDsName = null;
-			dsType.set(dsDef.getDsType());
-			m_primitiveDsType = null;
-			heartbeat.set(dsDef.getHeartbeat());
-			minValue.set(dsDef.getMinValue());
-			maxValue.set(dsDef.getMaxValue());
-			lastValue.set(Double.NaN);
-			accumValue.set(0.0);
-			final Header header = parentDb.getHeader();
-			nanSeconds.set(header.getLastUpdateTime() % header.getStep());
-		}
-	}
-
-	Datasource(final RrdDb parentDb, final DataImporter reader, final int dsIndex) throws IOException, RrdException {
-		this(parentDb, null);
-		dsName.set(reader.getDsName(dsIndex));
-		m_primitiveDsName = null;
-		dsType.set(reader.getDsType(dsIndex));
-		m_primitiveDsType = null;
-		heartbeat.set(reader.getHeartbeat(dsIndex));
-		minValue.set(reader.getMinValue(dsIndex));
-		maxValue.set(reader.getMaxValue(dsIndex));
-		lastValue.set(reader.getLastValue(dsIndex));
-		accumValue.set(reader.getAccumValue(dsIndex));
-		nanSeconds.set(reader.getNanSeconds(dsIndex));
-	}
-
-	String dump() throws IOException {
-		return "== DATASOURCE ==\n" +
-				"DS:" + dsName.get() + ":" + dsType.get() + ":" +
-				heartbeat.get() + ":" + minValue.get() + ":" +
-				maxValue.get() + "\nlastValue:" + lastValue.get() +
-				" nanSeconds:" + nanSeconds.get() +
-				" accumValue:" + accumValue.get() + "\n";
-	}
-
-	/**
-	 * Returns datasource name.
-	 *
-	 * @return Datasource name
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public String getDsName() throws IOException {
-	    if (m_primitiveDsName == null) {
-	        m_primitiveDsName = dsName.get();
-	    }
-	    return m_primitiveDsName;
-	}
-
-	/**
-	 * Returns datasource type (GAUGE, COUNTER, DERIVE, ABSOLUTE).
-	 *
-	 * @return Datasource type.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public String getDsType() throws IOException {
-	    if (m_primitiveDsType == null) {
-	        m_primitiveDsType = dsType.get();
-	    }
-	    return m_primitiveDsType;
-	}
-
-	/**
-	 * Returns datasource heartbeat
-	 *
-	 * @return Datasource heartbeat
-	 * @throws IOException Thrown in case of I/O error
-	 */
-
-	public long getHeartbeat() throws IOException {
-		return heartbeat.get();
-	}
-
-	/**
-	 * Returns mimimal allowed value for this datasource.
-	 *
-	 * @return Minimal value allowed.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public double getMinValue() throws IOException {
-		return minValue.get();
-	}
-
-	/**
-	 * Returns maximal allowed value for this datasource.
-	 *
-	 * @return Maximal value allowed.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public double getMaxValue() throws IOException {
-		return maxValue.get();
-	}
-
-	/**
-	 * Returns last known value of the datasource.
-	 *
-	 * @return Last datasource value.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public double getLastValue() throws IOException {
-		return lastValue.get();
-	}
-
-	/**
-	 * Returns value this datasource accumulated so far.
-	 *
-	 * @return Accumulated datasource value.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public double getAccumValue() throws IOException {
-		return accumValue.get();
-	}
-
-	/**
-	 * Returns the number of accumulated NaN seconds.
-	 *
-	 * @return Accumulated NaN seconds.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public long getNanSeconds() throws IOException {
-		return nanSeconds.get();
-	}
-
-	void process(final long newTime, final double newValue) throws IOException, RrdException {
-	    final Header header = parentDb.getHeader();
-	    final long step = header.getStep();
-		final long oldTime = header.getLastUpdateTime();
-		final long startTime = Util.normalize(oldTime, step);
-		final long endTime = startTime + step;
-		final double oldValue = lastValue.get();
-		final double updateValue = calculateUpdateValue(oldTime, oldValue, newTime, newValue);
-		if (newTime < endTime) {
-			accumulate(oldTime, newTime, updateValue);
-		}
-		else {
-			// should store something
-			final long boundaryTime = Util.normalize(newTime, step);
-			accumulate(oldTime, boundaryTime, updateValue);
-			final double value = calculateTotal(startTime, boundaryTime);
-			// how many updates?
-			final long numSteps = (boundaryTime - endTime) / step + 1L;
-			// ACTION!
-			parentDb.archive(this, value, numSteps);
-			// cleanup
-			nanSeconds.set(0);
-			accumValue.set(0.0);
-			accumulate(boundaryTime, newTime, updateValue);
-		}
-	}
-
-	private double calculateUpdateValue(final long oldTime, final double oldValue, final long newTime, final double newValue) throws IOException {
-		double updateValue = Double.NaN;
-		if (newTime - oldTime <= heartbeat.get()) {
-		    final String type = dsType.get();
-			if (type.equals(DT_GAUGE)) {
-				updateValue = newValue;
-			}
-			else if (type.equals(DT_ABSOLUTE)) {
-				if (!Double.isNaN(newValue)) {
-					updateValue = newValue / (newTime - oldTime);
-				}
-			}
-			else if (type.equals(DT_DERIVE)) {
-				if (!Double.isNaN(newValue) && !Double.isNaN(oldValue)) {
-					updateValue = (newValue - oldValue) / (newTime - oldTime);
-				}
-			}
-			else if (type.equals(DT_COUNTER)) {
-				if (!Double.isNaN(newValue) && !Double.isNaN(oldValue)) {
-				    double diff = newValue - oldValue;
-					if (diff < 0) {
-						diff += MAX_32_BIT;
-					}
-					if (diff < 0) {
-						diff += MAX_64_BIT - MAX_32_BIT;
-					}
-					if (diff >= 0) {
-						updateValue = diff / (newTime - oldTime);
-					}
-				}
-			}
-			if (!Double.isNaN(updateValue)) {
-			    final double minVal = minValue.get();
-				final double maxVal = maxValue.get();
-				if (!Double.isNaN(minVal) && updateValue < minVal) {
-					updateValue = Double.NaN;
-				}
-				if (!Double.isNaN(maxVal) && updateValue > maxVal) {
-					updateValue = Double.NaN;
-				}
-			}
-		}
-		lastValue.set(newValue);
-		return updateValue;
-	}
-
-	private void accumulate(final long oldTime, final long newTime, final double updateValue) throws IOException {
-		if (Double.isNaN(updateValue)) {
-			nanSeconds.set(nanSeconds.get() + (newTime - oldTime));
-		}
-		else {
-			accumValue.set(accumValue.get() + updateValue * (newTime - oldTime));
-		}
-	}
-
-	private double calculateTotal(final long startTime, final long boundaryTime) throws IOException {
-		double totalValue = Double.NaN;
-		final long validSeconds = boundaryTime - startTime - nanSeconds.get();
-		if (nanSeconds.get() <= heartbeat.get() && validSeconds > 0) {
-			totalValue = accumValue.get() / validSeconds;
-		}
-		// IMPORTANT:
-		// if datasource name ends with "!", we'll send zeros instead of NaNs
-		// this might be handy from time to time
-		if (Double.isNaN(totalValue) && dsName.get().endsWith(DsDef.FORCE_ZEROS_FOR_NANS_SUFFIX)) {
-			totalValue = 0D;
-		}
-		return totalValue;
-	}
-
-	void appendXml(final XmlWriter writer) throws IOException {
-		writer.startTag("ds");
-		writer.writeTag("name", dsName.get());
-		writer.writeTag("type", dsType.get());
-		writer.writeTag("minimal_heartbeat", heartbeat.get());
-		writer.writeTag("min", minValue.get());
-		writer.writeTag("max", maxValue.get());
-		writer.writeComment("PDP Status");
-		writer.writeTag("last_ds", lastValue.get(), "UNKN");
-		writer.writeTag("value", accumValue.get());
-		writer.writeTag("unknown_sec", nanSeconds.get());
-		writer.closeTag();  // ds
-	}
-
-	/**
-	 * Copies object's internal state to another Datasource object.
-	 *
-	 * @param other New Datasource object to copy state to
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if supplied argument is not a Datasource object
-	 */
-	public void copyStateTo(final RrdUpdater other) throws IOException, RrdException {
-		if (!(other instanceof Datasource)) {
-			throw new RrdException("Cannot copy Datasource object to " + other.getClass().getName());
-		}
-		final Datasource datasource = (Datasource) other;
-		if (!datasource.dsName.get().equals(dsName.get())) {
-			throw new RrdException("Incomaptible datasource names");
-		}
-		if (!datasource.dsType.get().equals(dsType.get())) {
-			throw new RrdException("Incomaptible datasource types");
-		}
-		datasource.lastValue.set(lastValue.get());
-		datasource.nanSeconds.set(nanSeconds.get());
-		datasource.accumValue.set(accumValue.get());
-	}
-
-	/**
-	 * Returns index of this Datasource object in the RRD.
-	 *
-	 * @return Datasource index in the RRD.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public int getDsIndex() throws IOException {
-		try {
-			return parentDb.getDsIndex(dsName.get());
-		}
-		catch (final RrdException e) {
-			return -1;
-		}
-	}
-
-	/**
-	 * Sets datasource heartbeat to a new value.
-	 *
-	 * @param heartbeat New heartbeat value
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if invalid (non-positive) heartbeat value is specified.
-	 */
-	public void setHeartbeat(final long heartbeat) throws RrdException, IOException {
-		if (heartbeat < 1L) {
-			throw new RrdException("Invalid heartbeat specified: " + heartbeat);
-		}
-		this.heartbeat.set(heartbeat);
-	}
-
-	/**
-	 * Sets datasource name to a new value
-	 *
-	 * @param newDsName New datasource name
-	 * @throws RrdException Thrown if invalid data source name is specified (name too long, or
-	 *                      name already defined in the RRD
-	 * @throws IOException  Thrown in case of I/O error
-	 */
-	public void setDsName(final String newDsName) throws RrdException, IOException {
-		if (newDsName.length() > RrdString.STRING_LENGTH) {
-			throw new RrdException("Invalid datasource name specified: " + newDsName);
-		}
-		if (parentDb.containsDs(newDsName)) {
-			throw new RrdException("Datasource already defined in this RRD: " + newDsName);
-		}
-		dsName.set(newDsName);
-		m_primitiveDsName = null;
-	}
-
-	public void setDsType(final String newDsType) throws RrdException, IOException {
-		if (!DsDef.isValidDsType(newDsType)) {
-			throw new RrdException("Invalid datasource type: " + newDsType);
-		}
-		// set datasource type
-		this.dsType.set(newDsType);
-		m_primitiveDsType = null;
-		// reset datasource status
-		lastValue.set(Double.NaN);
-		accumValue.set(0.0);
-		// reset archive status
-		final int dsIndex = parentDb.getDsIndex(dsName.get());
-		final Archive[] archives = parentDb.getArchives();
-		for (final Archive archive : archives) {
-			archive.getArcState(dsIndex).setAccumValue(Double.NaN);
-		}
-	}
-
-	/**
-	 * Sets minimum allowed value for this datasource. If <code>filterArchivedValues</code>
-	 * argment is set to true, all archived values less then <code>minValue</code> will
-	 * be fixed to NaN.
-	 *
-	 * @param minValue			 New minimal value. Specify <code>Double.NaN</code> if no minimal
-	 *                             value should be set
-	 * @param filterArchivedValues true, if archived datasource values should be fixed;
-	 *                             false, otherwise.
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if invalid minValue was supplied (not less then maxValue)
-	 */
-	public void setMinValue(final double minValue, final boolean filterArchivedValues) throws IOException, RrdException {
-	    final double maxValue = this.maxValue.get();
-		if (!Double.isNaN(minValue) && !Double.isNaN(maxValue) && minValue >= maxValue) {
-			throw new RrdException("Invalid min/max values: " + minValue + "/" + maxValue);
-		}
-		this.minValue.set(minValue);
-		if (!Double.isNaN(minValue) && filterArchivedValues) {
-			final int dsIndex = getDsIndex();
-			final Archive[] archives = parentDb.getArchives();
-			for (final Archive archive : archives) {
-				archive.getRobin(dsIndex).filterValues(minValue, Double.NaN);
-			}
-		}
-	}
-
-	/**
-	 * Sets maximum allowed value for this datasource. If <code>filterArchivedValues</code>
-	 * argment is set to true, all archived values greater then <code>maxValue</code> will
-	 * be fixed to NaN.
-	 *
-	 * @param maxValue			 New maximal value. Specify <code>Double.NaN</code> if no max
-	 *                             value should be set.
-	 * @param filterArchivedValues true, if archived datasource values should be fixed;
-	 *                             false, otherwise.
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if invalid maxValue was supplied (not greater then minValue)
-	 */
-	public void setMaxValue(final double maxValue, final boolean filterArchivedValues) throws IOException, RrdException {
-	    final double minValue = this.minValue.get();
-		if (!Double.isNaN(minValue) && !Double.isNaN(maxValue) && minValue >= maxValue) {
-			throw new RrdException("Invalid min/max values: " + minValue + "/" + maxValue);
-		}
-		this.maxValue.set(maxValue);
-		if (!Double.isNaN(maxValue) && filterArchivedValues) {
-			final int dsIndex = getDsIndex();
-			final Archive[] archives = parentDb.getArchives();
-			for (final Archive archive : archives) {
-				archive.getRobin(dsIndex).filterValues(Double.NaN, maxValue);
-			}
-		}
-	}
-
-	/**
-	 * Sets min/max values allowed for this datasource. If <code>filterArchivedValues</code>
-	 * argment is set to true, all archived values less then <code>minValue</code> or
-	 * greater then <code>maxValue</code> will be fixed to NaN.
-	 *
-	 * @param minValue			 New minimal value. Specify <code>Double.NaN</code> if no min
-	 *                             value should be set.
-	 * @param maxValue			 New maximal value. Specify <code>Double.NaN</code> if no max
-	 *                             value should be set.
-	 * @param filterArchivedValues true, if archived datasource values should be fixed;
-	 *                             false, otherwise.
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if invalid min/max values were supplied
-	 */
-	public void setMinMaxValue(final double minValue, final double maxValue, final boolean filterArchivedValues) throws IOException, RrdException {
-		if (!Double.isNaN(minValue) && !Double.isNaN(maxValue) && minValue >= maxValue) {
-			throw new RrdException("Invalid min/max values: " + minValue + "/" + maxValue);
-		}
-		this.minValue.set(minValue);
-		this.maxValue.set(maxValue);
-		if (!(Double.isNaN(minValue) && Double.isNaN(maxValue)) && filterArchivedValues) {
-		    final int dsIndex = getDsIndex();
-			final Archive[] archives = parentDb.getArchives();
-			for (final Archive archive : archives) {
-				archive.getRobin(dsIndex).filterValues(minValue, maxValue);
-			}
-		}
-	}
-
-	/**
-	 * Returns the underlying storage (backend) object which actually performs all
-	 * I/O operations.
-	 *
-	 * @return I/O backend object
-	 */
-	public RrdBackend getRrdBackend() {
-		return parentDb.getRrdBackend();
-	}
-
-	/**
-	 * Required to implement RrdUpdater interface. You should never call this method directly.
-	 *
-	 * @return Allocator object
-	 */
-	public RrdAllocator getRrdAllocator() {
-		return parentDb.getRrdAllocator();
-	}
-
-	public String toString() {
-	    return getClass().getName() + "@" + Integer.toHexString(hashCode()) + "[parentDb=" + parentDb
-	        + ",dsName=" + dsName + ",dsType=" + dsType + ",heartbeat=" + heartbeat
-	        + ",minValue=" + minValue + ",maxValue=" + maxValue + "]";
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/DsDef.java b/apps/jrobin/java/src/org/jrobin/core/DsDef.java
deleted file mode 100644
index 362c9d373ec3e659c0ac3fb3e315d3043d9ac8bc..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/DsDef.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-/**
- * Class to represent single data source definition within the RRD.
- * Datasource definition consists of the following five elements:
- * <p>
- * <ul>
- * <li>data source name
- * <li>data soruce type
- * <li>heartbeat
- * <li>minimal value
- * <li>maximal value
- * </ul>
- * <p>
- * For the complete explanation of all source definition parameters, see RRDTool's
- * <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class DsDef implements DsTypes {
-	/**
-	 * array of valid source types
-	 */
-	public static final String[] DS_TYPES = {DT_GAUGE, DT_COUNTER, DT_DERIVE, DT_ABSOLUTE};
-	static final String FORCE_ZEROS_FOR_NANS_SUFFIX = "!";
-
-	private String dsName, dsType;
-	private long heartbeat;
-	private double minValue, maxValue;
-
-	/**
-	 * Creates new data source definition object. This object should be passed as argument
-	 * to {@link RrdDef#addDatasource(DsDef) addDatasource()}
-	 * method of {@link RrdDb RrdDb} object.
-	 * <p>
-	 * For the complete explanation of all source definition parameters, see RRDTool's
-	 * <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
-	 * <p>
-	 * <b>IMPORTANT NOTE:</b> If datasource name ends with '!', corresponding archives will never
-	 * store NaNs as datasource values. In that case, NaN datasource values will be silently
-	 * replaced with zeros by the framework.
-	 *
-	 * @param dsName	Data source name.
-	 * @param dsType	Data source type. Valid values are "COUNTER", "GAUGE", "DERIVE"
-	 *                  and "ABSOLUTE" (these string constants are conveniently defined in the
-	 *                  {@link DsTypes} class).
-	 * @param heartbeat Hearbeat
-	 * @param minValue  Minimal value. Use <code>Double.NaN</code> if unknown.
-	 * @param maxValue  Maximal value. Use <code>Double.NaN</code> if unknown.
-	 * @throws RrdException Thrown if any parameter has illegal value.
-	 */
-	public DsDef(final String dsName, final String dsType, final long heartbeat, final double minValue, final double maxValue) throws RrdException {
-		this.dsName = dsName;
-		this.dsType = dsType;
-		this.heartbeat = heartbeat;
-		this.minValue = minValue;
-		this.maxValue = maxValue;
-		validate();
-	}
-
-	/**
-	 * Returns data source name.
-	 *
-	 * @return Data source name.
-	 */
-	public String getDsName() {
-		return dsName;
-	}
-
-	/**
-	 * Returns source type.
-	 *
-	 * @return Source type ("COUNTER", "GAUGE", "DERIVE" or "ABSOLUTE").
-	 */
-	public String getDsType() {
-		return dsType;
-	}
-
-	/**
-	 * Returns source heartbeat.
-	 *
-	 * @return Source heartbeat.
-	 */
-	public long getHeartbeat() {
-		return heartbeat;
-	}
-
-	/**
-	 * Returns minimal calculated source value.
-	 *
-	 * @return Minimal value.
-	 */
-	public double getMinValue() {
-		return minValue;
-	}
-
-	/**
-	 * Returns maximal calculated source value.
-	 *
-	 * @return Maximal value.
-	 */
-	public double getMaxValue() {
-		return maxValue;
-	}
-
-	private void validate() throws RrdException {
-		if (dsName == null) {
-			throw new RrdException("Null datasource name specified");
-		}
-		if (dsName.length() == 0) {
-			throw new RrdException("Datasource name length equal to zero");
-		}
-		if (dsName.length() > RrdPrimitive.STRING_LENGTH) {
-			throw new RrdException("Datasource name [" + dsName + "] to long (" +
-					dsName.length() + " chars found, only " + RrdPrimitive.STRING_LENGTH + " allowed");
-		}
-		if (!isValidDsType(dsType)) {
-			throw new RrdException("Invalid datasource type specified: " + dsType);
-		}
-		if (heartbeat <= 0) {
-			throw new RrdException("Invalid heartbeat, must be positive: " + heartbeat);
-		}
-		if (!Double.isNaN(minValue) && !Double.isNaN(maxValue) && minValue >= maxValue) {
-			throw new RrdException("Invalid min/max values specified: " +
-					minValue + "/" + maxValue);
-		}
-	}
-
-	/**
-	 * Checks if function argument represents valid source type.
-	 *
-	 * @param dsType Source type to be checked.
-	 * @return <code>true</code> if <code>dsType</code> is valid type,
-	 *         <code>false</code> otherwise.
-	 */
-	public static boolean isValidDsType(final String dsType) {
-		for (final String type : DS_TYPES) {
-			if (type.equals(dsType)) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	/**
-	 * Returns string representing source definition (RRDTool format).
-	 *
-	 * @return String containing all data source definition parameters.
-	 */
-	public String dump() {
-		return "DS:" + dsName + ":" + dsType + ":" + heartbeat +
-				":" + Util.formatDouble(minValue, "U", false) +
-				":" + Util.formatDouble(maxValue, "U", false);
-	}
-
-	/**
-	 * Checks if two datasource definitions are equal.
-	 * Source definitions are treated as equal if they have the same source name.
-	 * It is not possible to create RRD with two equal archive definitions.
-	 *
-	 * @param obj Archive definition to compare with.
-	 * @return <code>true</code> if archive definitions are equal,
-	 *         <code>false</code> otherwise.
-	 */
-	public boolean equals(final Object obj) {
-		if (obj instanceof DsDef) {
-			final DsDef dsObj = (DsDef) obj;
-			return dsName.equals(dsObj.dsName);
-		}
-		return false;
-	}
-
-	public int hashCode() {
-		return dsName.hashCode() * 47;
-	}
-
-	boolean exactlyEqual(final DsDef def) {
-		return dsName.equals(def.dsName) && dsType.equals(def.dsType) &&
-				heartbeat == def.heartbeat && Util.equal(minValue, def.minValue) &&
-				Util.equal(maxValue, def.maxValue);
-	}
-
-    public String toString() {
-        return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[" + dump() + "]";
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/DsTypes.java b/apps/jrobin/java/src/org/jrobin/core/DsTypes.java
deleted file mode 100644
index 31330b043ca7888f9e4e43c5bde74ce2b5941c40..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/DsTypes.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core;
-
-/**
- * Simple interface to represent available datasource types.
- */
-public interface DsTypes {
-	/**
-	 * Constant to represent GAUGE datasource type
-	 */
-	public static final String DT_GAUGE = "GAUGE";
-
-	/**
-	 * Constant to represent COUNTER datasource type
-	 */
-	public static final String DT_COUNTER = "COUNTER";
-
-	/**
-	 * Constant to represent DERIVE datasource type
-	 */
-	public static final String DT_DERIVE = "DERIVE";
-
-	/**
-	 * Constant to represent ABSOLUTE datasource type
-	 */
-	public static final String DT_ABSOLUTE = "ABSOLUTE";
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/FetchData.java b/apps/jrobin/java/src/org/jrobin/core/FetchData.java
deleted file mode 100644
index cc57a87a42c436bc58c87d5d9ea7edb7efa996ee..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/FetchData.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import org.jrobin.data.Aggregates;
-import org.jrobin.data.DataProcessor;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Class used to represent data fetched from the RRD.
- * Object of this class is created when the method
- * {@link FetchRequest#fetchData() fetchData()} is
- * called on a {@link FetchRequest FetchRequest} object.
- * <p>
- * Data returned from the RRD is, simply, just one big table filled with
- * timestamps and corresponding datasource values.
- * Use {@link #getRowCount() getRowCount()} method to count the number
- * of returned timestamps (table rows).
- * <p>
- * The first table column is filled with timestamps. Time intervals
- * between consecutive timestamps are guaranteed to be equal. Use
- * {@link #getTimestamps() getTimestamps()} method to get an array of
- * timestamps returned.
- * <p>
- * Remaining columns are filled with datasource values for the whole timestamp range,
- * on a column-per-datasource basis. Use {@link #getColumnCount() getColumnCount()} to find
- * the number of datasources and {@link #getValues(int) getValues(i)} method to obtain
- * all values for the i-th datasource. Returned datasource values correspond to
- * the values returned with {@link #getTimestamps() getTimestamps()} method.
- */
-public class FetchData implements ConsolFuns {
-	// anything fuuny will do
-	private static final String RPN_SOURCE_NAME = "WHERE THE SPEECHLES UNITE IN A SILENT ACCORD";
-
-	private FetchRequest request;
-	private String[] dsNames;
-	private long[] timestamps;
-	private double[][] values;
-
-	private Archive matchingArchive;
-	private long arcStep;
-	private long arcEndTime;
-
-	FetchData(Archive matchingArchive, FetchRequest request) throws IOException {
-		this.matchingArchive = matchingArchive;
-		this.arcStep = matchingArchive.getArcStep();
-		this.arcEndTime = matchingArchive.getEndTime();
-		this.dsNames = request.getFilter();
-		if (this.dsNames == null) {
-			this.dsNames = matchingArchive.getParentDb().getDsNames();
-		}
-		this.request = request;
-	}
-
-	void setTimestamps(long[] timestamps) {
-		this.timestamps = timestamps;
-	}
-
-	void setValues(double[][] values) {
-		this.values = values;
-	}
-
-	/**
-	 * Returns the number of rows fetched from the corresponding RRD.
-	 * Each row represents datasource values for the specific timestamp.
-	 *
-	 * @return Number of rows.
-	 */
-	public int getRowCount() {
-		return timestamps.length;
-	}
-
-	/**
-	 * Returns the number of columns fetched from the corresponding RRD.
-	 * This number is always equal to the number of datasources defined
-	 * in the RRD. Each column represents values of a single datasource.
-	 *
-	 * @return Number of columns (datasources).
-	 */
-	public int getColumnCount() {
-		return dsNames.length;
-	}
-
-	/**
-	 * Returns an array of timestamps covering the whole range specified in the
-	 * {@link FetchRequest FetchReguest} object.
-	 *
-	 * @return Array of equidistant timestamps.
-	 */
-	public long[] getTimestamps() {
-		return timestamps;
-	}
-
-	/**
-	 * Returns the step with which this data was fetched.
-	 *
-	 * @return Step as long.
-	 */
-	public long getStep() {
-		return timestamps[1] - timestamps[0];
-	}
-
-	/**
-	 * Returns all archived values for a single datasource.
-	 * Returned values correspond to timestamps
-	 * returned with {@link #getTimestamps() getTimestamps()} method.
-	 *
-	 * @param dsIndex Datasource index.
-	 * @return Array of single datasource values.
-	 */
-	public double[] getValues(int dsIndex) {
-		return values[dsIndex];
-	}
-
-	/**
-	 * Returns all archived values for all datasources.
-	 * Returned values correspond to timestamps
-	 * returned with {@link #getTimestamps() getTimestamps()} method.
-	 *
-	 * @return Two-dimensional aray of all datasource values.
-	 */
-	public double[][] getValues() {
-		return values;
-	}
-
-	/**
-	 * Returns all archived values for a single datasource.
-	 * Returned values correspond to timestamps
-	 * returned with {@link #getTimestamps() getTimestamps()} method.
-	 *
-	 * @param dsName Datasource name.
-	 * @return Array of single datasource values.
-	 * @throws RrdException Thrown if no matching datasource name is found.
-	 */
-	public double[] getValues(String dsName) throws RrdException {
-		for (int dsIndex = 0; dsIndex < getColumnCount(); dsIndex++) {
-			if (dsName.equals(dsNames[dsIndex])) {
-				return getValues(dsIndex);
-			}
-		}
-		throw new RrdException("Datasource [" + dsName + "] not found");
-	}
-
-	/**
-	 * Returns a set of values created by applying RPN expression to the fetched data.
-	 * For example, if you have two datasources named <code>x</code> and <code>y</code>
-	 * in this FetchData and you want to calculate values for <code>(x+y)/2</code> use something like:
-	 * <p>
-	 * <code>getRpnValues("x,y,+,2,/");</code>
-	 *
-	 * @param rpnExpression RRDTool-like RPN expression
-	 * @return Calculated values
-	 * @throws RrdException Thrown if invalid RPN expression is supplied
-	 */
-	public double[] getRpnValues(String rpnExpression) throws RrdException {
-		DataProcessor dataProcessor = createDataProcessor(rpnExpression);
-		return dataProcessor.getValues(RPN_SOURCE_NAME);
-	}
-
-	/**
-	 * Returns {@link FetchRequest FetchRequest} object used to create this FetchData object.
-	 *
-	 * @return Fetch request object.
-	 */
-	public FetchRequest getRequest() {
-		return request;
-	}
-
-	/**
-	 * Returns the first timestamp in this FetchData object.
-	 *
-	 * @return The smallest timestamp.
-	 */
-	public long getFirstTimestamp() {
-		return timestamps[0];
-	}
-
-	/**
-	 * Returns the last timestamp in this FecthData object.
-	 *
-	 * @return The biggest timestamp.
-	 */
-	public long getLastTimestamp() {
-		return timestamps[timestamps.length - 1];
-	}
-
-	/**
-	 * Returns Archive object which is determined to be the best match for the
-	 * timestamps specified in the fetch request. All datasource values are obtained
-	 * from round robin archives belonging to this archive.
-	 *
-	 * @return Matching archive.
-	 */
-	public Archive getMatchingArchive() {
-		return matchingArchive;
-	}
-
-	/**
-	 * Returns array of datasource names found in the corresponding RRD. If the request
-	 * was filtered (data was fetched only for selected datasources), only datasources selected
-	 * for fetching are returned.
-	 *
-	 * @return Array of datasource names.
-	 */
-	public String[] getDsNames() {
-		return dsNames;
-	}
-
-	/**
-	 * Retrieve the table index number of a datasource by name.  Names are case sensitive.
-	 *
-	 * @param dsName Name of the datasource for which to find the index.
-	 * @return Index number of the datasources in the value table.
-	 */
-	public int getDsIndex(String dsName) {
-		// Let's assume the table of dsNames is always small, so it is not necessary to use a hashmap for lookups
-		for (int i = 0; i < dsNames.length; i++) {
-			if (dsNames[i].equals(dsName)) {
-				return i;
-			}
-		}
-		return -1;		// Datasource not found !
-	}
-
-	/**
-	 * Dumps the content of the whole FetchData object. Useful for debugging.
-	 *
-	 * @return String containing the contents of this object, for debugging.
-	 */
-	public String dump() {
-		StringBuffer buffer = new StringBuffer("");
-		for (int row = 0; row < getRowCount(); row++) {
-			buffer.append(timestamps[row]);
-			buffer.append(":  ");
-			for (int dsIndex = 0; dsIndex < getColumnCount(); dsIndex++) {
-				buffer.append(Util.formatDouble(values[dsIndex][row], true));
-				buffer.append("  ");
-			}
-			buffer.append("\n");
-		}
-		return buffer.toString();
-	}
-
-	/**
-	 * Returns string representing fetched data in a RRDTool-like form.
-	 *
-	 * @return Fetched data as a string in a rrdfetch-like output form.
-	 */
-	public String toString() {
-		// print header row
-		StringBuffer buff = new StringBuffer();
-		buff.append(padWithBlanks("", 10));
-		buff.append(" ");
-		for (String dsName : dsNames) {
-			buff.append(padWithBlanks(dsName, 18));
-		}
-		buff.append("\n \n");
-		for (int i = 0; i < timestamps.length; i++) {
-			buff.append(padWithBlanks("" + timestamps[i], 10));
-			buff.append(":");
-			for (int j = 0; j < dsNames.length; j++) {
-				double value = values[j][i];
-				String valueStr = Double.isNaN(value) ? "nan" : Util.formatDouble(value);
-				buff.append(padWithBlanks(valueStr, 18));
-			}
-			buff.append("\n");
-		}
-		return buff.toString();
-	}
-
-	private static String padWithBlanks(String input, int width) {
-		StringBuffer buff = new StringBuffer("");
-		int diff = width - input.length();
-		while (diff-- > 0) {
-			buff.append(' ');
-		}
-		buff.append(input);
-		return buff.toString();
-	}
-
-	/**
-	 * Returns single aggregated value from the fetched data for a single datasource.
-	 *
-	 * @param dsName	Datasource name
-	 * @param consolFun Consolidation function to be applied to fetched datasource values.
-	 *                  Valid consolidation functions are "MIN", "MAX", "LAST", "FIRST", "AVERAGE" and "TOTAL"
-	 *                  (these string constants are conveniently defined in the {@link ConsolFuns} class)
-	 * @return MIN, MAX, LAST, FIRST, AVERAGE or TOTAL value calculated from the fetched data
-	 *         for the given datasource name
-	 * @throws RrdException Thrown if the given datasource name cannot be found in fetched data.
-	 */
-	public double getAggregate(String dsName, String consolFun) throws RrdException {
-		DataProcessor dp = createDataProcessor(null);
-		return dp.getAggregate(dsName, consolFun);
-	}
-
-	/**
-	 * Returns aggregated value from the fetched data for a single datasource.
-	 * Before applying aggregation functions, specified RPN expression is applied to fetched data.
-	 * For example, if you have a gauge datasource named 'foots' but you want to find the maximum
-	 * fetched value in meters use something like:
-	 * <p>
-	 * <code>getAggregate("foots", "MAX", "foots,0.3048,*");</code>
-	 *
-	 * @param dsName		Datasource name
-	 * @param consolFun	 Consolidation function (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL)
-	 * @param rpnExpression RRDTool-like RPN expression
-	 * @return Aggregated value
-	 * @throws RrdException Thrown if the given datasource name cannot be found in fetched data, or if
-	 *                      invalid RPN expression is supplied
-	 * @throws IOException  Thrown in case of I/O error (unlikely to happen)
-	 * @deprecated This method is preserved just for backward compatibility.
-	 */
-	public double getAggregate(String dsName, String consolFun, String rpnExpression)
-			throws RrdException, IOException {
-		// for backward compatibility
-		rpnExpression = rpnExpression.replaceAll("value", dsName);
-		return getRpnAggregate(rpnExpression, consolFun);
-	}
-
-	/**
-	 * Returns aggregated value for a set of values calculated by applying an RPN expression to the
-	 * fetched data. For example, if you have two datasources named <code>x</code> and <code>y</code>
-	 * in this FetchData and you want to calculate MAX value of <code>(x+y)/2</code> use something like:
-	 * <p>
-	 * <code>getRpnAggregate("x,y,+,2,/", "MAX");</code>
-	 *
-	 * @param rpnExpression RRDTool-like RPN expression
-	 * @param consolFun	 Consolidation function (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL)
-	 * @return Aggregated value
-	 * @throws RrdException Thrown if invalid RPN expression is supplied
-	 */
-	public double getRpnAggregate(String rpnExpression, String consolFun) throws RrdException {
-		DataProcessor dataProcessor = createDataProcessor(rpnExpression);
-		return dataProcessor.getAggregate(RPN_SOURCE_NAME, consolFun);
-	}
-
-	/**
-	 * Returns all aggregated values (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL) calculated from the fetched data
-	 * for a single datasource.
-	 *
-	 * @param dsName Datasource name.
-	 * @return Simple object containing all aggregated values.
-	 * @throws RrdException Thrown if the given datasource name cannot be found in the fetched data.
-	 */
-	public Aggregates getAggregates(String dsName) throws RrdException {
-		DataProcessor dataProcessor = createDataProcessor(null);
-		return dataProcessor.getAggregates(dsName);
-	}
-
-	/**
-	 * Returns all aggregated values for a set of values calculated by applying an RPN expression to the
-	 * fetched data. For example, if you have two datasources named <code>x</code> and <code>y</code>
-	 * in this FetchData and you want to calculate MIN, MAX, LAST, FIRST, AVERAGE and TOTAL value
-	 * of <code>(x+y)/2</code> use something like:
-	 * <p>
-	 * <code>getRpnAggregates("x,y,+,2,/");</code>
-	 *
-	 * @param rpnExpression RRDTool-like RPN expression
-	 * @return Object containing all aggregated values
-	 * @throws RrdException Thrown if invalid RPN expression is supplied
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public Aggregates getRpnAggregates(String rpnExpression) throws RrdException, IOException {
-		DataProcessor dataProcessor = createDataProcessor(rpnExpression);
-		return dataProcessor.getAggregates(RPN_SOURCE_NAME);
-	}
-
-	/**
-	 * Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.
-	 * <p>
-	 * The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
-	 * of source data is discarded. It is used as a measure of the peak value used when one discounts
-	 * a fair amount for transitory spikes. This makes it markedly different from the average.
-	 * <p>
-	 * Read more about this topic at:
-	 * <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or<br>
-	 * <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
-	 *
-	 * @param dsName Datasource name
-	 * @return 95th percentile of fetched source values
-	 * @throws RrdException Thrown if invalid source name is supplied
-	 */
-	public double get95Percentile(String dsName) throws RrdException {
-		DataProcessor dataProcessor = createDataProcessor(null);
-		return dataProcessor.get95Percentile(dsName);
-	}
-
-	/**
-	 * Same as {@link #get95Percentile(String)}, but for a set of values calculated with the given
-	 * RPN expression.
-	 *
-	 * @param rpnExpression RRDTool-like RPN expression
-	 * @return 95-percentile
-	 * @throws RrdException Thrown if invalid RPN expression is supplied
-	 */
-	public double getRpn95Percentile(String rpnExpression) throws RrdException {
-		DataProcessor dataProcessor = createDataProcessor(rpnExpression);
-		return dataProcessor.get95Percentile(RPN_SOURCE_NAME);
-	}
-
-	/**
-	 * Dumps fetch data to output stream in XML format.
-	 *
-	 * @param outputStream Output stream to dump fetch data to
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public void exportXml(OutputStream outputStream) throws IOException {
-		XmlWriter writer = new XmlWriter(outputStream);
-		writer.startTag("fetch_data");
-		writer.startTag("request");
-		writer.writeTag("file", request.getParentDb().getPath());
-		writer.writeComment(Util.getDate(request.getFetchStart()));
-		writer.writeTag("start", request.getFetchStart());
-		writer.writeComment(Util.getDate(request.getFetchEnd()));
-		writer.writeTag("end", request.getFetchEnd());
-		writer.writeTag("resolution", request.getResolution());
-		writer.writeTag("cf", request.getConsolFun());
-		writer.closeTag(); // request
-		writer.startTag("datasources");
-		for (String dsName : dsNames) {
-			writer.writeTag("name", dsName);
-		}
-		writer.closeTag(); // datasources
-		writer.startTag("data");
-		for (int i = 0; i < timestamps.length; i++) {
-			writer.startTag("row");
-			writer.writeComment(Util.getDate(timestamps[i]));
-			writer.writeTag("timestamp", timestamps[i]);
-			writer.startTag("values");
-			for (int j = 0; j < dsNames.length; j++) {
-				writer.writeTag("v", values[j][i]);
-			}
-			writer.closeTag(); // values
-			writer.closeTag(); // row
-		}
-		writer.closeTag(); // data
-		writer.closeTag(); // fetch_data
-		writer.flush();
-	}
-
-	/**
-	 * Dumps fetch data to file in XML format.
-	 *
-	 * @param filepath Path to destination file
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public void exportXml(String filepath) throws IOException {
-		OutputStream outputStream = null;
-		try {
-			outputStream = new FileOutputStream(filepath);
-			exportXml(outputStream);
-		}
-		finally {
-			if (outputStream != null) {
-				outputStream.close();
-			}
-		}
-	}
-
-	/**
-	 * Dumps fetch data in XML format.
-	 *
-	 * @return String containing XML formatted fetch data
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public String exportXml() throws IOException {
-		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-		exportXml(outputStream);
-		return outputStream.toString();
-	}
-
-	/**
-	 * Returns the step of the corresponding RRA archive
-	 *
-	 * @return Archive step in seconds
-	 */
-	public long getArcStep() {
-		return arcStep;
-	}
-
-	/**
-	 * Returns the timestamp of the last populated slot in the corresponding RRA archive
-	 *
-	 * @return Timestamp in seconds
-	 */
-	public long getArcEndTime() {
-		return arcEndTime;
-	}
-
-	private DataProcessor createDataProcessor(String rpnExpression) throws RrdException {
-		DataProcessor dataProcessor = new DataProcessor(request.getFetchStart(), request.getFetchEnd());
-		for (String dsName : dsNames) {
-			dataProcessor.addDatasource(dsName, this);
-		}
-		if (rpnExpression != null) {
-			dataProcessor.addDatasource(RPN_SOURCE_NAME, rpnExpression);
-			try {
-				dataProcessor.processData();
-			}
-			catch (IOException ioe) {
-				// highly unlikely, since all datasources have already calculated values
-				throw new RuntimeException("Impossible error: " + ioe);
-			}
-		}
-		return dataProcessor;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/FetchRequest.java b/apps/jrobin/java/src/org/jrobin/core/FetchRequest.java
deleted file mode 100644
index 5722acd9759f472c94b8ba50a3b72530a8e07887..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/FetchRequest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.util.Set;
-
-/**
- * Class to represent fetch request. For the complete explanation of all
- * fetch parameters consult RRDTool's
- * <a href="../../../../man/rrdfetch.html" target="man">rrdfetch man page</a>.
- * <p>
- * You cannot create <code>FetchRequest</code> directly (no public constructor
- * is provided). Use {@link RrdDb#createFetchRequest(String, long, long, long)
- * createFetchRequest()} method of your {@link RrdDb RrdDb} object.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class FetchRequest {
-	private RrdDb parentDb;
-	private String consolFun;
-	private long fetchStart;
-	private long fetchEnd;
-	private long resolution;
-	private String[] filter;
-
-	public FetchRequest(RrdDb parentDb, String consolFun, long fetchStart, long fetchEnd, long resolution) throws RrdException {
-		this.parentDb = parentDb;
-		this.consolFun = consolFun;
-		this.fetchStart = fetchStart;
-		this.fetchEnd = fetchEnd;
-		this.resolution = resolution;
-		validate();
-	}
-
-	/**
-	 * Sets request filter in order to fetch data only for
-	 * the specified array of datasources (datasource names).
-	 * If not set (or set to null), fetched data will
-	 * containt values of all datasources defined in the corresponding RRD.
-	 * To fetch data only from selected
-	 * datasources, specify an array of datasource names as method argument.
-	 *
-	 * @param filter Array of datsources (datsource names) to fetch data from.
-	 */
-	public void setFilter(String[] filter) {
-		this.filter = filter;
-	}
-
-	/**
-	 * Sets request filter in order to fetch data only for
-	 * the specified set of datasources (datasource names).
-	 * If the filter is not set (or set to null), fetched data will
-	 * containt values of all datasources defined in the corresponding RRD.
-	 * To fetch data only from selected
-	 * datasources, specify a set of datasource names as method argument.
-	 *
-	 * @param filter Set of datsource names to fetch data for.
-	 */
-	public void setFilter(Set<String> filter) {
-		this.filter = filter.toArray(new String[0]);
-	}
-
-	/**
-	 * Sets request filter in order to fetch data only for
-	 * a single datasource (datasource name).
-	 * If not set (or set to null), fetched data will
-	 * containt values of all datasources defined in the corresponding RRD.
-	 * To fetch data for a single datasource only,
-	 * specify an array of datasource names as method argument.
-	 *
-	 * @param filter Array of datsources (datsource names) to fetch data from.
-	 */
-	public void setFilter(String filter) {
-		this.filter = (filter == null) ? null : (new String[] {filter});
-	}
-
-	/**
-	 * Returns request filter. See {@link #setFilter(String[]) setFilter()} for
-	 * complete explanation.
-	 *
-	 * @return Request filter (array of datasource names), null if not set.
-	 */
-	public String[] getFilter() {
-		return filter;
-	}
-
-	/**
-	 * Returns consolitation function to be used during the fetch process.
-	 *
-	 * @return Consolidation function.
-	 */
-	public String getConsolFun() {
-		return consolFun;
-	}
-
-	/**
-	 * Returns starting timestamp to be used for the fetch request.
-	 *
-	 * @return Starting timstamp in seconds.
-	 */
-	public long getFetchStart() {
-		return fetchStart;
-	}
-
-	/**
-	 * Returns ending timestamp to be used for the fetch request.
-	 *
-	 * @return Ending timestamp in seconds.
-	 */
-	public long getFetchEnd() {
-		return fetchEnd;
-	}
-
-	/**
-	 * Returns fetch resolution to be used for the fetch request.
-	 *
-	 * @return Fetch resolution in seconds.
-	 */
-	public long getResolution() {
-		return resolution;
-	}
-
-	private void validate() throws RrdException {
-		if (!ArcDef.isValidConsolFun(consolFun)) {
-			throw new RrdException("Invalid consolidation function in fetch request: " + consolFun);
-		}
-		if (fetchStart < 0) {
-			throw new RrdException("Invalid start time in fetch request: " + fetchStart);
-		}
-		if (fetchEnd < 0) {
-			throw new RrdException("Invalid end time in fetch request: " + fetchEnd);
-		}
-		if (fetchStart > fetchEnd) {
-			throw new RrdException("Invalid start/end time in fetch request: " + fetchStart +
-					" > " + fetchEnd);
-		}
-		if (resolution <= 0) {
-			throw new RrdException("Invalid resolution in fetch request: " + resolution);
-		}
-	}
-
-	/**
-	 * Dumps the content of fetch request using the syntax of RRDTool's fetch command.
-	 *
-	 * @return Fetch request dump.
-	 */
-	public String dump() {
-		return "fetch \"" + parentDb.getRrdBackend().getPath() +
-				"\" " + consolFun + " --start " + fetchStart + " --end " + fetchEnd +
-				(resolution > 1 ? " --resolution " + resolution : "");
-	}
-
-	String getRrdToolCommand() {
-		return dump();
-	}
-
-	/**
-	 * Returns data from the underlying RRD and puts it in a single
-	 * {@link FetchData FetchData} object.
-	 *
-	 * @return FetchData object filled with timestamps and datasource values.
-	 * @throws RrdException Thrown in case of JRobin specific error.
-	 * @throws IOException  Thrown in case of I/O error.
-	 */
-	public FetchData fetchData() throws RrdException, IOException {
-		return parentDb.fetchData(this);
-	}
-
-	/**
-	 * Returns the underlying RrdDb object.
-	 *
-	 * @return RrdDb object used to create this FetchRequest object.
-	 */
-	public RrdDb getParentDb() {
-		return parentDb;
-	}
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/Header.java b/apps/jrobin/java/src/org/jrobin/core/Header.java
deleted file mode 100644
index faecb02a8f9f5e6e2cf6a3c88bf4da6056f862b5..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/Header.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Class to represent RRD header. Header information is mainly static (once set, it
- * cannot be changed), with the exception of last update time (this value is changed whenever
- * RRD gets updated).
- * <p>
- * Normally, you don't need to manipulate the Header object directly - JRobin framework
- * does it for you.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>*
- */
-public class Header implements RrdUpdater {
-	static final int SIGNATURE_LENGTH = 2;
-	static final String SIGNATURE = "JR";
-
-	static final String DEFAULT_SIGNATURE = "JRobin, version 0.1";
-	static final String RRDTOOL_VERSION = "0001";
-
-	private RrdDb parentDb;
-
-	private RrdString signature;
-	private RrdLong step;
-	private RrdInt dsCount, arcCount;
-	private RrdLong lastUpdateTime;
-	private Long m_primitiveStep = null;
-	private Integer m_primitiveDsCount = null;
-	private Integer m_primitiveArcCount = null;
-
-	Header(final RrdDb parentDb, final RrdDef rrdDef) throws IOException {
-	    final boolean shouldInitialize = rrdDef != null;
-		this.parentDb = parentDb;
-		signature = new RrdString(this);			 // NOT constant, may NOT be cached
-		step = new RrdLong(this, true);			 // constant, may be cached
-		dsCount = new RrdInt(this, true);			 // constant, may be cached
-		arcCount = new RrdInt(this, true);			 // constant, may be cached
-		lastUpdateTime = new RrdLong(this);
-		if (shouldInitialize) {
-			signature.set(DEFAULT_SIGNATURE);
-			step.set(rrdDef.getStep());
-			dsCount.set(rrdDef.getDsCount());
-			arcCount.set(rrdDef.getArcCount());
-			lastUpdateTime.set(rrdDef.getStartTime());
-		}
-	}
-
-	Header(final RrdDb parentDb, final DataImporter reader) throws IOException, RrdException {
-		this(parentDb, (RrdDef) null);
-		final String version = reader.getVersion();
-		final int intVersion = Integer.parseInt(version);
-		if (intVersion > 3) {
-			throw new RrdException("Could not unserialize xml version " + version);
-		}
-		signature.set(DEFAULT_SIGNATURE);
-		step.set(reader.getStep());
-		dsCount.set(reader.getDsCount());
-		arcCount.set(reader.getArcCount());
-		lastUpdateTime.set(reader.getLastUpdateTime());
-	}
-
-	/**
-	 * Returns RRD signature. Initially, the returned string will be
-	 * of the form <b><i>JRobin, version x.x</i></b>. Note: RRD format did not
-	 * change since Jrobin 1.0.0 release (and probably never will).
-	 *
-	 * @return RRD signature
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public String getSignature() throws IOException {
-		return signature.get();
-	}
-
-	public String getInfo() throws IOException {
-		return getSignature().substring(SIGNATURE_LENGTH);
-	}
-
-	public void setInfo(String info) throws IOException {
-		if (info != null && info.length() > 0) {
-			signature.set(SIGNATURE + info);
-		}
-		else {
-			signature.set(SIGNATURE);
-		}
-	}
-
-	/**
-	 * Returns the last update time of the RRD.
-	 *
-	 * @return Timestamp (Unix epoch, no milliseconds) corresponding to the last update time.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public long getLastUpdateTime() throws IOException {
-		return lastUpdateTime.get();
-	}
-
-	/**
-	 * Returns primary RRD time step.
-	 *
-	 * @return Primary time step in seconds
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public long getStep() throws IOException {
-	    if (m_primitiveStep == null) {
-	        m_primitiveStep = step.get();
-	    }
-	    return m_primitiveStep;
-	}
-
-	/**
-	 * Returns the number of datasources defined in the RRD.
-	 *
-	 * @return Number of datasources defined
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public int getDsCount() throws IOException {
-	    if (m_primitiveDsCount == null) {
-	        m_primitiveDsCount = dsCount.get();
-	    }
-	    return m_primitiveDsCount;
-	}
-
-	/**
-	 * Returns the number of archives defined in the RRD.
-	 *
-	 * @return Number of archives defined
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public int getArcCount() throws IOException {
-	    if (m_primitiveArcCount == null) {
-	        m_primitiveArcCount = arcCount.get();
-	    }
-	    return m_primitiveArcCount;
-	}
-
-	public void setLastUpdateTime(final long lastUpdateTime) throws IOException {
-		this.lastUpdateTime.set(lastUpdateTime);
-	}
-
-	String dump() throws IOException {
-		return "== HEADER ==\n" +
-				"signature:" + getSignature() +
-				" lastUpdateTime:" + getLastUpdateTime() +
-				" step:" + getStep() +
-				" dsCount:" + getDsCount() +
-				" arcCount:" + getArcCount() + "\n";
-	}
-
-	void appendXml(XmlWriter writer) throws IOException {
-		writer.writeComment(signature.get());
-		writer.writeTag("version", RRDTOOL_VERSION);
-		writer.writeComment("Seconds");
-		writer.writeTag("step", step.get());
-		writer.writeComment(Util.getDate(lastUpdateTime.get()));
-		writer.writeTag("lastupdate", lastUpdateTime.get());
-	}
-
-	/**
-	 * Copies object's internal state to another Header object.
-	 *
-	 * @param other New Header object to copy state to
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if supplied argument is not a Header object
-	 */
-	public void copyStateTo(final RrdUpdater other) throws IOException, RrdException {
-		if (!(other instanceof Header)) {
-			throw new RrdException( "Cannot copy Header object to " + other.getClass().getName());
-		}
-		final Header header = (Header) other;
-		header.signature.set(signature.get());
-		header.lastUpdateTime.set(lastUpdateTime.get());
-	}
-
-	/**
-	 * Returns the underlying storage (backend) object which actually performs all
-	 * I/O operations.
-	 *
-	 * @return I/O backend object
-	 */
-	public RrdBackend getRrdBackend() {
-		return parentDb.getRrdBackend();
-	}
-
-	boolean isJRobinHeader() throws IOException {
-		return signature.get().startsWith(SIGNATURE);
-	}
-
-	void validateHeader() throws IOException, RrdException {
-		if (!isJRobinHeader()) {
-			throw new RrdException("Invalid file header. File [" + parentDb.getCanonicalPath() + "] is not a JRobin RRD file");
-		}
-	}
-
-	/**
-	 * Required to implement RrdUpdater interface. You should never call this method directly.
-	 *
-	 * @return Allocator object
-	 */
-	public RrdAllocator getRrdAllocator() {
-		return parentDb.getRrdAllocator();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/Robin.java b/apps/jrobin/java/src/org/jrobin/core/Robin.java
deleted file mode 100644
index 623bc80a9462881030b6dbe22dd328e1262697c7..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/Robin.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Class to represent archive values for a single datasource. Robin class is the heart of
- * the so-called "round robin database" concept. Basically, each Robin object is a
- * fixed length array of double values. Each double value reperesents consolidated, archived
- * value for the specific timestamp. When the underlying array of double values gets completely
- * filled, new values will replace the oldest ones.
- * <p>
- * Robin object does not hold values in memory - such object could be quite large.
- * Instead of it, Robin reads them from the backend I/O only when necessary.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class Robin implements RrdUpdater {
-	private Archive parentArc;
-	private RrdInt pointer;
-	private RrdDoubleArray values;
-	private int rows;
-
-	Robin(Archive parentArc, int rows, boolean shouldInitialize) throws IOException {
-		this.parentArc = parentArc;
-		this.pointer = new RrdInt(this);
-		this.values = new RrdDoubleArray(this, rows);
-		this.rows = rows;
-		if (shouldInitialize) {
-			pointer.set(0);
-			values.set(0, Double.NaN, rows);
-		}
-	}
-
-	/**
-	 * Fetches all archived values.
-	 *
-	 * @return Array of double archive values, starting from the oldest one.
-	 * @throws IOException Thrown in case of I/O specific error.
-	 */
-	public double[] getValues() throws IOException {
-		return getValues(0, rows);
-	}
-
-	// stores single value
-	void store(double newValue) throws IOException {
-		int position = pointer.get();
-		values.set(position, newValue);
-		pointer.set((position + 1) % rows);
-	}
-
-	// stores the same value several times
-	void bulkStore(double newValue, int bulkCount) throws IOException {
-		assert bulkCount <= rows: "Invalid number of bulk updates: " + bulkCount +
-				" rows=" + rows;
-		int position = pointer.get();
-		// update tail
-		int tailUpdateCount = Math.min(rows - position, bulkCount);
-		values.set(position, newValue, tailUpdateCount);
-		pointer.set((position + tailUpdateCount) % rows);
-		// do we need to update from the start?
-		int headUpdateCount = bulkCount - tailUpdateCount;
-		if (headUpdateCount > 0) {
-			values.set(0, newValue, headUpdateCount);
-			pointer.set(headUpdateCount);
-		}
-	}
-
-	void update(double[] newValues) throws IOException {
-		assert rows == newValues.length: "Invalid number of robin values supplied (" + newValues.length +
-				"), exactly " + rows + " needed";
-		pointer.set(0);
-		values.writeDouble(0, newValues);
-	}
-
-	/**
-	 * Updates archived values in bulk.
-	 *
-	 * @param newValues Array of double values to be stored in the archive
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if the length of the input array is different from the length of
-	 *                      this archive
-	 */
-	public void setValues(double[] newValues) throws IOException, RrdException {
-		if (rows != newValues.length) {
-			throw new RrdException("Invalid number of robin values supplied (" + newValues.length +
-					"), exactly " + rows + " needed");
-		}
-		update(newValues);
-	}
-
-	/**
-	 * (Re)sets all values in this archive to the same value.
-	 *
-	 * @param newValue New value
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public void setValues(double newValue) throws IOException {
-		double[] values = new double[rows];
-		for (int i = 0; i < values.length; i++) {
-			values[i] = newValue;
-		}
-		update(values);
-	}
-
-	String dump() throws IOException {
-		StringBuffer buffer = new StringBuffer("Robin " + pointer.get() + "/" + rows + ": ");
-		double[] values = getValues();
-		for (double value : values) {
-			buffer.append(Util.formatDouble(value, true)).append(" ");
-		}
-		buffer.append("\n");
-		return buffer.toString();
-	}
-
-	/**
-	 * Returns the i-th value from the Robin archive.
-	 *
-	 * @param index Value index
-	 * @return Value stored in the i-th position (the oldest value has zero index)
-	 * @throws IOException Thrown in case of I/O specific error.
-	 */
-	public double getValue(int index) throws IOException {
-		int arrayIndex = (pointer.get() + index) % rows;
-		return values.get(arrayIndex);
-	}
-
-	/**
-	 * Sets the i-th value in the Robin archive.
-	 *
-	 * @param index index in the archive (the oldest value has zero index)
-	 * @param value value to be stored
-	 * @throws IOException Thrown in case of I/O specific error.
-	 */
-	public void setValue(int index, double value) throws IOException {
-		int arrayIndex = (pointer.get() + index) % rows;
-		values.set(arrayIndex, value);
-	}
-
-	double[] getValues(int index, int count) throws IOException {
-		assert count <= rows: "Too many values requested: " + count + " rows=" + rows;
-		int startIndex = (pointer.get() + index) % rows;
-		int tailReadCount = Math.min(rows - startIndex, count);
-		double[] tailValues = values.get(startIndex, tailReadCount);
-		if (tailReadCount < count) {
-			int headReadCount = count - tailReadCount;
-			double[] headValues = values.get(0, headReadCount);
-			double[] values = new double[count];
-			int k = 0;
-			for (double tailValue : tailValues) {
-				values[k++] = tailValue;
-			}
-			for (double headValue : headValues) {
-				values[k++] = headValue;
-			}
-			return values;
-		}
-		else {
-			return tailValues;
-		}
-	}
-
-	/**
-	 * Returns the Archive object to which this Robin object belongs.
-	 *
-	 * @return Parent Archive object
-	 */
-	public Archive getParent() {
-		return parentArc;
-	}
-
-	/**
-	 * Returns the size of the underlying array of archived values.
-	 *
-	 * @return Number of stored values
-	 */
-	public int getSize() {
-		return rows;
-	}
-
-	/**
-	 * Copies object's internal state to another Robin object.
-	 *
-	 * @param other New Robin object to copy state to
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if supplied argument is not a Robin object
-	 */
-	public void copyStateTo(RrdUpdater other) throws IOException, RrdException {
-		if (!(other instanceof Robin)) {
-			throw new RrdException(
-					"Cannot copy Robin object to " + other.getClass().getName());
-		}
-		Robin robin = (Robin) other;
-		int rowsDiff = rows - robin.rows;
-		if (rowsDiff == 0) {
-			// Identical dimensions. Do copy in BULK to speed things up
-			robin.pointer.set(pointer.get());
-			robin.values.writeBytes(values.readBytes());
-		}
-		else {
-			// different sizes
-			for (int i = 0; i < robin.rows; i++) {
-				int j = i + rowsDiff;
-				robin.store(j >= 0 ? getValue(j) : Double.NaN);
-			}
-		}
-	}
-
-	/**
-	 * Filters values stored in this archive based on the given boundary.
-	 * Archived values found to be outside of <code>[minValue, maxValue]</code> interval (inclusive)
-	 * will be silently replaced with <code>NaN</code>.
-	 *
-	 * @param minValue lower boundary
-	 * @param maxValue upper boundary
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public void filterValues(double minValue, double maxValue) throws IOException {
-		for (int i = 0; i < rows; i++) {
-			double value = values.get(i);
-			if (!Double.isNaN(minValue) && !Double.isNaN(value) && minValue > value) {
-				values.set(i, Double.NaN);
-			}
-			if (!Double.isNaN(maxValue) && !Double.isNaN(value) && maxValue < value) {
-				values.set(i, Double.NaN);
-			}
-		}
-	}
-
-	/**
-	 * Returns the underlying storage (backend) object which actually performs all
-	 * I/O operations.
-	 *
-	 * @return I/O backend object
-	 */
-	public RrdBackend getRrdBackend() {
-		return parentArc.getRrdBackend();
-	}
-
-	/**
-	 * Required to implement RrdUpdater interface. You should never call this method directly.
-	 *
-	 * @return Allocator object
-	 */
-	public RrdAllocator getRrdAllocator() {
-		return parentArc.getRrdAllocator();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdAllocator.java b/apps/jrobin/java/src/org/jrobin/core/RrdAllocator.java
deleted file mode 100644
index f3b20586309419f46e888f0f6974fa984aeb7905..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdAllocator.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-class RrdAllocator {
-	private long allocationPointer = 0L;
-
-	long allocate(long byteCount) throws IOException {
-		long pointer = allocationPointer;
-		allocationPointer += byteCount;
-		return pointer;
-	}
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdBackend.java b/apps/jrobin/java/src/org/jrobin/core/RrdBackend.java
deleted file mode 100644
index d15c5857b89ad95ec0a807f59c370e23cdb2e422..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdBackend.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Base implementation class for all backend classes. Each Round Robin Database object
- * ({@link RrdDb} object) is backed with a single RrdBackend object which performs
- * actual I/O operations on the underlying storage. JRobin supports
- * three different bakcends out of the box:
- * <p>
- * <ul>
- * <li>{@link RrdFileBackend}: objects of this class are created from the
- * {@link RrdFileBackendFactory} class. This was the default backend used in all
- * JRobin releases prior to 1.4.0. It uses java.io.* package and
- * RandomAccessFile class to store RRD data in files on the disk.
- * <p>
- * <li>{@link RrdNioBackend}: objects of this class are created from the
- * {@link RrdNioBackendFactory} class. The backend uses java.io.* and java.nio.*
- * classes (mapped ByteBuffer) to store RRD data in files on the disk. This backend is fast, very fast,
- * but consumes a lot of memory (borrowed not from the JVM but from the underlying operating system
- * directly). <b>This is the default backend used in JRobin since 1.4.0 release.</b>
- * <p>
- * <li>{@link RrdMemoryBackend}: objects of this class are created from the
- * {@link RrdMemoryBackendFactory} class. This backend stores all data in memory. Once
- * JVM exits, all data gets lost. The backend is extremely fast and memory hungry.
- * </ul>
- * <p>
- * To create your own backend in order to provide some custom type of RRD storage,
- * you should do the following:
- * <p>
- * <ul>
- * <li>Create your custom RrdBackend class (RrdCustomBackend, for example)
- * by extending RrdBackend class. You have to implement all abstract methods defined
- * in the base class.
- * <p>
- * <li>Create your custom RrdBackendFactory class (RrdCustomBackendFactory,
- * for example) by extending RrdBackendFactory class. You have to implement all
- * abstract methods defined in the base class. Your custom factory class will actually
- * create custom backend objects when necessary.
- * <p>
- * <li>Create instance of your custom RrdBackendFactory and register it as a regular
- * factory available to JRobin framework. See javadoc for {@link RrdBackendFactory} to
- * find out how to do this
- * </ul>
- */
-public abstract class RrdBackend {
-	private static boolean s_instanceCreated = false;
-	private String m_path = null;
-	private boolean m_readOnly = false;
-
-	/**
-	 * Creates backend for a RRD storage with the given path.
-	 *
-	 * @param path String identifying RRD storage. For files on the disk, this
-	 *             argument should represent file path. Other storage types might interpret
-	 *             this argument differently.
-	 */
-	protected RrdBackend(final String path) {
-	    this(path, false);
-	}
-
-	protected RrdBackend(final String path, final boolean readOnly) {
-        m_path = path;
-	    m_readOnly = readOnly;
-	    RrdBackend.setInstanceCreated();
-	}
-
-	/**
-	 * Returns path to the storage.
-	 *
-	 * @return Storage path
-	 */
-	public String getPath() {
-		return m_path;
-	}
-
-	/**
-	 * Is the RRD ReadOnly?
-	 *
-	 * @return True if the RRD is read only, false if not.
-	 */
-	public boolean isReadOnly() {
-	    return m_readOnly;
-	}
-
-	/**
-	 * Writes an array of bytes to the underlying storage starting from the given
-	 * storage offset.
-	 *
-	 * @param offset Storage offset.
-	 * @param b	  Array of bytes that should be copied to the underlying storage
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	protected abstract void write(long offset, byte[] b) throws IOException;
-
-	/**
-	 * Reads an array of bytes from the underlying storage starting from the given
-	 * storage offset.
-	 *
-	 * @param offset Storage offset.
-	 * @param b	  Array which receives bytes from the underlying storage
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	protected abstract void read(long offset, byte[] b) throws IOException;
-
-	/**
-	 * Returns the number of RRD bytes in the underlying storage.
-	 *
-	 * @return Number of RRD bytes in the storage.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public abstract long getLength() throws IOException;
-
-	/**
-	 * Sets the number of bytes in the underlying RRD storage.
-	 * This method is called only once, immediately after a new RRD storage gets created.
-	 *
-	 * @param length Length of the underlying RRD storage in bytes.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected abstract void setLength(long length) throws IOException;
-
-	/**
-	 * Closes the underlying backend.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public void close() throws IOException {
-	}
-
-	/**
-	 * This method suggests the caching policy to the JRobin frontend (high-level) classes. If <code>true</code>
-	 * is returned, frontent classes will cache frequently used parts of a RRD file in memory to improve
-	 * performance. If <code>false</code> is returned, high level classes will never cache RRD file sections
-	 * in memory.
-	 *
-	 * @return <code>true</code> if file caching is enabled, <code>false</code> otherwise. By default, the
-	 *         method returns <code>true</code> but it can be overriden in subclasses.
-	 */
-	protected boolean isCachingAllowed() {
-		return true;
-	}
-
-	/**
-	 * Reads all RRD bytes from the underlying storage
-	 *
-	 * @return RRD bytes
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public final byte[] readAll() throws IOException {
-	    final byte[] b = new byte[(int) getLength()];
-		read(0, b);
-		return b;
-	}
-
-	final void writeInt(final long offset, final int value) throws IOException {
-		write(offset, getIntBytes(value));
-	}
-
-	final void writeLong(final long offset, final long value) throws IOException {
-		write(offset, getLongBytes(value));
-	}
-
-	final void writeDouble(final long offset, final double value) throws IOException {
-		write(offset, getDoubleBytes(value));
-	}
-
-	final void writeDouble(final long offset, final double value, final int count) throws IOException {
-	    final byte[] b = getDoubleBytes(value);
-		final byte[] image = new byte[8 * count];
-		for (int i = 0, k = 0; i < count; i++) {
-			image[k++] = b[0];
-			image[k++] = b[1];
-			image[k++] = b[2];
-			image[k++] = b[3];
-			image[k++] = b[4];
-			image[k++] = b[5];
-			image[k++] = b[6];
-			image[k++] = b[7];
-		}
-		write(offset, image);
-	}
-
-	final void writeDouble(final long offset, final double[] values) throws IOException {
-		final int count = values.length;
-		final byte[] image = new byte[8 * count];
-		for (int i = 0, k = 0; i < count; i++) {
-			final byte[] b = getDoubleBytes(values[i]);
-			image[k++] = b[0];
-			image[k++] = b[1];
-			image[k++] = b[2];
-			image[k++] = b[3];
-			image[k++] = b[4];
-			image[k++] = b[5];
-			image[k++] = b[6];
-			image[k++] = b[7];
-		}
-		write(offset, image);
-	}
-
-	final void writeString(final long offset, final String rawValue) throws IOException {
-	    final String value = rawValue.trim();
-	    final byte[] b = new byte[RrdPrimitive.STRING_LENGTH * 2];
-		for (int i = 0, k = 0; i < RrdPrimitive.STRING_LENGTH; i++) {
-			final char c = (i < value.length()) ? value.charAt(i) : ' ';
-			final byte[] cb = getCharBytes(c);
-			b[k++] = cb[0];
-			b[k++] = cb[1];
-		}
-		write(offset, b);
-	}
-
-	final int readInt(final long offset) throws IOException {
-	    final byte[] b = new byte[4];
-		read(offset, b);
-		return getInt(b);
-	}
-
-	final long readLong(final long offset) throws IOException {
-	    final byte[] b = new byte[8];
-		read(offset, b);
-		return getLong(b);
-	}
-
-	final double readDouble(final long offset) throws IOException {
-	    final byte[] b = new byte[8];
-		read(offset, b);
-		return getDouble(b);
-	}
-
-	final double[] readDouble(final long offset, final int count) throws IOException {
-	    final int byteCount = 8 * count;
-		final byte[] image = new byte[byteCount];
-		read(offset, image);
-		final double[] values = new double[count];
-		for (int i = 0, k = -1; i < count; i++) {
-			final byte[] b = new byte[] {
-					image[++k], image[++k], image[++k], image[++k],
-					image[++k], image[++k], image[++k], image[++k]
-			};
-			values[i] = getDouble(b);
-		}
-		return values;
-	}
-
-	final String readString(final long offset) throws IOException {
-	    final byte[] b = new byte[RrdPrimitive.STRING_LENGTH * 2];
-		final char[] c = new char[RrdPrimitive.STRING_LENGTH];
-		read(offset, b);
-		for (int i = 0, k = -1; i < RrdPrimitive.STRING_LENGTH; i++) {
-			final byte[] cb = new byte[] {b[++k], b[++k]};
-			c[i] = getChar(cb);
-		}
-		return new String(c).trim();
-	}
-
-	// static helper methods
-
-	private static byte[] getIntBytes(final int value) {
-	    final byte[] b = new byte[4];
-		b[0] = (byte) ((value >>> 24) & 0xFF);
-		b[1] = (byte) ((value >>> 16) & 0xFF);
-		b[2] = (byte) ((value >>> 8) & 0xFF);
-		b[3] = (byte) ((value) & 0xFF);
-		return b;
-	}
-
-	private static byte[] getLongBytes(final long value) {
-	    final byte[] b = new byte[8];
-		b[0] = (byte) ((int) (value >>> 56) & 0xFF);
-		b[1] = (byte) ((int) (value >>> 48) & 0xFF);
-		b[2] = (byte) ((int) (value >>> 40) & 0xFF);
-		b[3] = (byte) ((int) (value >>> 32) & 0xFF);
-		b[4] = (byte) ((int) (value >>> 24) & 0xFF);
-		b[5] = (byte) ((int) (value >>> 16) & 0xFF);
-		b[6] = (byte) ((int) (value >>> 8) & 0xFF);
-		b[7] = (byte) ((int) (value) & 0xFF);
-		return b;
-	}
-
-	private static byte[] getCharBytes(final char value) {
-	    final byte[] b = new byte[2];
-		b[0] = (byte) ((value >>> 8) & 0xFF);
-		b[1] = (byte) ((value) & 0xFF);
-		return b;
-	}
-
-	private static byte[] getDoubleBytes(final double value) {
-		return getLongBytes(Double.doubleToLongBits(value));
-	}
-
-	private static int getInt(final byte[] b) {
-		assert b.length == 4: "Invalid number of bytes for integer conversion";
-		return ((b[0] << 24) & 0xFF000000) + ((b[1] << 16) & 0x00FF0000) +
-				((b[2] << 8) & 0x0000FF00) + (b[3] & 0x000000FF);
-	}
-
-	private static long getLong(final byte[] b) {
-		assert b.length == 8: "Invalid number of bytes for long conversion";
-		int high = getInt(new byte[] {b[0], b[1], b[2], b[3]});
-		int low = getInt(new byte[] {b[4], b[5], b[6], b[7]});
-		return ((long) (high) << 32) + (low & 0xFFFFFFFFL);
-	}
-
-	private static char getChar(final byte[] b) {
-		assert b.length == 2: "Invalid number of bytes for char conversion";
-		return (char) (((b[0] << 8) & 0x0000FF00)
-				+ (b[1] & 0x000000FF));
-	}
-
-	private static double getDouble(final byte[] b) {
-		assert b.length == 8: "Invalid number of bytes for double conversion";
-		return Double.longBitsToDouble(getLong(b));
-	}
-
-	private static void setInstanceCreated() {
-		s_instanceCreated = true;
-	}
-
-	static boolean isInstanceCreated() {
-		return s_instanceCreated;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdBackendFactory.java b/apps/jrobin/java/src/org/jrobin/core/RrdBackendFactory.java
deleted file mode 100644
index 42ebbd7610f38fc2f9094172df2c6e0115dbc161..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdBackendFactory.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-/**
- * Base (abstract) backend factory class which holds references to all concrete
- * backend factories and defines abstract methods which must be implemented in
- * all concrete factory implementations.
- * <p>
- * Factory classes are used to create concrete {@link RrdBackend} implementations.
- * Each factory creates unlimited number of specific backend objects.
- * <p>
- * JRobin supports four different backend types (backend factories) out of the box:<p>
- * <ul>
- * <li>{@link RrdFileBackend}: objects of this class are created from the
- * {@link RrdFileBackendFactory} class. This was the default backend used in all
- * JRobin releases before 1.4.0 release. It uses java.io.* package and RandomAccessFile class to store
- * RRD data in files on the disk.
- * <p>
- * <li>{@link RrdSafeFileBackend}: objects of this class are created from the
- * {@link RrdSafeFileBackendFactory} class. It uses java.io.* package and RandomAccessFile class to store
- * RRD data in files on the disk. This backend is SAFE:
- * it locks the underlying RRD file during update/fetch operations, and caches only static
- * parts of a RRD file in memory. Therefore, this backend is safe to be used when RRD files should
- * be shared <b>between several JVMs</b> at the same time. However, this backend is *slow* since it does
- * not use fast java.nio.* package (it's still based on the RandomAccessFile class).
- * <p>
- * <li>{@link RrdNioBackend}: objects of this class are created from the
- * {@link RrdNioBackendFactory} class. The backend uses java.io.* and java.nio.*
- * classes (mapped ByteBuffer) to store RRD data in files on the disk. This is the default backend
- * since 1.4.0 release.
- * <p>
- * <li>{@link RrdMemoryBackend}: objects of this class are created from the
- * {@link RrdMemoryBackendFactory} class. This backend stores all data in memory. Once
- * JVM exits, all data gets lost. The backend is extremely fast and memory hungry.
- * </ul>
- * <p>
- * Each backend factory is identifed by its {@link #getFactoryName() name}. Constructors
- * are provided in the {@link RrdDb} class to create RrdDb objects (RRD databases)
- * backed with a specific backend.
- * <p>
- * See javadoc for {@link RrdBackend} to find out how to create your custom backends.
- */
-public abstract class RrdBackendFactory {
-	private static final HashMap<String, RrdBackendFactory> factories = new HashMap<String, RrdBackendFactory>();
-	private static RrdBackendFactory defaultFactory;
-
-	static {
-		try {
-			RrdFileBackendFactory fileFactory = new RrdFileBackendFactory();
-			registerFactory(fileFactory);
-			RrdJRobin14FileBackendFactory jrobin14Factory = new RrdJRobin14FileBackendFactory();
-			registerFactory(jrobin14Factory);
-			RrdMemoryBackendFactory memoryFactory = new RrdMemoryBackendFactory();
-			registerFactory(memoryFactory);
-			RrdNioBackendFactory nioFactory = new RrdNioBackendFactory();
-			registerFactory(nioFactory);
-			RrdSafeFileBackendFactory safeFactory = new RrdSafeFileBackendFactory();
-			registerFactory(safeFactory);
-			RrdNioByteBufferBackendFactory nioByteBufferFactory = new RrdNioByteBufferBackendFactory();
-			registerFactory(nioByteBufferFactory);
-			selectDefaultFactory();
-		}
-		catch (RrdException e) {
-			throw new RuntimeException("FATAL: Cannot register RRD backend factories: " + e);
-		}
-	}
-
-	private static void selectDefaultFactory() throws RrdException {
-		setDefaultFactory("FILE");
-	}
-
-	/**
-	 * Returns backend factory for the given backend factory name.
-	 *
-	 * @param name Backend factory name. Initially supported names are:<p>
-	 *             <ul>
-	 *             <li><b>FILE</b>: Default factory which creates backends based on the
-	 *             java.io.* package. RRD data is stored in files on the disk
-	 *             <li><b>SAFE</b>: Default factory which creates backends based on the
-	 *             java.io.* package. RRD data is stored in files on the disk. This backend
-	 *             is "safe". Being safe means that RRD files can be safely shared between
-	 *             several JVM's.
-	 *             <li><b>NIO</b>: Factory which creates backends based on the
-	 *             java.nio.* package. RRD data is stored in files on the disk
-	 *             <li><b>MEMORY</b>: Factory which creates memory-oriented backends.
-	 *             RRD data is stored in memory, it gets lost as soon as JVM exits.
-	 *             </ul>
-	 * @return Backend factory for the given factory name
-	 * @throws RrdException Thrown if no factory with the given name
-	 *                      is available.
-	 */
-	public static synchronized RrdBackendFactory getFactory(final String name) throws RrdException {
-	    final RrdBackendFactory factory = factories.get(name);
-		if (factory != null) {
-			return factory;
-		}
-		throw new RrdException("No backend factory found with the name specified [" + name + "]");
-	}
-
-	/**
-	 * Registers new (custom) backend factory within the JRobin framework.
-	 *
-	 * @param factory Factory to be registered
-	 * @throws RrdException Thrown if the name of the specified factory is already
-	 *                      used.
-	 */
-	public static synchronized void registerFactory(final RrdBackendFactory factory) throws RrdException {
-	    final String name = factory.getFactoryName();
-		if (!factories.containsKey(name)) {
-			factories.put(name, factory);
-		}
-		else {
-			throw new RrdException("Backend factory of this name2 (" + name + ") already exists and cannot be registered");
-		}
-	}
-
-	/**
-	 * Registers new (custom) backend factory within the JRobin framework and sets this
-	 * factory as the default.
-	 *
-	 * @param factory Factory to be registered and set as default
-	 * @throws RrdException Thrown if the name of the specified factory is already
-	 *                      used.
-	 */
-	public static synchronized void registerAndSetAsDefaultFactory(final RrdBackendFactory factory) throws RrdException {
-		registerFactory(factory);
-		setDefaultFactory(factory.getFactoryName());
-	}
-
-	/**
-	 * Returns the defaul backend factory. This factory is used to construct
-	 * {@link RrdDb} objects if no factory is specified in the RrdDb constructor.
-	 *
-	 * @return Default backend factory.
-	 */
-	public static RrdBackendFactory getDefaultFactory() {
-		return defaultFactory;
-	}
-
-	/**
-	 * Replaces the default backend factory with a new one. This method must be called before
-	 * the first RRD gets created. <p>
-	 *
-	 * @param factoryName Name of the default factory. Out of the box, JRobin supports four
-	 *                    different RRD backends: "FILE" (java.io.* based), "SAFE" (java.io.* based - use this
-	 *                    backend if RRD files may be accessed from several JVMs at the same time),
-	 *                    "NIO" (java.nio.* based) and "MEMORY" (byte[] based).
-	 * @throws RrdException Thrown if invalid factory name is supplied or not called before
-	 *                      the first RRD is created.
-	 */
-	public static void setDefaultFactory(final String factoryName) throws RrdException {
-		// We will allow this only if no RRDs are created
-		if (!RrdBackend.isInstanceCreated()) {
-			defaultFactory = getFactory(factoryName);
-		}
-		else {
-			throw new RrdException("Could not change the default backend factory. This method must be called before the first RRD gets created");
-		}
-	}
-
-	/**
-	 * Whether or not the RRD backend has created an instance yet.
-	 *
-	 * @return True if the backend instance is created, false if not.
-	 */
-	public static boolean isInstanceCreated() {
-		return RrdBackend.isInstanceCreated();
-	}
-
-	/**
-	 * Creates RrdBackend object for the given storage path.
-	 *
-	 * @param path	 Storage path
-	 * @param readOnly True, if the storage should be accessed in read/only mode.
-	 *                 False otherwise.
-	 * @return Backend object which handles all I/O operations for the given storage path
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected abstract RrdBackend open(String path, boolean readOnly) throws IOException;
-
-	/**
-	 * Method to determine if a storage with the given path already exists.
-	 *
-	 * @param path Storage path
-	 * @return True, if such storage exists, false otherwise.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected abstract boolean exists(String path) throws IOException;
-
-	/**
-	 * Returns the name (primary ID) for the factory.
-	 *
-	 * @return Name of the factory.
-	 */
-	public abstract String getFactoryName();
-
-	public String toString() {
-	    return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[name=" + getFactoryName() + "]";
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdDb.java b/apps/jrobin/java/src/org/jrobin/core/RrdDb.java
deleted file mode 100644
index 64544dc77e5ce311ea9460582a1942191ac9b788..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdDb.java
+++ /dev/null
@@ -1,1166 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Date;
-
-/**
- * Main class used to create and manipulate round robin databases (RRDs). Use this class to perform
- * update and fetch operations on exisiting RRDs, to create new RRD from
- * the definition (object of class {@link org.jrobin.core.RrdDef RrdDef}) or
- * from XML file (dumped content of RRDTool's or JRobin's RRD file).
- * <p>
- * Each RRD is backed with some kind of storage. For example, RRDTool supports only one kind of
- * storage (disk file). On the contrary, JRobin gives you freedom to use other storage (backend) types
- * even to create your own backend types for some special purposes. JRobin by default stores
- * RRD data in files (as RRDTool), but you might choose to store RRD data in memory (this is
- * supported in JRobin), to use java.nio.* instead of java.io.* package for file manipulation
- * (also supported) or to store whole RRDs in the SQL database
- * (you'll have to extend some classes to do this).
- * <p>
- * Note that JRobin uses binary format different from RRDTool's format. You cannot
- * use this class to manipulate RRD files created with RRDTool. <b>However, if you perform
- * the same sequence of create, update and fetch operations, you will get exactly the same
- * results from JRobin and RRDTool.</b>
- * <p>
- * You will not be able to use JRobin API if you are not familiar with
- * basic RRDTool concepts. Good place to start is the
- * <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/tutorial/rrdtutorial.html">official RRD tutorial</a>
- * and relevant RRDTool man pages: <a href="../../../../man/rrdcreate.html" target="man">rrdcreate</a>,
- * <a href="../../../../man/rrdupdate.html" target="man">rrdupdate</a>,
- * <a href="../../../../man/rrdfetch.html" target="man">rrdfetch</a> and
- * <a href="../../../../man/rrdgraph.html" target="man">rrdgraph</a>.
- * For RRDTool's advanced graphing capabilities (RPN extensions), also supported in JRobin,
- * there is an excellent
- * <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/tutorial/cdeftutorial.html" target="man">CDEF tutorial</a>.
- * <p>
- *
- * @see RrdBackend
- * @see RrdBackendFactory
- */
-public class RrdDb implements RrdUpdater {
-	/**
-	 * prefix to identify external XML file source used in various RrdDb constructors
-	 */
-	public static final String PREFIX_XML = "xml:/";
-	/**
-	 * prefix to identify external RRDTool file source used in various RrdDb constructors
-	 */
-	public static final String PREFIX_RRDTool = "rrdtool:/";
-
-	// static final String RRDTOOL = "rrdtool";
-	static final int XML_INITIAL_BUFFER_CAPACITY = 100000; // bytes
-
-	private RrdBackend backend;
-	private RrdAllocator allocator = new RrdAllocator();
-
-	private Header header;
-	private Datasource[] datasources;
-	private Archive[] archives;
-
-	private boolean closed = false;
-
-	/**
-	 * Constructor used to create new RRD object from the definition. This RRD object will be backed
-	 * with a storage (backend) of the default type. Initially, storage type defaults to "NIO"
-	 * (RRD bytes will be put in a file on the disk). Default storage type can be changed with a static
-	 * {@link RrdBackendFactory#setDefaultFactory(String)} method call.
-	 * <p>
-	 * New RRD file structure is specified with an object of class
-	 * {@link org.jrobin.core.RrdDef <b>RrdDef</b>}. The underlying RRD storage is created as soon
-	 * as the constructor returns.
-	 * <p>
-	 * Typical scenario:
-	 * <p>
-	 * <pre>
-	 * // create new RRD definition
-	 * RrdDef def = new RrdDef("test.rrd", 300);
-	 * def.addDatasource("input", DsTypes.DT_COUNTER, 600, 0, Double.NaN);
-	 * def.addDatasource("output", DsTypes.DT_COUNTER, 600, 0, Double.NaN);
-	 * def.addArchive(ConsolFuns.CF_AVERAGE, 0.5, 1, 600);
-	 * def.addArchive(ConsolFuns.CF_AVERAGE, 0.5, 6, 700);
-	 * def.addArchive(ConsolFuns.CF_AVERAGE, 0.5, 24, 797);
-	 * def.addArchive(ConsolFuns.CF_AVERAGE, 0.5, 288, 775);
-	 * def.addArchive(ConsolFuns.CF_MAX, 0.5, 1, 600);
-	 * def.addArchive(ConsolFuns.CF_MAX, 0.5, 6, 700);
-	 * def.addArchive(ConsolFuns.CF_MAX, 0.5, 24, 797);
-	 * def.addArchive(ConsolFuns.CF_MAX, 0.5, 288, 775);
-	 *
-	 * // RRD definition is now completed, create the database!
-	 * RrdDb rrd = new RrdDb(def);
-	 * // new RRD file has been created on your disk
-	 * </pre>
-	 *
-	 * @param rrdDef Object describing the structure of the new RRD file.
-	 * @throws IOException  Thrown in case of I/O error.
-	 * @throws RrdException Thrown if invalid RrdDef object is supplied.
-	 */
-	public RrdDb(RrdDef rrdDef) throws RrdException, IOException {
-		this(rrdDef, RrdFileBackendFactory.getDefaultFactory());
-	}
-
-	/**
-	 * Constructor used to create new RRD object from the definition object but with a storage
-	 * (backend) different from default.
-	 * <p>
-	 * JRobin uses <i>factories</i> to create RRD backend objecs. There are three different
-	 * backend factories supplied with JRobin, and each factory has its unique name:
-	 * <p>
-	 * <ul>
-	 * <li><b>FILE</b>: backends created from this factory will store RRD data to files by using
-	 * java.io.* classes and methods
-	 * <li><b>NIO</b>: backends created from this factory will store RRD data to files by using
-	 * java.nio.* classes and methods
-	 * <li><b>MEMORY</b>: backends created from this factory will store RRD data in memory. This might
-	 * be useful in runtime environments which prohibit disk utilization, or for storing temporary,
-	 * non-critical data (it gets lost as soon as JVM exits).
-	 * </ul>
-	 * <p>
-	 * For example, to create RRD in memory, use the following code:
-	 * <pre>
-	 * RrdBackendFactory factory = RrdBackendFactory.getFactory("MEMORY");
-	 * RrdDb rrdDb = new RrdDb(rrdDef, factory);
-	 * rrdDb.close();
-	 * </pre>
-	 * <p>
-	 * New RRD file structure is specified with an object of class
-	 * {@link org.jrobin.core.RrdDef <b>RrdDef</b>}. The underlying RRD storage is created as soon
-	 * as the constructor returns.
-	 *
-	 * @param rrdDef  RRD definition object
-	 * @param factory The factory which will be used to create storage for this RRD
-	 * @throws RrdException Thrown if invalid factory or definition is supplied
-	 * @throws IOException  Thrown in case of I/O error
-	 * @see RrdBackendFactory
-	 */
-	public RrdDb(RrdDef rrdDef, RrdBackendFactory factory) throws RrdException, IOException {
-		rrdDef.validate();
-		String path = rrdDef.getPath();
-		backend = factory.open(path, false);
-		try {
-			backend.setLength(rrdDef.getEstimatedSize());
-			// create header
-			header = new Header(this, rrdDef);
-			// create datasources
-			DsDef[] dsDefs = rrdDef.getDsDefs();
-			datasources = new Datasource[dsDefs.length];
-			for (int i = 0; i < dsDefs.length; i++) {
-				datasources[i] = new Datasource(this, dsDefs[i]);
-			}
-			// create archives
-			ArcDef[] arcDefs = rrdDef.getArcDefs();
-			archives = new Archive[arcDefs.length];
-			for (int i = 0; i < arcDefs.length; i++) {
-				archives[i] = new Archive(this, arcDefs[i]);
-			}
-		}
-		catch (IOException e) {
-			backend.close();
-			throw e;
-		}
-	}
-
-	/**
-	 * Constructor used to open already existing RRD. This RRD object will be backed
-	 * with a storage (backend) of the default type (file on the disk). Constructor
-	 * obtains read or read/write access to this RRD.
-	 *
-	 * @param path	 Path to existing RRD.
-	 * @param readOnly Should be set to <code>false</code> if you want to update
-	 *                 the underlying RRD. If you want just to fetch data from the RRD file
-	 *                 (read-only access), specify <code>true</code>. If you try to update RRD file
-	 *                 open in read-only mode (<code>m_readOnly</code> set to <code>true</code>),
-	 *                 <code>IOException</code> will be thrown.
-	 * @throws IOException  Thrown in case of I/O error.
-	 * @throws RrdException Thrown in case of JRobin specific error.
-	 */
-	public RrdDb(String path, boolean readOnly) throws IOException, RrdException {
-		this(path, readOnly, RrdBackendFactory.getDefaultFactory());
-	}
-
-	/**
-	 * Constructor used to open already existing RRD backed
-	 * with a storage (backend) different from default. Constructor
-	 * obtains read or read/write access to this RRD.
-	 *
-	 * @param path	 Path to existing RRD.
-	 * @param readOnly Should be set to <code>false</code> if you want to update
-	 *                 the underlying RRD. If you want just to fetch data from the RRD file
-	 *                 (read-only access), specify <code>true</code>. If you try to update RRD file
-	 *                 open in read-only mode (<code>m_readOnly</code> set to <code>true</code>),
-	 *                 <code>IOException</code> will be thrown.
-	 * @param factory  Backend factory which will be used for this RRD.
-	 * @throws FileNotFoundException Thrown if the requested file does not exist.
-	 * @throws IOException		   Thrown in case of general I/O error (bad RRD file, for example).
-	 * @throws RrdException		  Thrown in case of JRobin specific error.
-	 * @see RrdBackendFactory
-	 */
-	public RrdDb(String path, boolean readOnly, RrdBackendFactory factory)
-			throws FileNotFoundException, IOException, RrdException {
-		// opens existing RRD file - throw exception if the file does not exist...
-		if (!factory.exists(path)) {
-			throw new FileNotFoundException("Could not open " + path + " [non existent]");
-		}
-		backend = factory.open(path, readOnly);
-		try {
-			// restore header
-			header = new Header(this, (RrdDef) null);
-			header.validateHeader();
-			// restore datasources
-			int dsCount = header.getDsCount();
-			datasources = new Datasource[dsCount];
-			for (int i = 0; i < dsCount; i++) {
-				datasources[i] = new Datasource(this, null);
-			}
-			// restore archives
-			int arcCount = header.getArcCount();
-			archives = new Archive[arcCount];
-			for (int i = 0; i < arcCount; i++) {
-				archives[i] = new Archive(this, null);
-			}
-		}
-		catch (RrdException e) {
-			backend.close();
-			throw e;
-		}
-		catch (IOException e) {
-			backend.close();
-			throw e;
-		}
-	}
-
-	/**
-	 * <p>Constructor used to open already existing RRD in R/W mode, with a default storage
-	 * (backend) type (file on the disk).
-	 *
-	 * @param path Path to existing RRD.
-	 * @throws IOException  Thrown in case of I/O error.
-	 * @throws RrdException Thrown in case of JRobin specific error.
-	 */
-	public RrdDb(String path) throws IOException, RrdException {
-		this(path, false);
-	}
-
-	/**
-	 * <p>Constructor used to open already existing RRD in R/W mode with a storage (backend) type
-	 * different from default.</p>
-	 *
-	 * @param path	Path to existing RRD.
-	 * @param factory Backend factory used to create this RRD.
-	 * @throws IOException  Thrown in case of I/O error.
-	 * @throws RrdException Thrown in case of JRobin specific error.
-	 * @see RrdBackendFactory
-	 */
-	public RrdDb(String path, RrdBackendFactory factory) throws IOException, RrdException {
-		this(path, false, factory);
-	}
-
-	/**
-	 * Constructor used to create RRD files from external file sources.
-	 * Supported external file sources are:
-	 * <p>
-	 * <ul>
-	 * <li>RRDTool/JRobin XML file dumps (i.e files created with <code>rrdtool dump</code> command).
-	 * <li>RRDTool binary files.
-	 * </ul>
-	 * <p>
-	 * Newly created RRD will be backed with a default storage (backend) type
-	 * (file on the disk).
-	 * <p>
-	 * JRobin and RRDTool use the same format for XML dump and this constructor should be used to
-	 * (re)create JRobin RRD files from XML dumps. First, dump the content of a RRDTool
-	 * RRD file (use command line):
-	 * <p>
-	 * <pre>
-	 * rrdtool dump original.rrd &gt; original.xml
-	 * </pre>
-	 * <p>
-	 * Than, use the file <code>original.xml</code> to create JRobin RRD file named
-	 * <code>copy.rrd</code>:
-	 * <p>
-	 * <pre>
-	 * RrdDb rrd = new RrdDb("copy.rrd", "original.xml");
-	 * </pre>
-	 * <p>
-	 * or:
-	 * <p>
-	 * <pre>
-	 * RrdDb rrd = new RrdDb("copy.rrd", "xml:/original.xml");
-	 * </pre>
-	 * <p>
-	 * See documentation for {@link #dumpXml(java.lang.String) dumpXml()} method
-	 * to see how to convert JRobin files to RRDTool's format.
-	 * <p>
-	 * To read RRDTool files directly, specify <code>rrdtool:/</code> prefix in the
-	 * <code>externalPath</code> argument. For example, to create JRobin compatible file named
-	 * <code>copy.rrd</code> from the file <code>original.rrd</code> created with RRDTool, use
-	 * the following code:
-	 * <p>
-	 * <pre>
-	 * RrdDb rrd = new RrdDb("copy.rrd", "rrdtool:/original.rrd");
-	 * </pre>
-	 * <p>
-	 * Note that the prefix <code>xml:/</code> or <code>rrdtool:/</code> is necessary to distinguish
-	 * between XML and RRDTool's binary sources. If no prefix is supplied, XML format is assumed.
-	 *
-	 * @param rrdPath	  Path to a RRD file which will be created
-	 * @param externalPath Path to an external file which should be imported, with an optional
-	 *                     <code>xml:/</code> or <code>rrdtool:/</code> prefix.
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public RrdDb(String rrdPath, String externalPath) throws IOException, RrdException, RrdException {
-		this(rrdPath, externalPath, RrdBackendFactory.getDefaultFactory());
-	}
-
-	/**
-	 * Constructor used to create RRD files from external file sources with a backend type
-	 * different from default. Supported external file sources are:
-	 * <p>
-	 * <ul>
-	 * <li>RRDTool/JRobin XML file dumps (i.e files created with <code>rrdtool dump</code> command).
-	 * <li>RRDTool binary files.
-	 * </ul>
-	 * <p>
-	 * JRobin and RRDTool use the same format for XML dump and this constructor should be used to
-	 * (re)create JRobin RRD files from XML dumps. First, dump the content of a RRDTool
-	 * RRD file (use command line):
-	 * <p>
-	 * <pre>
-	 * rrdtool dump original.rrd &gt; original.xml
-	 * </pre>
-	 * <p>
-	 * Than, use the file <code>original.xml</code> to create JRobin RRD file named
-	 * <code>copy.rrd</code>:
-	 * <p>
-	 * <pre>
-	 * RrdDb rrd = new RrdDb("copy.rrd", "original.xml");
-	 * </pre>
-	 * <p>
-	 * or:
-	 * <p>
-	 * <pre>
-	 * RrdDb rrd = new RrdDb("copy.rrd", "xml:/original.xml");
-	 * </pre>
-	 * <p>
-	 * See documentation for {@link #dumpXml(java.lang.String) dumpXml()} method
-	 * to see how to convert JRobin files to RRDTool's format.
-	 * <p>
-	 * To read RRDTool files directly, specify <code>rrdtool:/</code> prefix in the
-	 * <code>externalPath</code> argument. For example, to create JRobin compatible file named
-	 * <code>copy.rrd</code> from the file <code>original.rrd</code> created with RRDTool, use
-	 * the following code:
-	 * <p>
-	 * <pre>
-	 * RrdDb rrd = new RrdDb("copy.rrd", "rrdtool:/original.rrd");
-	 * </pre>
-	 * <p>
-	 * Note that the prefix <code>xml:/</code> or <code>rrdtool:/</code> is necessary to distinguish
-	 * between XML and RRDTool's binary sources. If no prefix is supplied, XML format is assumed.
-	 *
-	 * @param rrdPath	  Path to RRD which will be created
-	 * @param externalPath Path to an external file which should be imported, with an optional
-	 *                     <code>xml:/</code> or <code>rrdtool:/</code> prefix.
-	 * @param factory	  Backend factory which will be used to create storage (backend) for this RRD.
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 * @see RrdBackendFactory
-	 */
-	public RrdDb(String rrdPath, String externalPath, RrdBackendFactory factory)
-			throws IOException, RrdException {
-		DataImporter reader;
-		if (externalPath.startsWith(PREFIX_RRDTool)) {
-			String rrdToolPath = externalPath.substring(PREFIX_RRDTool.length());
-			reader = new RrdToolReader(rrdToolPath);
-		}
-		else if (externalPath.startsWith(PREFIX_XML)) {
-			externalPath = externalPath.substring(PREFIX_XML.length());
-			reader = new XmlReader(externalPath);
-		}
-		else {
-			reader = new XmlReader(externalPath);
-		}
-		backend = factory.open(rrdPath, false);
-		try {
-			backend.setLength(reader.getEstimatedSize());
-			// create header
-			header = new Header(this, reader);
-			// create datasources
-			datasources = new Datasource[reader.getDsCount()];
-			for (int i = 0; i < datasources.length; i++) {
-				datasources[i] = new Datasource(this, reader, i);
-			}
-			// create archives
-			archives = new Archive[reader.getArcCount()];
-			for (int i = 0; i < archives.length; i++) {
-				archives[i] = new Archive(this, reader, i);
-			}
-			reader.release();
-		}
-		catch (RrdException e) {
-			backend.close();
-			throw e;
-		}
-		catch (IOException e) {
-			backend.close();
-			throw e;
-		}
-	}
-
-	public RrdDb(final File file) throws IOException, RrdException {
-	    this(file.getPath());
-    }
-
-    public RrdDb(final File file, final boolean readOnly) throws IOException, RrdException {
-        this(file.getPath(), readOnly);
-    }
-
-    /**
-	 * Closes RRD. No further operations are allowed on this RrdDb object.
-	 *
-	 * @throws IOException Thrown in case of I/O related error.
-	 */
-	public synchronized void close() throws IOException {
-		if (!closed) {
-			closed = true;
-			backend.close();
-		}
-	}
-
-	/**
-	 * Returns true if the RRD is closed.
-	 *
-	 * @return true if closed, false otherwise
-	 */
-	public boolean isClosed() {
-		return closed;
-	}
-
-	/**
-	 * Returns RRD header.
-	 *
-	 * @return Header object
-	 */
-	public Header getHeader() {
-		return header;
-	}
-
-	/**
-	 * Returns Datasource object for the given datasource index.
-	 *
-	 * @param dsIndex Datasource index (zero based)
-	 * @return Datasource object
-	 */
-	public Datasource getDatasource(int dsIndex) {
-		return datasources[dsIndex];
-	}
-
-	/**
-	 * Returns Archive object for the given archive index.
-	 *
-	 * @param arcIndex Archive index (zero based)
-	 * @return Archive object
-	 */
-	public Archive getArchive(int arcIndex) {
-		return archives[arcIndex];
-	}
-
-	/**
-	 * Returns an array of datasource names defined in RRD.
-	 *
-	 * @return Array of datasource names.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public String[] getDsNames() throws IOException {
-	    int n = datasources.length;
-		String[] dsNames = new String[n];
-		for (int i = 0; i < n; i++) {
-			dsNames[i] = datasources[i].getDsName();
-		}
-		return dsNames;
-	}
-
-	/**
-	 * Creates new sample with the given timestamp and all datasource values set to
-	 * 'unknown'. Use returned <code>Sample</code> object to specify
-	 * datasource values for the given timestamp. See documentation for
-	 * {@link org.jrobin.core.Sample Sample} for an explanation how to do this.
-	 * <p>
-	 * Once populated with data source values, call Sample's
-	 * {@link org.jrobin.core.Sample#update() update()} method to actually
-	 * store sample in the RRD associated with it.
-	 *
-	 * @param time Sample timestamp rounded to the nearest second (without milliseconds).
-	 * @return Fresh sample with the given timestamp and all data source values set to 'unknown'.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public Sample createSample(long time) throws IOException {
-		return new Sample(this, time);
-	}
-
-	/**
-	 * Creates new sample with the current timestamp and all data source values set to
-	 * 'unknown'. Use returned <code>Sample</code> object to specify
-	 * datasource values for the current timestamp. See documentation for
-	 * {@link org.jrobin.core.Sample Sample} for an explanation how to do this.
-	 * <p>
-	 * Once populated with data source values, call Sample's
-	 * {@link org.jrobin.core.Sample#update() update()} method to actually
-	 * store sample in the RRD associated with it.
-	 *
-	 * @return Fresh sample with the current timestamp and all
-	 *         data source values set to 'unknown'.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public Sample createSample() throws IOException {
-		return createSample(Util.getTime());
-	}
-
-	/**
-	 * <p>Prepares fetch request to be executed on this RRD. Use returned
-	 * <code>FetchRequest</code> object and its {@link org.jrobin.core.FetchRequest#fetchData() fetchData()}
-	 * method to actually fetch data from the RRD file.</p>
-	 *
-	 * @param consolFun  Consolidation function to be used in fetch request. Allowed values are
-	 *                   "AVERAGE", "MIN", "MAX" and "LAST" (these constants are conveniently defined in the
-	 *                   {@link ConsolFuns} class).
-	 * @param fetchStart Starting timestamp for fetch request.
-	 * @param fetchEnd   Ending timestamp for fetch request.
-	 * @param resolution Fetch resolution (see RRDTool's
-	 *                   <a href="../../../../man/rrdfetch.html" target="man">rrdfetch man page</a> for an
-	 *                   explanation of this parameter.
-	 * @return Request object that should be used to actually fetch data from RRD.
-	 * @throws RrdException In case of JRobin related error (invalid consolidation function or
-	 *                      invalid time span).
-	 */
-	public FetchRequest createFetchRequest(String consolFun, long fetchStart, long fetchEnd,
-										   long resolution) throws RrdException {
-		return new FetchRequest(this, consolFun, fetchStart, fetchEnd, resolution);
-	}
-
-	/**
-	 * <p>Prepares fetch request to be executed on this RRD. Use returned
-	 * <code>FetchRequest</code> object and its {@link org.jrobin.core.FetchRequest#fetchData() fetchData()}
-	 * method to actually fetch data from this RRD. Data will be fetched with the smallest
-	 * possible resolution (see RRDTool's
-	 * <a href="../../../../man/rrdfetch.html" target="man">rrdfetch man page</a>
-	 * for the explanation of the resolution parameter).</p>
-	 *
-	 * @param consolFun  Consolidation function to be used in fetch request. Allowed values are
-	 *                   "AVERAGE", "MIN", "MAX" and "LAST" (these constants are conveniently defined in the
-	 *                   {@link ConsolFuns} class).
-	 * @param fetchStart Starting timestamp for fetch request.
-	 * @param fetchEnd   Ending timestamp for fetch request.
-	 * @return Request object that should be used to actually fetch data from RRD.
-	 * @throws RrdException In case of JRobin related error (invalid consolidation function or
-	 *                      invalid time span).
-	 */
-	public FetchRequest createFetchRequest(String consolFun, long fetchStart, long fetchEnd)
-			throws RrdException {
-		return createFetchRequest(consolFun, fetchStart, fetchEnd, 1);
-	}
-
-	synchronized void store(Sample sample) throws IOException, RrdException {
-		if (closed) {
-			throw new RrdException("RRD already closed, cannot store this  sample");
-		}
-		long newTime = sample.getTime();
-		long lastTime = header.getLastUpdateTime();
-		if (lastTime >= newTime) {
-			throw new RrdException("Bad sample timestamp " + newTime +
-					". Last update time was " + lastTime + ", at least one second step is required");
-		}
-		double[] newValues = sample.getValues();
-		for (int i = 0; i < datasources.length; i++) {
-			double newValue = newValues[i];
-			datasources[i].process(newTime, newValue);
-		}
-		header.setLastUpdateTime(newTime);
-	}
-
-	synchronized FetchData fetchData(FetchRequest request) throws IOException, RrdException {
-		if (closed) {
-			throw new RrdException("RRD already closed, cannot fetch data");
-		}
-		Archive archive = findMatchingArchive(request);
-		return archive.fetchData(request);
-	}
-
-	public Archive findMatchingArchive(FetchRequest request) throws RrdException, IOException {
-		String consolFun = request.getConsolFun();
-		long fetchStart = request.getFetchStart();
-		long fetchEnd = request.getFetchEnd();
-		long resolution = request.getResolution();
-		Archive bestFullMatch = null, bestPartialMatch = null;
-		long bestStepDiff = 0, bestMatch = 0;
-		for (Archive archive : archives) {
-			if (archive.getConsolFun().equals(consolFun)) {
-				long arcStep = archive.getArcStep();
-				long arcStart = archive.getStartTime() - arcStep;
-				long arcEnd = archive.getEndTime();
-				long fullMatch = fetchEnd - fetchStart;
-				if (arcEnd >= fetchEnd && arcStart <= fetchStart) {
-					long tmpStepDiff = Math.abs(archive.getArcStep() - resolution);
-
-					if (tmpStepDiff < bestStepDiff || bestFullMatch == null) {
-						bestStepDiff = tmpStepDiff;
-						bestFullMatch = archive;
-					}
-				}
-				else {
-					long tmpMatch = fullMatch;
-
-					if (arcStart > fetchStart) {
-						tmpMatch -= (arcStart - fetchStart);
-					}
-					if (arcEnd < fetchEnd) {
-						tmpMatch -= (fetchEnd - arcEnd);
-					}
-					if (bestPartialMatch == null || bestMatch < tmpMatch) {
-						bestPartialMatch = archive;
-						bestMatch = tmpMatch;
-					}
-				}
-			}
-		}
-		if (bestFullMatch != null) {
-			return bestFullMatch;
-		}
-		else if (bestPartialMatch != null) {
-			return bestPartialMatch;
-		}
-		else {
-			throw new RrdException("RRD file does not contain RRA:" + consolFun + " archive");
-		}
-	}
-
-	/**
-	 * Finds the archive that best matches to the start time (time period being start-time until now)
-	 * and requested resolution.
-	 *
-	 * @param consolFun  Consolidation function of the datasource.
-	 * @param startTime  Start time of the time period in seconds.
-	 * @param resolution Requested fetch resolution.
-	 * @return Reference to the best matching archive.
-	 * @throws IOException Thrown in case of I/O related error.
-	 */
-	public Archive findStartMatchArchive(String consolFun, long startTime, long resolution) throws IOException {
-		long arcStep, diff;
-		int fallBackIndex = 0;
-		int arcIndex = -1;
-		long minDiff = Long.MAX_VALUE;
-		long fallBackDiff = Long.MAX_VALUE;
-
-		for (int i = 0; i < archives.length; i++) {
-			if (archives[i].getConsolFun().equals(consolFun)) {
-				arcStep = archives[i].getArcStep();
-				diff = Math.abs(resolution - arcStep);
-
-				// Now compare start time, see if this archive encompasses the requested interval
-				if (startTime >= archives[i].getStartTime()) {
-					if (diff == 0)				// Best possible match either way
-					{
-						return archives[i];
-					}
-					else if (diff < minDiff) {
-						minDiff = diff;
-						arcIndex = i;
-					}
-				}
-				else if (diff < fallBackDiff) {
-					fallBackDiff = diff;
-					fallBackIndex = i;
-				}
-			}
-		}
-
-		return (arcIndex >= 0 ? archives[arcIndex] : archives[fallBackIndex]);
-	}
-
-	/**
-	 * <p>Returns string representing complete internal RRD state. The returned
-	 * string can be printed to <code>stdout</code> and/or used for debugging purposes.</p>
-	 *
-	 * @return String representing internal RRD state.
-	 * @throws IOException Thrown in case of I/O related error.
-	 */
-	public synchronized String dump() throws IOException {
-		StringBuffer buffer = new StringBuffer();
-		buffer.append(header.dump());
-		for (Datasource datasource : datasources) {
-			buffer.append(datasource.dump());
-		}
-		for (Archive archive : archives) {
-			buffer.append(archive.dump());
-		}
-		return buffer.toString();
-	}
-
-	void archive(Datasource datasource, double value, long numUpdates)
-			throws IOException, RrdException {
-		int dsIndex = getDsIndex(datasource.getDsName());
-		for (Archive archive : archives) {
-			archive.archive(dsIndex, value, numUpdates);
-		}
-	}
-
-	/**
-	 * <p>Returns internal index number for the given datasource name. This index is heavily
-	 * used by jrobin.graph package and has no value outside of it.</p>
-	 *
-	 * @param dsName Data source name.
-	 * @return Internal index of the given data source name in this RRD.
-	 * @throws RrdException Thrown in case of JRobin related error (invalid data source name,
-	 *                      for example)
-	 * @throws IOException  Thrown in case of I/O error.
-	 */
-	public int getDsIndex(String dsName) throws RrdException, IOException {
-		for (int i = 0; i < datasources.length; i++) {
-			if (datasources[i].getDsName().equals(dsName)) {
-				return i;
-			}
-		}
-		throw new RrdException("Unknown datasource name: " + dsName);
-	}
-
-	/**
-	 * Checks presence of a specific datasource.
-	 *
-	 * @param dsName Datasource name to check
-	 * @return <code>true</code> if datasource is present in this RRD, <code>false</code> otherwise
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public boolean containsDs(String dsName) throws IOException {
-		for (Datasource datasource : datasources) {
-			if (datasource.getDsName().equals(dsName)) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	Datasource[] getDatasources() {
-		return datasources;
-	}
-
-	Archive[] getArchives() {
-		return archives;
-	}
-
-	/**
-	 * Writes the RRD content to OutputStream using XML format. This format
-	 * is fully compatible with RRDTool's XML dump format and can be used for conversion
-	 * purposes or debugging.
-	 *
-	 * @param destination Output stream to receive XML data
-	 * @throws IOException Thrown in case of I/O related error
-	 */
-	public synchronized void dumpXml(OutputStream destination) throws IOException {
-		XmlWriter writer = new XmlWriter(destination);
-		writer.startTag("rrd");
-		// dump header
-		header.appendXml(writer);
-		// dump datasources
-		for (Datasource datasource : datasources) {
-			datasource.appendXml(writer);
-		}
-		// dump archives
-		for (Archive archive : archives) {
-			archive.appendXml(writer);
-		}
-		writer.closeTag();
-		writer.flush();
-	}
-
-	/**
-	 * This method is just an alias for {@link #dumpXml(OutputStream) dumpXml} method.
-	 *
-	 * @param destination Output stream to receive XML data
-	 * @throws IOException Thrown in case of I/O related error
-	 */
-	public synchronized void exportXml(OutputStream destination) throws IOException {
-		dumpXml(destination);
-	}
-
-	/**
-	 * Returns string representing internal RRD state in XML format. This format
-	 * is fully compatible with RRDTool's XML dump format and can be used for conversion
-	 * purposes or debugging.
-	 *
-	 * @return Internal RRD state in XML format.
-	 * @throws IOException  Thrown in case of I/O related error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public synchronized String getXml() throws IOException, RrdException {
-		ByteArrayOutputStream destination = new ByteArrayOutputStream(XML_INITIAL_BUFFER_CAPACITY);
-		dumpXml(destination);
-		return destination.toString();
-	}
-
-	/**
-	 * This method is just an alias for {@link #getXml() getXml} method.
-	 *
-	 * @return Internal RRD state in XML format.
-	 * @throws IOException  Thrown in case of I/O related error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public synchronized String exportXml() throws IOException, RrdException {
-		return getXml();
-	}
-
-	/**
-	 * Dumps internal RRD state to XML file.
-	 * Use this XML file to convert your JRobin RRD to RRDTool format.
-	 * <p>
-	 * Suppose that you have a JRobin RRD file <code>original.rrd</code> and you want
-	 * to convert it to RRDTool format. First, execute the following java code:
-	 * <p>
-	 * <code>RrdDb rrd = new RrdDb("original.rrd");
-	 * rrd.dumpXml("original.xml");</code>
-	 * <p>
-	 * Use <code>original.xml</code> file to create the corresponding RRDTool file
-	 * (from your command line):
-	 * <p>
-	 * <code>rrdtool restore copy.rrd original.xml</code>
-	 *
-	 * @param filename Path to XML file which will be created.
-	 * @throws IOException  Thrown in case of I/O related error.
-	 * @throws RrdException Thrown in case of JRobin related error.
-	 */
-	public synchronized void dumpXml(String filename) throws IOException, RrdException {
-		OutputStream outputStream = null;
-		try {
-			outputStream = new FileOutputStream(filename, false);
-			dumpXml(outputStream);
-		}
-		finally {
-			if (outputStream != null) {
-				outputStream.close();
-			}
-		}
-	}
-
-	/**
-	 * This method is just an alias for {@link #dumpXml(String) dumpXml(String)} method.
-	 *
-	 * @param filename Path to XML file which will be created.
-	 * @throws IOException  Thrown in case of I/O related error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public synchronized void exportXml(String filename) throws IOException, RrdException {
-		dumpXml(filename);
-	}
-
-	/**
-	 * Returns time of last update operation as timestamp (in seconds).
-	 *
-	 * @return Last update time (in seconds).
-	 * @throws IOException  Thrown in case of I/O related error
-	 */
-	public synchronized long getLastUpdateTime() throws IOException {
-		return header.getLastUpdateTime();
-	}
-
-	/**
-	 * Returns RRD definition object which can be used to create new RRD
-	 * with the same creation parameters but with no data in it.
-	 * <p>
-	 * Example:
-	 * <p>
-	 * <pre>
-	 * RrdDb rrd1 = new RrdDb("original.rrd");
-	 * RrdDef def = rrd1.getRrdDef();
-	 * // fix path
-	 * def.setPath("empty_copy.rrd");
-	 * // create new RRD file
-	 * RrdDb rrd2 = new RrdDb(def);
-	 * </pre>
-	 *
-	 * @return RRD definition.
-	 * @throws IOException  Thrown in case of I/O related error.
-	 * @throws RrdException Thrown in case of JRobin specific error.
-	 */
-	public synchronized RrdDef getRrdDef() throws RrdException, IOException {
-		// set header
-		long startTime = header.getLastUpdateTime();
-		long step = header.getStep();
-		String path = backend.getPath();
-		RrdDef rrdDef = new RrdDef(path, startTime, step);
-		// add datasources
-		for (Datasource datasource : datasources) {
-			DsDef dsDef = new DsDef(datasource.getDsName(),
-					datasource.getDsType(), datasource.getHeartbeat(),
-					datasource.getMinValue(), datasource.getMaxValue());
-			rrdDef.addDatasource(dsDef);
-		}
-		// add archives
-		for (Archive archive : archives) {
-			ArcDef arcDef = new ArcDef(archive.getConsolFun(),
-					archive.getXff(), archive.getSteps(), archive.getRows());
-			rrdDef.addArchive(arcDef);
-		}
-		return rrdDef;
-	}
-
-	protected void finalize() throws Throwable {
-		super.finalize();
-		close();
-	}
-
-	/**
-	 * Copies object's internal state to another RrdDb object.
-	 *
-	 * @param other New RrdDb object to copy state to
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if supplied argument is not a compatible RrdDb object
-	 */
-	public synchronized void copyStateTo(RrdUpdater other) throws IOException, RrdException {
-		if (!(other instanceof RrdDb)) {
-			throw new RrdException("Cannot copy RrdDb object to " + other.getClass().getName());
-		}
-		RrdDb otherRrd = (RrdDb) other;
-		header.copyStateTo(otherRrd.header);
-		for (int i = 0; i < datasources.length; i++) {
-			int j = Util.getMatchingDatasourceIndex(this, i, otherRrd);
-			if (j >= 0) {
-				datasources[i].copyStateTo(otherRrd.datasources[j]);
-			}
-		}
-		for (int i = 0; i < archives.length; i++) {
-			int j = Util.getMatchingArchiveIndex(this, i, otherRrd);
-			if (j >= 0) {
-				archives[i].copyStateTo(otherRrd.archives[j]);
-			}
-		}
-	}
-
-	/**
-	 * Returns Datasource object corresponding to the given datasource name.
-	 *
-	 * @param dsName Datasource name
-	 * @return Datasource object corresponding to the give datasource name or null
-	 *         if not found.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public Datasource getDatasource(String dsName) throws IOException {
-		try {
-			return getDatasource(getDsIndex(dsName));
-		}
-		catch (RrdException e) {
-			return null;
-		}
-	}
-
-	/**
-	 * Returns index of Archive object with the given consolidation function and the number
-	 * of steps. Exception is thrown if such archive could not be found.
-	 *
-	 * @param consolFun Consolidation function
-	 * @param steps	 Number of archive steps
-	 * @return Requested Archive object
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if no such archive could be found
-	 */
-	public int getArcIndex(String consolFun, int steps) throws RrdException, IOException {
-		for (int i = 0; i < archives.length; i++) {
-			if (archives[i].getConsolFun().equals(consolFun) &&
-					archives[i].getSteps() == steps) {
-				return i;
-			}
-		}
-		throw new RrdException("Could not find archive " + consolFun + "/" + steps);
-	}
-
-	/**
-	 * Returns Archive object with the given consolidation function and the number
-	 * of steps.
-	 *
-	 * @param consolFun Consolidation function
-	 * @param steps	 Number of archive steps
-	 * @return Requested Archive object or null if no such archive could be found
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public Archive getArchive(String consolFun, int steps) throws IOException {
-		try {
-			return getArchive(getArcIndex(consolFun, steps));
-		}
-		catch (RrdException e) {
-			return null;
-		}
-	}
-
-	/**
-	 * Returns canonical path to the underlying RRD file. Note that this method makes sense just for
-	 * ordinary RRD files created on the disk - an exception will be thrown for RRD objects created in
-	 * memory or with custom backends.
-	 *
-	 * @return Canonical path to RRD file;
-	 * @throws IOException Thrown in case of I/O error or if the underlying backend is
-	 *                     not derived from RrdFileBackend.
-	 */
-	public String getCanonicalPath() throws IOException {
-		if (backend instanceof RrdFileBackend) {
-			return ((RrdFileBackend) backend).getCanonicalPath();
-		}
-		else {
-			throw new IOException("The underlying backend has no canonical path");
-		}
-	}
-
-	/**
-	 * Returns path to this RRD.
-	 *
-	 * @return Path to this RRD.
-	 */
-	public String getPath() {
-		return backend.getPath();
-	}
-
-	/**
-	 * Returns backend object for this RRD which performs actual I/O operations.
-	 *
-	 * @return RRD backend for this RRD.
-	 */
-	public RrdBackend getRrdBackend() {
-		return backend;
-	}
-
-	/**
-	 * Required to implement RrdUpdater interface. You should never call this method directly.
-	 *
-	 * @return Allocator object
-	 */
-	public RrdAllocator getRrdAllocator() {
-		return allocator;
-	}
-
-	/**
-	 * Returns an array of bytes representing the whole RRD.
-	 *
-	 * @return All RRD bytes
-	 * @throws IOException Thrown in case of I/O related error.
-	 */
-	public synchronized byte[] getBytes() throws IOException {
-		return backend.readAll();
-	}
-
-	/**
-	 * Sets default backend factory to be used. This method is just an alias for
-	 * {@link RrdBackendFactory#setDefaultFactory(java.lang.String)}.<p>
-	 *
-	 * @param factoryName Name of the backend factory to be set as default.
-	 * @throws RrdException Thrown if invalid factory name is supplied, or not called
-	 *                      before the first backend object (before the first RrdDb object) is created.
-	 */
-	public static void setDefaultFactory(String factoryName) throws RrdException {
-		RrdBackendFactory.setDefaultFactory(factoryName);
-	}
-
-	/**
-	 * Returns an array of last datasource values. The first value in the array corresponds
-	 * to the first datasource defined in the RrdDb and so on.
-	 *
-	 * @return Array of last datasource values
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public synchronized double[] getLastDatasourceValues() throws IOException {
-		double[] values = new double[datasources.length];
-		for (int i = 0; i < values.length; i++) {
-			values[i] = datasources[i].getLastValue();
-		}
-		return values;
-	}
-
-	/**
-	 * Returns the last stored value for the given datasource.
-	 *
-	 * @param dsName Datasource name
-	 * @return Last stored value for the given datasource
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown if no datasource in this RrdDb matches the given datasource name
-	 */
-	public synchronized double getLastDatasourceValue(String dsName) throws IOException, RrdException {
-		int dsIndex = getDsIndex(dsName);
-		return datasources[dsIndex].getLastValue();
-	}
-
-	/**
-	 * Returns the number of datasources defined in the file
-	 *
-	 * @return The number of datasources defined in the file
-	 */
-	public int getDsCount() {
-		return datasources.length;
-	}
-
-	/**
-	 * Returns the number of RRA arcihves defined in the file
-	 *
-	 * @return The number of RRA arcihves defined in the file
-	 */
-	public int getArcCount() {
-		return archives.length;
-	}
-
-	/**
-	 * Returns the last time when some of the archives in this RRD was updated. This time is not the
-	 * same as the {@link #getLastUpdateTime()} since RRD file can be updated without updating any of
-	 * the archives.
-	 *
-	 * @return last time when some of the archives in this RRD was updated
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public long getLastArchiveUpdateTime() throws IOException {
-		long last = 0;
-		for (Archive archive : archives) {
-			last = Math.max(last, archive.getEndTime());
-		}
-		return last;
-	}
-
-	public synchronized String getInfo() throws IOException {
-		return header.getInfo();
-	}
-
-	public synchronized void setInfo(String info) throws IOException {
-		header.setInfo(info);
-	}
-
-	public static void main(String[] args) {
-		System.out.println("JRobin Java Library :: RRDTool choice for the Java world");
-		System.out.println("==================================================================");
-		System.out.println("JRobin base directory: " + Util.getJRobinHomeDirectory());
-		long time = Util.getTime();
-		System.out.println("Current timestamp: " + time + ": " + new Date(time * 1000L));
-		System.out.println("------------------------------------------------------------------");
-		System.out.println("For the latest information visit: http://www.jrobin.org");
-		System.out.println("(C) 2003-2005 Sasa Markovic. All rights reserved.");
-	}
-
-	public String toString() {
-	    return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[" + new File(getPath()).getName() + "]";
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdDbPool.java b/apps/jrobin/java/src/org/jrobin/core/RrdDbPool.java
deleted file mode 100644
index 3be890f3ea168476e0e7c487e5c129cad78494ec..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdDbPool.java
+++ /dev/null
@@ -1,932 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-import org.jrobin.core.RrdException;
-
-/**
- * This class should be used to synchronize access to RRD files
- * in a multithreaded environment. This class should be also used to prevent openning of
- * too many RRD files at the same time (thus avoiding operating system limits)
- */
-
-public class RrdDbPool {
-	/**
-	 * Initial capacity of the pool i.e. maximum number of simultaneously open RRD files. The pool will
-	 * never open too many RRD files at the same time.
-	 */
-	public static final int INITIAL_CAPACITY = 200;
-	private static RrdDbPool instance;
-
-	private int capacity = INITIAL_CAPACITY;
-	private HashMap<String, RrdEntry> rrdMap = new HashMap<String, RrdEntry>(INITIAL_CAPACITY);
-
-	/**
-	 * Creates a single instance of the class on the first call, or returns already existing one.
-	 *
-	 * @return Single instance of this class
-	 * @throws RrdException Thrown if the default RRD backend is not derived from the {@link RrdFileBackendFactory}
-	 */
-	public synchronized static RrdDbPool getInstance() throws RrdException {
-		if (instance == null) {
-			instance = new RrdDbPool();
-		}
-		return instance;
-	}
-
-	private RrdDbPool() throws RrdException {
-		RrdBackendFactory factory = RrdBackendFactory.getDefaultFactory();
-		if (!(factory instanceof RrdFileBackendFactory)) {
-			throw new RrdException("Cannot create instance of " + getClass().getName() + " with " +
-					"a default backend factory not derived from RrdFileBackendFactory");
-		}
-	}
-
-	/**
-	 * Requests a RrdDb reference for the given RRD file path.<p>
-	 * <ul>
-	 * <li>If the file is already open, previously returned RrdDb reference will be returned. Its usage count
-	 * will be incremented by one.
-	 * <li>If the file is not already open and the number of already open RRD files is less than
-	 * {@link #INITIAL_CAPACITY}, the file will be open and a new RrdDb reference will be returned.
-	 * If the file is not already open and the number of already open RRD files is equal to
-	 * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
-	 * </ul>
-	 *
-	 * @param path Path to existing RRD file
-	 * @return reference for the give RRD file
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException {
-		String canonicalPath = Util.getCanonicalPath(path);
-		while (!rrdMap.containsKey(canonicalPath) && rrdMap.size() >= capacity) {
-			try {
-				wait();
-			}
-			catch (InterruptedException e) {
-				throw new RrdException(e);
-			}
-		}
-		if (rrdMap.containsKey(canonicalPath)) {
-			// already open, just increase usage count
-			RrdEntry entry = rrdMap.get(canonicalPath);
-			entry.count++;
-			return entry.rrdDb;
-		}
-		else {
-			// not open, open it now and add to the map
-			RrdDb rrdDb = new RrdDb(canonicalPath);
-			rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
-			return rrdDb;
-		}
-	}
-
-	/**
-	 * Requests a RrdDb reference for the given RRD file definition object.<p>
-	 * <ul>
-	 * <li>If the file with the path specified in the RrdDef object is already open,
-	 * the method blocks until the file is closed.
-	 * <li>If the file is not already open and the number of already open RRD files is less than
-	 * {@link #INITIAL_CAPACITY}, a new RRD file will be created and a its RrdDb reference will be returned.
-	 * If the file is not already open and the number of already open RRD files is equal to
-	 * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
-	 * </ul>
-	 *
-	 * @param rrdDef Definition of the RRD file to be created
-	 * @return Reference to the newly created RRD file
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException {
-		String canonicalPath = Util.getCanonicalPath(rrdDef.getPath());
-		while (rrdMap.containsKey(canonicalPath) || rrdMap.size() >= capacity) {
-			try {
-				wait();
-			}
-			catch (InterruptedException e) {
-				throw new RrdException(e);
-			}
-		}
-		RrdDb rrdDb = new RrdDb(rrdDef);
-		rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
-		return rrdDb;
-	}
-
-	/**
-	 * Requests a RrdDb reference for the given path. The file will be created from
-	 * external data (from XML dump, RRD file or RRDTool's binary RRD file).<p>
-	 * <ul>
-	 * <li>If the file with the path specified is already open,
-	 * the method blocks until the file is closed.
-	 * <li>If the file is not already open and the number of already open RRD files is less than
-	 * {@link #INITIAL_CAPACITY}, a new RRD file will be created and a its RrdDb reference will be returned.
-	 * If the file is not already open and the number of already open RRD files is equal to
-	 * {@link #INITIAL_CAPACITY}, the method blocks until some RRD file is closed.
-	 * </ul>
-	 *
-	 * @param path	   Path to RRD file which should be created
-	 * @param sourcePath Path to external data which is to be converted to JRobin's native RRD file format
-	 * @return Reference to the newly created RRD file
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public synchronized RrdDb requestRrdDb(String path, String sourcePath)
-			throws IOException, RrdException,RrdException {
-		String canonicalPath = Util.getCanonicalPath(path);
-		while (rrdMap.containsKey(canonicalPath) || rrdMap.size() >= capacity) {
-			try {
-				wait();
-			}
-			catch (InterruptedException e) {
-				throw new RrdException(e);
-			}
-		}
-		RrdDb rrdDb = new RrdDb(canonicalPath, sourcePath);
-		rrdMap.put(canonicalPath, new RrdEntry(rrdDb));
-		return rrdDb;
-	}
-
-	/**
-	 * Releases RrdDb reference previously obtained from the pool. When a reference is released, its usage
-	 * count is decremented by one. If usage count drops to zero, the underlying RRD file will be closed.
-	 *
-	 * @param rrdDb RrdDb reference to be returned to the pool
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public synchronized void release(RrdDb rrdDb) throws IOException, RrdException {
-		// null pointer should not kill the thread, just ignore it
-		if (rrdDb == null) {
-			return;
-		}
-		String canonicalPath = Util.getCanonicalPath(rrdDb.getPath());
-		if (!rrdMap.containsKey(canonicalPath)) {
-			throw new RrdException("Could not release [" + canonicalPath + "], the file was never requested");
-		}
-		RrdEntry entry = rrdMap.get(canonicalPath);
-		if (--entry.count <= 0) {
-			// no longer used
-			rrdMap.remove(canonicalPath);
-			notifyAll();
-			entry.rrdDb.close();
-		}
-	}
-
-	/**
-	 * Returns the maximum number of simultaneously open RRD files.
-	 *
-	 * @return maximum number of simultaneously open RRD files
-	 */
-	public synchronized int getCapacity() {
-		return capacity;
-	}
-
-	/**
-	 * Sets the maximum number of simultaneously open RRD files.
-	 *
-	 * @param capacity Maximum number of simultaneously open RRD files.
-	 */
-	public synchronized void setCapacity(int capacity) {
-		this.capacity = capacity;
-	}
-
-	/**
-	 * Returns an array of open file names.
-	 *
-	 * @return Array with canonical paths to open RRD files held in the pool.
-	 */
-	public synchronized String[] getOpenFiles() {
-		return rrdMap.keySet().toArray(new String[0]);
-	}
-
-	/**
-	 * Returns the number of open RRD files.
-	 *
-	 * @return Number of currently open RRD files held in the pool.
-	 */
-	public synchronized int getOpenFileCount() {
-		return rrdMap.size();
-	}
-
-	private final static class RrdEntry {
-		RrdDb rrdDb;
-		int count;
-
-		RrdEntry(final RrdDb rrdDb) {
-			this.rrdDb = rrdDb;
-			this.count = 1;
-		}
-	}
-}
-
-// OLDER VERSION IS HERE
-
-///* ============================================================
-// * JRobin : Pure java implementation of RRDTool's functionality
-// * ============================================================
-// *
-// * Project Info:  http://www.jrobin.org
-// * Project Lead:  Sasa Markovic (saxon@jrobin.org);
-// *
-// * (C) Copyright 2003-2005, by Sasa Markovic.
-// *
-// * Developers:    Sasa Markovic (saxon@jrobin.org)
-// *
-// *
-// * This library is free software; you can redistribute it and/or modify it under the terms
-// * of the GNU Lesser General Public License as published by the Free Software Foundation;
-// * either version 2.1 of the License, or (at your option) any later version.
-// *
-// * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-// * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-// * See the GNU Lesser General Public License for more details.
-// *
-// * You should have received a copy of the GNU Lesser General Public License along with this
-// * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
-// * Boston, MA 02111-1307, USA.
-// */
-//package org.jrobin.core;
-//
-//import java.io.IOException;
-//import java.util.HashMap;
-//import java.util.Iterator;
-//import java.util.LinkedHashMap;
-//import java.util.Set;
-//
-///**
-// * Class to represent the pool of open RRD files.<p>
-// *
-// * To open already existing RRD file with JRobin, you have to create a
-// * {@link org.jrobin.core.RrdDb RrdDb} object by specifying RRD file path
-// * as constructor argument. This operation can be time consuming
-// * especially with large RRD files with many datasources and
-// * several long archives.<p>
-// *
-// * In a multithreaded environment you might probably need a reference to the
-// * same RRD file from two different threads (RRD file updates are performed in
-// * one thread but data fetching and graphing is performed in another one). To make
-// * the RrdDb construction process more efficient it might be convenient to open all
-// * RRD files in a centralized place. That's the purpose of RrdDbPool class.<p>
-// *
-// * How does it work? The typical usage scenario goes like this:<p>
-// *
-// * <pre>
-// * // obtain instance to RrdDbPool object
-// * RrdDbPool pool = RrdDbPool.getInstance();
-// *
-// * // request a reference to RrdDb object
-// * String path = "some_relative_or_absolute_path_to_any_RRD_file";
-// * RrdDb rrdDb = RrdDbPool.requestRrdDb(path);
-// *
-// * // reference obtained, do whatever you want with it...
-// * ...
-// * ...
-// *
-// * // once you don't need the reference, release it.
-// * // DO NOT CALL rrdDb.close() - files no longer in use are eventually closed by the pool
-// * pool.release(rrdDb);
-// * </pre>
-// *
-// * It's that simple. When the reference is requested for the first time, RrdDbPool will open the RRD file
-// * for you and make some internal note that the RRD file is used only once. When the reference
-// * to the same file (same RRD file path) is requested for the second time, the same RrdDb
-// * reference will be returned, and its usage count will be increased by one. When the
-// * reference is released its usage count will be decremented by one.<p>
-// *
-// * When the reference count drops to zero, RrdDbPool will not close the underlying
-// * RRD file immediatelly. Instead of it, it will be marked as 'eligible for closing'.
-// * If someone request the same RRD file again (before it gets closed), the same
-// * reference will be returned again.<p>
-// *
-// * RrdDbPool has a 'garbage collector' which runs in a separate, low-priority
-// * thread and gets activated only when the number of RRD files kept in the
-// * pool is too big (greater than number returned from {@link #getCapacity getCapacity()}).
-// * Only RRD files with a reference count equal to zero
-// * will be eligible for closing. Unreleased RrdDb references are never invalidated.
-// * RrdDbPool object keeps track of the time when each RRD file
-// * becomes eligible for closing so that the oldest RRD file gets closed first.<p>
-// *
-// * Initial RrdDbPool capacity is set to {@link #INITIAL_CAPACITY}. Use {@link #setCapacity(int)}
-// * method to change it at any time.<p>
-// *
-// * <b>WARNING:</b>Never use close() method on the reference returned from the pool.
-// * When the reference is no longer needed, return it to the pool with the
-// * {@link #release(RrdDb) release()} method.<p>
-// *
-// * However, you are not forced to use RrdDbPool methods to obtain RrdDb references
-// * to RRD files, 'ordinary' RrdDb constructors are still available. But RrdDbPool class
-// * offers serious performance improvement especially in complex applications with many
-// * threads and many simultaneously open RRD files.<p>
-// *
-// * The pool is thread-safe. Not that the {@link RrdDb} objects returned from the pool are
-// * also thread-safe<p>
-// *
-// * You should know that each operating system has its own internal limit on the number
-// * of simultaneously open files. The capacity of your RrdDbPool should be
-// * reasonably smaller than the limit imposed by your operating system.<p>
-// *
-// * <b>WARNING:</b> The pool cannot be used to manipulate RrdDb objects
-// * with {@link RrdBackend backends} different from default.<p>
-// */
-//public class RrdDbPool implements Runnable {
-//	static final String GC_THREAD_NAME = "RrdDbPool GC thread";
-//	static final String CLOSING_THREAD_NAME = "RrdDbPool closing thread";
-//	private static final boolean DEBUG = false;
-//
-//	// singleton pattern
-//	private static RrdDbPool ourInstance;
-//	private boolean closingOnExit = true;
-//
-//	private Thread shutdownHook = new Thread(CLOSING_THREAD_NAME) {
-//		public void run() {
-//			try {
-//				close();
-//			}
-//			catch (IOException e) {
-//				e.printStackTrace();
-//			}
-//		}
-//	};
-//
-//	/**
-//	 * Constant to represent the maximum number of internally open RRD files
-//	 * which still does not force garbage collector (the process which closes RRD files) to run.
-//	 */
-//	public static final int INITIAL_CAPACITY = 500;
-//	private int capacity = INITIAL_CAPACITY, maxUsedCapacity;
-//	private boolean active = true;
-//
-//	/**
-//	 * Constant to represent the internal behaviour of the pool.
-//	 * Defaults to <code>true</code> but can be changed at runtime. See
-//	 * {@link #setLimitedCapacity(boolean)} for more information.
-//	 */
-//	public static final boolean LIMITED_CAPACITY = false;
-//	private boolean limitedCapacity = LIMITED_CAPACITY;
-//
-//	/**
-//	 * Constant to represent the priority of the background thread which closes excessive RRD files
-//	 * which are no longer in use.
-//	 */
-//	public static final int GC_THREAD_PRIORITY = /** Thread.NORM_PRIORITY - */ 1;
-//
-//	private HashMap<String, RrdEntry> rrdMap = new HashMap<String, RrdEntry>(INITIAL_CAPACITY);
-//	private LinkedHashMap<String, RrdEntry> rrdIdleMap = new LinkedHashMap<String, RrdEntry>(INITIAL_CAPACITY);
-//	private RrdBackendFactory factory;
-//	private int poolHitsCount = 0, poolRequestsCount = 0;
-//
-//	/**
-//	 * Returns an instance to RrdDbPool object. Only one such object may exist in each JVM.
-//	 *
-//	 * @return Instance to RrdDbPool object.
-//	 */
-//	public synchronized static RrdDbPool getInstance() {
-//		if (ourInstance == null) {
-//			ourInstance = new RrdDbPool();
-//			ourInstance.startGarbageCollector();
-//		}
-//		return ourInstance;
-//	}
-//
-//	private RrdDbPool() {
-//		setClosingOnExit(closingOnExit);
-//	}
-//
-//	/**
-//	 * Checks the exiting behaviour of RrdDbPool.
-//	 * @return <code>True</code>, if all RRD files are to be closed
-//	 * when application invokes <code>System.exit()</code>.
-//	 * <code>False</code> otherwise. The default behaviour is <code>true</code>
-//	 * (all RRD files will be closed on exit).
-//	 */
-//	public synchronized boolean isClosingOnExit() {
-//		return closingOnExit;
-//	}
-//
-//	/**
-//	 * Sets the exiting behaviour of RrdDbPool.
-//	 * @param closingOnExit <code>True</code>, if all RRD files are to be closed
-//	 * when application invokes <code>System.exit()</code>.
-//	 * <code>False</code> otherwise. The default behaviour is <code>true</code>
-//	 * (all RRD files will be closed on exit).
-//	 */
-//	public synchronized void setClosingOnExit(boolean closingOnExit) {
-//		Runtime runtime = Runtime.getRuntime();
-//		runtime.removeShutdownHook(shutdownHook);
-//		if(closingOnExit) {
-//			runtime.addShutdownHook(shutdownHook);
-//		}
-//		this.closingOnExit = closingOnExit;
-//	}
-//
-//	private void startGarbageCollector() {
-//		Thread gcThread = new Thread(this, GC_THREAD_NAME);
-//		gcThread.setPriority(GC_THREAD_PRIORITY);
-//		gcThread.setDaemon(true);
-//		gcThread.start();
-//	}
-//
-//	/**
-//	 * Returns a reference to an existing RRD file with the specified path.
-//	 * If the file is already open in the pool, existing reference to it will be returned.
-//	 * Otherwise, the file is open and a newly created reference to it is returned.
-//	 *
-//	 * @param path Relative or absolute path to a RRD file.
-//	 * @return Reference to a RrdDb object (RRD file).
-//	 * @throws IOException  Thrown in case of I/O error.
-//	 * @throws RrdException Thrown in case of JRobin specific error.
-//	 */
-//	public synchronized RrdDb requestRrdDb(String path) throws IOException, RrdException {
-//		proveActive();
-//		poolRequestsCount++;
-//		String canonicalPath = getCanonicalPath(path);
-//		for(;;) {
-//			RrdEntry rrdEntry = rrdMap.get(canonicalPath);
-//			if (rrdEntry != null) {
-//				// already open, use it!
-//				reportUsage(canonicalPath, rrdEntry);
-//				poolHitsCount++;
-////				debug("CACHED: " + rrdEntry.dump());
-//				return rrdEntry.getRrdDb();
-//			}
-//			else if(!limitedCapacity || rrdMap.size() < capacity) {
-//				// not found, open it
-//				RrdDb rrdDb = createRrdDb(path, null);
-//				rrdEntry = new RrdEntry(rrdDb);
-//				addRrdEntry(canonicalPath, rrdEntry);
-////				debug("ADDED: " + rrdEntry.dump());
-//				return rrdDb;
-//			}
-//			else {
-//				// we have to wait
-//				try {
-//					wait();
-//				}
-//				catch (InterruptedException e) {
-//					throw new RrdException("Request for file '" + path + "' was interrupted");
-//				}
-//			}
-//		}
-//	}
-//
-//	/**
-//	 * Returns a reference to a new RRD file. The new file will have the specified
-//	 * relative or absolute path, and its contents will be provided from the specified
-//	 * XML file (RRDTool comaptible).
-//	 *
-//	 * @param path    Relative or absolute path to a new RRD file.
-//	 * @param xmlPath Relative or absolute path to an existing XML dump file (RRDTool comaptible)
-//	 * @return Reference to a RrdDb object (RRD file).
-//	 * @throws IOException  Thrown in case of I/O error.
-//	 * @throws RrdException Thrown in case of JRobin specific error.
-//	 */
-//	public synchronized RrdDb requestRrdDb(String path, String xmlPath)
-//			throws IOException, RrdException {
-//		return requestNewRrdDb(path, xmlPath);
-//	}
-//
-//	/**
-//	 * Returns a reference to a new RRD file. The new file will be created based on the
-//	 * definition contained in a RrdDef object.
-//	 *
-//	 * @param rrdDef RRD definition object
-//	 * @return Reference to a RrdDb object (RRD file).
-//	 * @throws IOException  Thrown in case of I/O error.
-//	 * @throws RrdException Thrown in case of JRobin specific error.
-//	 */
-//	public synchronized RrdDb requestRrdDb(RrdDef rrdDef) throws IOException, RrdException {
-//		return requestNewRrdDb(rrdDef.getPath(), rrdDef);
-//	}
-//
-//	private RrdDb requestNewRrdDb(String path, Object creationDef) throws IOException, RrdException {
-//		proveActive();
-//		poolRequestsCount++;
-//		String canonicalPath = getCanonicalPath(path);
-//		for(;;) {
-//			RrdEntry rrdEntry = rrdMap.get(canonicalPath);
-//			if(rrdEntry != null) {
-//				// already open
-//				removeIfIdle(canonicalPath, rrdEntry);
-//			}
-//			else if(!limitedCapacity || rrdMap.size() < capacity) {
-//				RrdDb rrdDb = createRrdDb(path, creationDef);
-//				RrdEntry newRrdEntry = new RrdEntry(rrdDb);
-//				addRrdEntry(canonicalPath, newRrdEntry);
-////				debug("ADDED: " + newRrdEntry.dump());
-//				return rrdDb;
-//			}
-//			else {
-//				 // we have to wait
-//				try {
-//					wait();
-//				}
-//				catch (InterruptedException e) {
-//					throw new RrdException("Request for file '" + path + "' was interrupted");
-//				}
-//			}
-//		}
-//	}
-//
-//	private RrdDb createRrdDb(String path, Object creationDef) throws RrdException, IOException {
-//		if(creationDef == null) {
-//			// existing RRD
-//			return new RrdDb(path, getFactory());
-//		}
-//		else if(creationDef instanceof String) {
-//			// XML input
-//			return new RrdDb(path, (String) creationDef, getFactory());
-//		}
-//		else if(creationDef instanceof RrdDef) {
-//			// RrdDef
-//			return new RrdDb((RrdDef) creationDef, getFactory());
-//		}
-//		else {
-//			throw new RrdException("Unexpected input object type: " +
-//				creationDef.getClass().getName());
-//		}
-//	}
-//
-//	private void reportUsage(String canonicalPath, RrdEntry rrdEntry) {
-//		if (rrdEntry.reportUsage() == 1) {
-//			// must not be garbage collected
-//			rrdIdleMap.remove(canonicalPath);
-//		}
-//	}
-//
-//	private void reportRelease(String canonicalPath, RrdEntry rrdEntry) {
-//		if (rrdEntry.reportRelease() == 0) {
-//			// ready to be garbage collected
-//			rrdIdleMap.put(canonicalPath, rrdEntry);
-//		}
-//	}
-//
-//	private void addRrdEntry(String canonicalPath, RrdEntry newRrdEntry) {
-//		rrdMap.put(canonicalPath, newRrdEntry);
-//		maxUsedCapacity = Math.max(rrdMap.size(), maxUsedCapacity);
-//		// notify waiting threads
-//		notifyAll();
-//	}
-//
-//	private void removeIfIdle(String canonicalPath, RrdEntry rrdEntry)
-//			throws RrdException, IOException {
-//		// already open, check if active (not released)
-//		if (rrdEntry.isInUse()) {
-//			// not released, not allowed here
-//			throw new RrdException("Cannot create new RrdDb file: " +
-//					"File '" + canonicalPath + "' already in use");
-//		} else {
-//			// open but released... safe to close it
-////			debug("WILL BE RECREATED: " + rrdEntry.dump());
-//			removeRrdEntry(canonicalPath, rrdEntry);
-//		}
-//	}
-//
-//	private void removeRrdEntry(String canonicalPath, RrdEntry rrdEntry) throws IOException {
-//		rrdEntry.closeRrdDb();
-//		rrdMap.remove(canonicalPath);
-//		rrdIdleMap.remove(canonicalPath);
-////		debug("REMOVED: " + rrdEntry.dump());
-//	}
-//
-//	/**
-//	 * Method used to report that the reference to a RRD file is no longer needed. File that
-//	 * is no longer needed (all references to it are released) is marked 'eligible for
-//	 * closing'. It will be eventually closed by the pool when the number of open RRD files
-//	 * becomes too big. Most recently released files will be closed last.
-//	 *
-//	 * @param rrdDb Reference to RRD file that is no longer needed.
-//	 * @throws IOException  Thrown in case of I/O error.
-//	 * @throws RrdException Thrown in case of JRobin specific error.
-//	 */
-//	public synchronized void release(RrdDb rrdDb) throws IOException, RrdException {
-//		proveActive();
-//		if (rrdDb == null) {
-//			// we don't want NullPointerException
-//			return;
-//		}
-//		if (rrdDb.isClosed()) {
-//			throw new RrdException("File " + rrdDb.getPath() + " already closed");
-//		}
-//		String canonicalPath = getCanonicalPath(rrdDb.getPath());
-//		if (rrdMap.containsKey(canonicalPath)) {
-//			RrdEntry rrdEntry = rrdMap.get(canonicalPath);
-//			reportRelease(canonicalPath, rrdEntry);
-////			debug("RELEASED: " + rrdEntry.dump());
-//		} else {
-//			throw new RrdException("RRD file " + rrdDb.getPath() + " not in the pool");
-//		}
-//		// notify waiting threads
-//		notifyAll();
-//	}
-//
-//	/**
-//	 * This method runs garbage collector in a separate thread. If the number of
-//	 * open RRD files kept in the pool is too big (greater than number
-//	 * returned from {@link #getCapacity getCapacity()}), garbage collector will try
-//	 * to close and remove RRD files with a reference count equal to zero.
-//	 * Never call this method directly.
-//	 */
-//	public void run() {
-////		debug("GC: started");
-//		while (active) {
-//			synchronized (this) {
-//				if (rrdMap.size() >= capacity && rrdIdleMap.size() > 0) {
-//					try {
-//						String canonicalPath = rrdIdleMap.keySet().iterator().next();
-//						RrdEntry rrdEntry = rrdIdleMap.get(canonicalPath);
-////						debug("GC: closing " + rrdEntry.dump());
-//						removeRrdEntry(canonicalPath, rrdEntry);
-//					} catch (IOException e) {
-//						e.printStackTrace();
-//					}
-//					notifyAll();
-//				}
-//				else {
-//					try {
-////						debug("GC: waiting");
-//						wait();
-////						debug("GC: running");
-//					} catch (InterruptedException e) {
-//						e.printStackTrace();
-//					}
-//				}
-//			}
-//		}
-//	}
-//
-//	protected void finalize() throws IOException {
-//		close();
-//	}
-//
-//	/**
-//	 * Clears the internal state of the pool entirely. All open RRD files are closed.
-//	 *
-//	 * @throws IOException Thrown in case of I/O related error.
-//	 */
-//	public synchronized void reset() throws IOException {
-//		Iterator<RrdEntry> it = rrdMap.values().iterator();
-//		while (it.hasNext()) {
-//			RrdEntry rrdEntry = it.next();
-//			rrdEntry.closeRrdDb();
-//		}
-//		rrdMap.clear();
-//		rrdIdleMap.clear();
-////		debug("Pool cleared");
-//	}
-//
-//	/**
-//	 * Closes the pool and all RRD files currently held in the pool.
-//	 * No further operations on the pool are allowed.
-//	 * @throws IOException Thrown in case of I/O error.
-//	 */
-//	public synchronized void close() throws IOException {
-//		if(active) {
-//			active = false;
-//			reset();
-////			debug("The pool is closed.");
-//		}
-//		else {
-////			debug("The pool is already closed!");
-//		}
-//	}
-//
-//	private static String getCanonicalPath(String path) throws IOException {
-//		return Util.getCanonicalPath(path);
-//	}
-//
-//	static void debug(String msg) {
-//		if (DEBUG) {
-//			System.out.println("POOL: " + msg);
-//		}
-//	}
-//
-//	/**
-//	 * Returns the internal state of the pool. Useful for debugging purposes.
-//	 *
-//	 * @param dumpFiles <code>true</code>, if dumped information should contain paths to open files
-//	 * currently held in the pool, <code>false</code> otherwise
-//	 * @return Internal pool state (with an optional list of open RRD files and
-//	 * the current number of usages for each one).
-//	 * @throws IOException Thrown in case of I/O error.
-//	 */
-//	public synchronized String dump(boolean dumpFiles) throws IOException {
-//		StringBuffer buff = new StringBuffer();
-//		buff.append("==== POOL DUMP ===========================\n");
-//		buff.append("open=" + rrdMap.size() + ", idle=" + rrdIdleMap.size() + "\n");
-//		buff.append("capacity=" + capacity + ", " + "maxUsedCapacity=" + maxUsedCapacity + "\n");
-//		buff.append("hits=" + poolHitsCount + ", " + "requests=" + poolRequestsCount + "\n");
-//		buff.append("efficiency=" + getPoolEfficiency() + "\n");
-//		if(dumpFiles) {
-//			buff.append("---- CACHED FILES ------------------------\n");
-//			Iterator<RrdEntry> it = rrdMap.values().iterator();
-//			while (it.hasNext()) {
-//				RrdEntry rrdEntry = it.next();
-//				buff.append(rrdEntry.dump() + "\n");
-//			}
-//		}
-//		return buff.toString();
-//	}
-//
-//	/**
-//	 * Returns the complete internal state of the pool. Useful for debugging purposes.
-//	 *
-//	 * @return Internal pool state (with a list of open RRD files and the current number of
-//	 * usages for each one).
-//	 * @throws IOException Thrown in case of I/O error.
-//	 */
-//	public synchronized String dump() throws IOException {
-//		return dump(true);
-//	}
-//
-//	/**
-//	 * Returns paths to all open files currently held in the pool.
-//	 * @return An array containing open file paths.
-//	 */
-//	public synchronized String[] getCachedFilePaths() {
-//		Set<String> keySet = rrdMap.keySet();
-//		int n = keySet.size(), i = 0;
-//		String[] files = new String[n];
-//		Iterator<String> it = keySet.iterator();
-//		while(it.hasNext()) {
-//			files[i++] = it.next();
-//		}
-//		return files;
-//	}
-//
-//	/**
-//	 * Returns maximum number of internally open RRD files
-//	 * which still does not force garbage collector to run.
-//	 *
-//	 * @return Desired nuber of open files held in the pool.
-//	 */
-//	public synchronized int getCapacity() {
-//		return capacity;
-//	}
-//
-//	/**
-//	 * Sets maximum number of internally open RRD files
-//	 * which still does not force garbage collector to run.
-//	 *
-//	 * @param capacity Desired number of open files to hold in the pool
-//	 */
-//	public synchronized void setCapacity(int capacity) {
-//		this.capacity = capacity;
-////		debug("Capacity set to: " + capacity);
-//	}
-//
-//	private RrdBackendFactory getFactory() throws RrdException {
-//		if (factory == null) {
-//			factory = RrdBackendFactory.getDefaultFactory();
-//			if (!(factory instanceof RrdFileBackendFactory)) {
-//				factory = null;
-//				throw new RrdException(
-//					"RrdDbPool cannot work with factories not derived from RrdFileBackendFactory");
-//			}
-//		}
-//		return factory;
-//	}
-//
-//	private class RrdEntry {
-//		private RrdDb rrdDb;
-//		private int usageCount = 1;
-//
-//		public RrdEntry(RrdDb rrdDb) {
-//			this.rrdDb = rrdDb;
-//		}
-//
-//		RrdDb getRrdDb() {
-//			return rrdDb;
-//		}
-//
-//		int reportUsage() {
-//			assert usageCount >= 0: "Unexpected reportUsage count: " + usageCount;
-//			return ++usageCount;
-//		}
-//
-//		int reportRelease() {
-//			assert usageCount > 0: "Unexpected reportRelease count: " + usageCount;
-//			return --usageCount;
-//		}
-//
-//		boolean isInUse() {
-//			return usageCount > 0;
-//		}
-//
-//		void closeRrdDb() throws IOException {
-//			rrdDb.close();
-//		}
-//
-//		String dump() throws IOException {
-//			String canonicalPath = getCanonicalPath(rrdDb.getPath());
-//			return canonicalPath + " [" + usageCount + "]";
-//		}
-//	}
-//
-//	/**
-//	 * Calculates pool's efficency ratio. The ratio is obtained by dividing the number of
-//	 * RrdDb requests served from the internal pool of open RRD files
-//	 * with the number of total RrdDb requests.
-//	 *
-//	 * @return Pool's efficiency ratio as a double between 1 (best) and 0 (worst).
-//	 * If no RrdDb reference was ever requested, 1 would be returned.
-//	 */
-//	public synchronized double getPoolEfficiency() {
-//		if (poolRequestsCount == 0) {
-//			return 1.0;
-//		}
-//		double ratio = (double) poolHitsCount / (double) poolRequestsCount;
-//		// round to 3 decimal digits
-//		return Math.round(ratio * 1000.0) / 1000.0;
-//	}
-//
-//	/**
-//	 * Returns the number of RRD requests served from the internal pool of open RRD files
-//	 *
-//	 * @return The number of pool "hits".
-//	 */
-//	public synchronized int getPoolHitsCount() {
-//		return poolHitsCount;
-//	}
-//
-//	/**
-//	 * Returns the total number of RRD requests successfully served by this pool.
-//	 *
-//	 * @return Total number of RRD requests
-//	 */
-//	public synchronized int getPoolRequestsCount() {
-//		return poolRequestsCount;
-//	}
-//
-//	/**
-//	 * Returns the maximum number of open RRD files over the lifetime
-//	 * of the pool.
-//	 * @return maximum number of open RRD files.
-//	 */
-//	public synchronized int getMaxUsedCapacity() {
-//		return maxUsedCapacity;
-//	}
-//
-//	/**
-//	 * Checks the internal behaviour of the pool. See {@link #setLimitedCapacity(boolean)} for
-//	 * more information.
-//	 *
-//	 * @return <code>true</code> if the pool is 'flexible' (by not imposing the strict
-//	 * limit on the number of simultaneously open files), <code>false</code> otherwise.
-//	 */
-//	public synchronized boolean isLimitedCapacity() {
-//		return limitedCapacity;
-//	}
-//
-//	/**
-//	 * Sets the behaviour of the pool. If <code>true</code> is passed as argument, the pool will never
-//	 * open more than {@link #getCapacity()} files at any time. If set to <code>false</code>,
-//	 * the pool might keep more open files, but only for a short period of time. This method might be
-//	 * useful if you want avoid OS limits when it comes to the number of simultaneously open files.<p>
-//	 *
-//	 * By default, the pool behaviour is 'flexible' (<code>limitedCapacity</code> property defaults
-//	 * to false<p>
-//	 *
-//	 * @param limitedCapacity <code>true</code> if the pool should be 'flexible' (not imposing the strict
-//	 * limit on the number of simultaneously open files), <code>false</code> otherwise.
-//	 */
-//	public synchronized void setLimitedCapacity(boolean limitedCapacity) {
-//		this.limitedCapacity = limitedCapacity;
-//	}
-//
-//	private void proveActive() throws IOException {
-//		if(!active) {
-//			throw new IOException("RrdDbPool is already closed");
-//		}
-//	}
-//
-//	/**
-//	 * Checks if the pool is active. You can request RrdDb references only from the active pool. The
-//	 * pool is deactived when the {@link #close()} method is called.
-//	 * @return <code>true</code> if active, <code>false</code> otherwise.
-//	 */
-//	public synchronized boolean isActive() {
-//		return active;
-//	}
-//}
-//
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdDef.java b/apps/jrobin/java/src/org/jrobin/core/RrdDef.java
deleted file mode 100644
index e5369761d87f3b229f4c13ccb5078fb703d92c34..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdDef.java
+++ /dev/null
@@ -1,694 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.*;
-
-/**
- * Class to represent definition of new Round Robin Database (RRD).
- * Object of this class is used to create
- * new RRD from scratch - pass its reference as a <code>RrdDb</code> constructor
- * argument (see documentation for {@link RrdDb RrdDb} class). <code>RrdDef</code>
- * object <b>does not</b> actually create new RRD. It just holds all necessary
- * information which will be used during the actual creation process.
- * <p>
- * RRD definition (RrdDef object) consists of the following elements:
- * <p>
- * <ul>
- * <li> path to RRD that will be created
- * <li> starting timestamp
- * <li> step
- * <li> one or more datasource definitions
- * <li> one or more archive definitions
- * </ul>
- * RrdDef provides API to set all these elements. For the complete explanation of all
- * RRD definition parameters, see RRDTool's
- * <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
- * <p>
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class RrdDef {
-	/**
-	 * default RRD step to be used if not specified in constructor (300 seconds)
-	 */
-	public static final long DEFAULT_STEP = 300L;
-	/**
-	 * if not specified in constructor, starting timestamp will be set to the
-	 * current timestamp plus DEFAULT_INITIAL_SHIFT seconds (-10)
-	 */
-	public static final long DEFAULT_INITIAL_SHIFT = -10L;
-
-	private String path;
-	private long startTime = Util.getTime() + DEFAULT_INITIAL_SHIFT;
-	private long step = DEFAULT_STEP;
-	private ArrayList<DsDef> dsDefs = new ArrayList<DsDef>();
-	private ArrayList<ArcDef> arcDefs = new ArrayList<ArcDef>();
-
-	/**
-	 * <p>Creates new RRD definition object with the given path.
-	 * When this object is passed to
-	 * <code>RrdDb</code> constructor, new RRD will be created using the
-	 * specified path. </p>
-	 *
-	 * @param path Path to new RRD.
-	 * @throws RrdException Thrown if name is invalid (null or empty).
-	 */
-	public RrdDef(final String path) throws RrdException {
-		if (path == null || path.length() == 0) {
-			throw new RrdException("No path specified");
-		}
-		this.path = path;
-	}
-
-	/**
-	 * <p>Creates new RRD definition object with the given path and step.</p>
-	 *
-	 * @param path Path to new RRD.
-	 * @param step RRD step.
-	 * @throws RrdException Thrown if supplied parameters are invalid.
-	 */
-	public RrdDef(final String path, final long step) throws RrdException {
-		this(path);
-		if (step <= 0) {
-			throw new RrdException("Invalid RRD step specified: " + step);
-		}
-		this.step = step;
-	}
-
-	/**
-	 * <p>Creates new RRD definition object with the given path, starting timestamp
-	 * and step.</p>
-	 *
-	 * @param path	  Path to new RRD.
-	 * @param startTime RRD starting timestamp.
-	 * @param step	  RRD step.
-	 * @throws RrdException Thrown if supplied parameters are invalid.
-	 */
-	public RrdDef(final String path, final long startTime, final long step) throws RrdException {
-		this(path, step);
-		if (startTime < 0) {
-			throw new RrdException("Invalid RRD start time specified: " + startTime);
-		}
-		this.startTime = startTime;
-	}
-
-	/**
-	 * Returns path for the new RRD
-	 *
-	 * @return path to the new RRD which should be created
-	 */
-	public String getPath() {
-		return path;
-	}
-
-	/**
-	 * Returns starting timestamp for the RRD that should be created.
-	 *
-	 * @return RRD starting timestamp
-	 */
-	public long getStartTime() {
-		return startTime;
-	}
-
-	/**
-	 * Returns time step for the RRD that will be created.
-	 *
-	 * @return RRD step
-	 */
-	public long getStep() {
-		return step;
-	}
-
-	/**
-	 * Sets path to RRD.
-	 *
-	 * @param path to new RRD.
-	 */
-	public void setPath(final String path) {
-		this.path = path;
-	}
-
-	/**
-	 * Sets RRD's starting timestamp.
-	 *
-	 * @param startTime starting timestamp.
-	 */
-	public void setStartTime(final long startTime) {
-		this.startTime = startTime;
-	}
-
-	/**
-	 * Sets RRD's starting timestamp.
-	 *
-	 * @param date starting date
-	 */
-	public void setStartTime(final Date date) {
-		this.startTime = Util.getTimestamp(date);
-	}
-
-	/**
-	 * Sets RRD's starting timestamp.
-	 *
-	 * @param gc starting date
-	 */
-	public void setStartTime(final Calendar gc) {
-		this.startTime = Util.getTimestamp(gc);
-	}
-
-	/**
-	 * Sets RRD's time step.
-	 *
-	 * @param step RRD time step.
-	 */
-	public void setStep(final long step) {
-		this.step = step;
-	}
-
-	/**
-	 * Adds single datasource definition represented with object of class <code>DsDef</code>.
-	 *
-	 * @param dsDef Datasource definition.
-	 * @throws RrdException Thrown if new datasource definition uses already used data
-	 *                      source name.
-	 */
-	public void addDatasource(final DsDef dsDef) throws RrdException {
-		if (dsDefs.contains(dsDef)) {
-			throw new RrdException("Datasource already defined: " + dsDef.dump());
-		}
-		dsDefs.add(dsDef);
-	}
-
-	/**
-	 * Adds single datasource to RRD definition by specifying its data source name, source type,
-	 * heartbeat, minimal and maximal value. For the complete explanation of all data
-	 * source definition parameters see RRDTool's
-	 * <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
-	 * <p>
-	 * <b>IMPORTANT NOTE:</b> If datasource name ends with '!', corresponding archives will never
-	 * store NaNs as datasource values. In that case, NaN datasource values will be silently
-	 * replaced with zeros by the framework.
-	 *
-	 * @param dsName	Data source name.
-	 * @param dsType	Data source type. Valid types are "COUNTER",
-	 *                  "GAUGE", "DERIVE" and "ABSOLUTE" (these string constants are conveniently defined in
-	 *                  the {@link DsTypes} class).
-	 * @param heartbeat Data source heartbeat.
-	 * @param minValue  Minimal acceptable value. Use <code>Double.NaN</code> if unknown.
-	 * @param maxValue  Maximal acceptable value. Use <code>Double.NaN</code> if unknown.
-	 * @throws RrdException Thrown if new datasource definition uses already used data
-	 *                      source name.
-	 */
-	public void addDatasource(final String dsName, final String dsType, final long heartbeat, final double minValue, final double maxValue) throws RrdException {
-		addDatasource(new DsDef(dsName, dsType, heartbeat, minValue, maxValue));
-	}
-
-	/**
-	 * Adds single datasource to RRD definition from a RRDTool-like
-	 * datasource definition string. The string must have six elements separated with colons
-	 * (:) in the following order:
-	 * <p>
-	 * <pre>
-	 * DS:name:type:heartbeat:minValue:maxValue
-	 * </pre>
-	 * For example:
-	 * <p>
-	 * <pre>
-	 * DS:input:COUNTER:600:0:U
-	 * </pre>
-	 * For more information on datasource definition parameters see <code>rrdcreate</code>
-	 * man page.
-	 *
-	 * @param rrdToolDsDef Datasource definition string with the syntax borrowed from RRDTool.
-	 * @throws RrdException Thrown if invalid string is supplied.
-	 */
-	public void addDatasource(final String rrdToolDsDef) throws RrdException {
-		final RrdException rrdException = new RrdException("Wrong rrdtool-like datasource definition: " + rrdToolDsDef);
-		final StringTokenizer tokenizer = new StringTokenizer(rrdToolDsDef, ":");
-		if (tokenizer.countTokens() != 6) {
-			throw rrdException;
-		}
-		final String[] tokens = new String[6];
-		for (int curTok = 0; tokenizer.hasMoreTokens(); curTok++) {
-			tokens[curTok] = tokenizer.nextToken();
-		}
-		if (!tokens[0].equalsIgnoreCase("DS")) {
-			throw rrdException;
-		}
-		final String dsName = tokens[1];
-		final String dsType = tokens[2];
-		long dsHeartbeat;
-		try {
-			dsHeartbeat = Long.parseLong(tokens[3]);
-		}
-		catch (final NumberFormatException nfe) {
-			throw rrdException;
-		}
-		double minValue = Double.NaN;
-		if (!tokens[4].equalsIgnoreCase("U")) {
-			try {
-				minValue = Double.parseDouble(tokens[4]);
-			}
-			catch (final NumberFormatException nfe) {
-				throw rrdException;
-			}
-		}
-		double maxValue = Double.NaN;
-		if (!tokens[5].equalsIgnoreCase("U")) {
-			try {
-				maxValue = Double.parseDouble(tokens[5]);
-			}
-			catch (final NumberFormatException nfe) {
-				throw rrdException;
-			}
-		}
-		addDatasource(new DsDef(dsName, dsType, dsHeartbeat, minValue, maxValue));
-	}
-
-	/**
-	 * Adds data source definitions to RRD definition in bulk.
-	 *
-	 * @param dsDefs Array of data source definition objects.
-	 * @throws RrdException Thrown if duplicate data source name is used.
-	 */
-	public void addDatasource(final DsDef[] dsDefs) throws RrdException {
-		for (final DsDef dsDef : dsDefs) {
-			addDatasource(dsDef);
-		}
-	}
-
-	/**
-	 * Adds single archive definition represented with object of class <code>ArcDef</code>.
-	 *
-	 * @param arcDef Archive definition.
-	 * @throws RrdException Thrown if archive with the same consolidation function
-	 *                      and the same number of steps is already added.
-	 */
-	public void addArchive(final ArcDef arcDef) throws RrdException {
-		if (arcDefs.contains(arcDef)) {
-			throw new RrdException("Archive already defined: " + arcDef.dump());
-		}
-		arcDefs.add(arcDef);
-	}
-
-	/**
-	 * Adds archive definitions to RRD definition in bulk.
-	 *
-	 * @param arcDefs Array of archive definition objects
-	 * @throws RrdException Thrown if RRD definition already contains archive with
-	 *                      the same consolidation function and the same number of steps.
-	 */
-	public void addArchive(final ArcDef[] arcDefs) throws RrdException {
-		for (final ArcDef arcDef : arcDefs) {
-			addArchive(arcDef);
-		}
-	}
-
-	/**
-	 * Adds single archive definition by specifying its consolidation function, X-files factor,
-	 * number of steps and rows. For the complete explanation of all archive
-	 * definition parameters see RRDTool's
-	 * <a href="../../../../man/rrdcreate.html" target="man">rrdcreate man page</a>.
-	 * <p>
-	 *
-	 * @param consolFun Consolidation function. Valid values are "AVERAGE",
-	 *                  "MIN", "MAX" and "LAST" (these constants are conveniently defined in the
-	 *                  {@link ConsolFuns} class)
-	 * @param xff	   X-files factor. Valid values are between 0 and 1.
-	 * @param steps	 Number of archive steps
-	 * @param rows	  Number of archive rows
-	 * @throws RrdException Thrown if archive with the same consolidation function
-	 *                      and the same number of steps is already added.
-	 */
-	public void addArchive(final String consolFun, final double xff, final int steps, final int rows) throws RrdException {
-		addArchive(new ArcDef(consolFun, xff, steps, rows));
-	}
-
-	/**
-	 * Adds single archive to RRD definition from a RRDTool-like
-	 * archive definition string. The string must have five elements separated with colons
-	 * (:) in the following order:
-	 * <p>
-	 * <pre>
-	 * RRA:consolidationFunction:XFilesFactor:steps:rows
-	 * </pre>
-	 * For example:
-	 * <p>
-	 * <pre>
-	 * RRA:AVERAGE:0.5:10:1000
-	 * </pre>
-	 * For more information on archive definition parameters see <code>rrdcreate</code>
-	 * man page.
-	 *
-	 * @param rrdToolArcDef Archive definition string with the syntax borrowed from RRDTool.
-	 * @throws RrdException Thrown if invalid string is supplied.
-	 */
-	public void addArchive(final String rrdToolArcDef) throws RrdException {
-	    final RrdException rrdException = new RrdException("Wrong rrdtool-like archive definition: " + rrdToolArcDef);
-		final StringTokenizer tokenizer = new StringTokenizer(rrdToolArcDef, ":");
-		if (tokenizer.countTokens() != 5) {
-			throw rrdException;
-		}
-		final String[] tokens = new String[5];
-		for (int curTok = 0; tokenizer.hasMoreTokens(); curTok++) {
-			tokens[curTok] = tokenizer.nextToken();
-		}
-		if (!tokens[0].equalsIgnoreCase("RRA")) {
-			throw rrdException;
-		}
-		final String consolFun = tokens[1];
-		double xff;
-		try {
-			xff = Double.parseDouble(tokens[2]);
-		}
-		catch (final NumberFormatException nfe) {
-			throw rrdException;
-		}
-		int steps;
-		try {
-			steps = Integer.parseInt(tokens[3]);
-		}
-		catch (final NumberFormatException nfe) {
-			throw rrdException;
-		}
-		int rows;
-		try {
-			rows = Integer.parseInt(tokens[4]);
-		}
-		catch (final NumberFormatException nfe) {
-			throw rrdException;
-		}
-		addArchive(new ArcDef(consolFun, xff, steps, rows));
-	}
-
-	void validate() throws RrdException {
-		if (dsDefs.size() == 0) {
-			throw new RrdException("No RRD datasource specified. At least one is needed.");
-		}
-		if (arcDefs.size() == 0) {
-			throw new RrdException("No RRD archive specified. At least one is needed.");
-		}
-	}
-
-	/**
-	 * Returns all data source definition objects specified so far.
-	 *
-	 * @return Array of data source definition objects
-	 */
-	public DsDef[] getDsDefs() {
-		return dsDefs.toArray(new DsDef[0]);
-	}
-
-	/**
-	 * Returns all archive definition objects specified so far.
-	 *
-	 * @return Array of archive definition objects.
-	 */
-	public ArcDef[] getArcDefs() {
-		return arcDefs.toArray(new ArcDef[0]);
-	}
-
-	/**
-	 * Returns number of defined datasources.
-	 *
-	 * @return Number of defined datasources.
-	 */
-	public int getDsCount() {
-		return dsDefs.size();
-	}
-
-	/**
-	 * Returns number of defined archives.
-	 *
-	 * @return Number of defined archives.
-	 */
-	public int getArcCount() {
-		return arcDefs.size();
-	}
-
-	/**
-	 * Returns string that represents all specified RRD creation parameters. Returned string
-	 * has the syntax of RRDTool's <code>create</code> command.
-	 *
-	 * @return Dumped content of <code>RrdDb</code> object.
-	 */
-	public String dump() {
-	    final StringBuffer buffer = new StringBuffer("create \"");
-		buffer.append(path).append("\"");
-		buffer.append(" --start ").append(getStartTime());
-		buffer.append(" --step ").append(getStep()).append(" ");
-		for (final DsDef dsDef : dsDefs) {
-			buffer.append(dsDef.dump()).append(" ");
-		}
-		for (final ArcDef arcDef : arcDefs) {
-			buffer.append(arcDef.dump()).append(" ");
-		}
-		return buffer.toString().trim();
-	}
-
-	String getRrdToolCommand() {
-		return dump();
-	}
-
-	void removeDatasource(final String dsName) throws RrdException {
-		for (int i = 0; i < dsDefs.size(); i++) {
-		    final DsDef dsDef = dsDefs.get(i);
-			if (dsDef.getDsName().equals(dsName)) {
-				dsDefs.remove(i);
-				return;
-			}
-		}
-		throw new RrdException("Could not find datasource named '" + dsName + "'");
-	}
-
-	void saveSingleDatasource(final String dsName) {
-	    final Iterator<DsDef> it = dsDefs.iterator();
-		while (it.hasNext()) {
-			final DsDef dsDef = it.next();
-			if (!dsDef.getDsName().equals(dsName)) {
-				it.remove();
-			}
-		}
-	}
-
-	void removeArchive(final String consolFun, final int steps) throws RrdException {
-	    final ArcDef arcDef = findArchive(consolFun, steps);
-		if (!arcDefs.remove(arcDef)) {
-			throw new RrdException("Could not remove archive " + consolFun + "/" + steps);
-		}
-	}
-
-	ArcDef findArchive(final String consolFun, final int steps) throws RrdException {
-	    for (final ArcDef arcDef : arcDefs) {
-			if (arcDef.getConsolFun().equals(consolFun) && arcDef.getSteps() == steps) {
-				return arcDef;
-			}
-		}
-		throw new RrdException("Could not find archive " + consolFun + "/" + steps);
-	}
-
-	/**
-	 * Exports RrdDef object to output stream in XML format. Generated XML code can be parsed
-	 * with {@link RrdDefTemplate} class.
-	 *
-	 * @param out Output stream
-	 */
-	public void exportXmlTemplate(final OutputStream out) {
-	    final XmlWriter xml = new XmlWriter(out);
-		xml.startTag("rrd_def");
-		xml.writeTag("path", getPath());
-		xml.writeTag("step", getStep());
-		xml.writeTag("start", getStartTime());
-		for (final DsDef dsDef : getDsDefs()) {
-			xml.startTag("datasource");
-			xml.writeTag("name", dsDef.getDsName());
-			xml.writeTag("type", dsDef.getDsType());
-			xml.writeTag("heartbeat", dsDef.getHeartbeat());
-			xml.writeTag("min", dsDef.getMinValue(), "U");
-			xml.writeTag("max", dsDef.getMaxValue(), "U");
-			xml.closeTag(); // datasource
-		}
-		for (ArcDef arcDef : getArcDefs()) {
-			xml.startTag("archive");
-			xml.writeTag("cf", arcDef.getConsolFun());
-			xml.writeTag("xff", arcDef.getXff());
-			xml.writeTag("steps", arcDef.getSteps());
-			xml.writeTag("rows", arcDef.getRows());
-			xml.closeTag(); // archive
-		}
-		xml.closeTag(); // rrd_def
-		xml.flush();
-	}
-
-	/**
-	 * Exports RrdDef object to string in XML format. Generated XML string can be parsed
-	 * with {@link RrdDefTemplate} class.
-	 *
-	 * @return XML formatted string representing this RrdDef object
-	 */
-	public String exportXmlTemplate() {
-	    final ByteArrayOutputStream out = new ByteArrayOutputStream();
-		exportXmlTemplate(out);
-		return out.toString();
-	}
-
-	/**
-	 * Exports RrdDef object to a file in XML format. Generated XML code can be parsed
-	 * with {@link RrdDefTemplate} class.
-	 *
-	 * @param filePath path to the file
-	 * @throws IOException if an I/O error occurs.
-	 */
-	public void exportXmlTemplate(final String filePath) throws IOException {
-	    final FileOutputStream out = new FileOutputStream(filePath, false);
-		exportXmlTemplate(out);
-		out.close();
-	}
-
-	/**
-	 * Returns the number of storage bytes required to create RRD from this
-	 * RrdDef object.
-	 *
-	 * @return Estimated byte count of the underlying RRD storage.
-	 */
-	public long getEstimatedSize() {
-		final int dsCount = dsDefs.size();
-		final int arcCount = arcDefs.size();
-		int rowsCount = 0;
-		for (final ArcDef arcDef : arcDefs) {
-			rowsCount += arcDef.getRows();
-		}
-		return calculateSize(dsCount, arcCount, rowsCount);
-	}
-
-	static long calculateSize(final int dsCount, final int arcCount, final int rowsCount) {
-		return (24L + 48L * dsCount + 16L * arcCount +
-				20L * dsCount * arcCount + 8L * dsCount * rowsCount) +
-				(1L + 2L * dsCount + arcCount) * 2L * RrdPrimitive.STRING_LENGTH;
-	}
-
-	/**
-	 * Compares the current RrdDef with another. RrdDefs are considered equal if:<p>
-	 * <ul>
-	 * <li>RRD steps match
-	 * <li>all datasources have exactly the same definition in both RrdDef objects (datasource names,
-	 * types, heartbeat, min and max values must match)
-	 * <li>all archives have exactly the same definition in both RrdDef objects (archive consolidation
-	 * functions, X-file factors, step and row counts must match)
-	 * </ul>
-	 *
-	 * @param obj The second RrdDef object
-	 * @return true if RrdDefs match exactly, false otherwise
-	 */
-	public boolean equals(final Object obj) {
-		if (obj == null || !(obj instanceof RrdDef)) {
-			return false;
-		}
-		final RrdDef rrdDef2 = (RrdDef) obj;
-		// check primary RRD step
-		if (step != rrdDef2.step) {
-			return false;
-		}
-		// check datasources
-		final DsDef[] dsDefs = getDsDefs();
-		final DsDef[] dsDefs2 = rrdDef2.getDsDefs();
-		if (dsDefs.length != dsDefs2.length) {
-			return false;
-		}
-		for (final DsDef dsDef : dsDefs) {
-			boolean matched = false;
-			for (final DsDef dsDef2 : dsDefs2) {
-				if (dsDef.exactlyEqual(dsDef2)) {
-					matched = true;
-					break;
-				}
-			}
-			// this datasource could not be matched
-			if (!matched) {
-				return false;
-			}
-		}
-		// check archives
-		final ArcDef[] arcDefs = getArcDefs();
-		final ArcDef[] arcDefs2 = rrdDef2.getArcDefs();
-		if (arcDefs.length != arcDefs2.length) {
-			return false;
-		}
-		for (final ArcDef arcDef : arcDefs) {
-			boolean matched = false;
-			for (final ArcDef arcDef2 : arcDefs2) {
-				if (arcDef.exactlyEqual(arcDef2)) {
-					matched = true;
-					break;
-				}
-			}
-			// this archive could not be matched
-			if (!matched) {
-				return false;
-			}
-		}
-		// everything matches
-		return true;
-	}
-
-	public int hashCode() {
-		int hashCode = (int)step;
-		for (final DsDef dsDef : dsDefs) {
-			hashCode *= dsDef.hashCode();
-		}
-		for (final ArcDef arcDef : arcDefs) {
-			hashCode *= arcDef.hashCode();
-		}
-		return hashCode;
-	}
-
-	/**
-	 * Removes all datasource definitions.
-	 */
-	public void removeDatasources() {
-		dsDefs.clear();
-	}
-
-	/**
-	 * Removes all RRA archive definitions.
-	 */
-	public void removeArchives() {
-		arcDefs.clear();
-	}
-
-    public String toString() {
-        return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[arcDefs=[" + join(getArcDefs()) + "],dsDefs=[" + join(getDsDefs()) + "]]";
-    }
-
-    private String join(final Object[] objs) {
-        final StringBuffer sb = new StringBuffer();
-        for (int i = 0; i < objs.length; i++) {
-            sb.append(objs[i]);
-            if (i != (objs.length - 1)) {
-                sb.append(",");
-            }
-        }
-        return sb.toString();
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdDefTemplate.java b/apps/jrobin/java/src/org/jrobin/core/RrdDefTemplate.java
deleted file mode 100644
index 7e667a80723982da144d79c8d0d86f0d9ce2a759..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdDefTemplate.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core;
-
-import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Calendar;
-
-/**
- * Class used to create an arbitrary number of {@link RrdDef} (RRD definition) objects
- * from a single XML template. XML template can be supplied as an XML InputSource,
- * XML file or XML formatted string.
- * <p>
- * Here is an example of a properly formatted XML template with all available
- * options in it (unwanted options can be removed):
- * <pre>
- * &lt;rrd_def&gt;
- *     &lt;path&gt;test.rrd&lt;/path&gt;
- *     &lt;!-- not mandatory --&gt;
- *     &lt;start&gt;1000123456&lt;/start&gt;
- *     &lt;!-- not mandatory --&gt;
- *     &lt;step&gt;300&lt;/step&gt;
- *     &lt;!-- at least one datasource must be supplied --&gt;
- *     &lt;datasource&gt;
- *         &lt;name&gt;input&lt;/name&gt;
- *         &lt;type&gt;COUNTER&lt;/type&gt;
- *         &lt;heartbeat&gt;300&lt;/heartbeat&gt;
- *         &lt;min&gt;0&lt;/min&gt;
- *         &lt;max&gt;U&lt;/max&gt;
- *     &lt;/datasource&gt;
- *     &lt;datasource&gt;
- *         &lt;name&gt;temperature&lt;/name&gt;
- *         &lt;type&gt;GAUGE&lt;/type&gt;
- *         &lt;heartbeat&gt;400&lt;/heartbeat&gt;
- *         &lt;min&gt;U&lt;/min&gt;
- *         &lt;max&gt;1000&lt;/max&gt;
- *     &lt;/datasource&gt;
- *     &lt;!-- at least one archive must be supplied --&gt;
- *     &lt;archive&gt;
- *         &lt;cf&gt;AVERAGE&lt;/cf&gt;
- *         &lt;xff&gt;0.5&lt;/xff&gt;
- *         &lt;steps&gt;1&lt;/steps&gt;
- *         &lt;rows&gt;600&lt;/rows&gt;
- *     &lt;/archive&gt;
- *     &lt;archive&gt;
- *         &lt;cf&gt;MAX&lt;/cf&gt;
- *         &lt;xff&gt;0.6&lt;/xff&gt;
- *         &lt;steps&gt;6&lt;/steps&gt;
- *         &lt;rows&gt;7000&lt;/rows&gt;
- *     &lt;/archive&gt;
- * &lt;/rrd_def&gt;
- * </pre>
- * Notes on the template syntax:<p>
- * <ul>
- * <li>There is a strong relation between the XML template syntax and the syntax of
- * {@link RrdDef} class methods. If you are not sure what some XML tag means, check javadoc
- * for the corresponding class.
- * <li>starting timestamp can be supplied either as a long integer
- * (like: 1000243567) or as an ISO formatted string (like: 2004-02-21 12:25:45)
- * <li>whitespaces are not harmful
- * <li>floating point values: anything that cannot be parsed will be treated as Double.NaN
- * (like: U, unknown, 12r.23)
- * <li>comments are allowed.
- * </ul>
- * Any template value (text between <code>&lt;some_tag&gt;</code> and
- * <code>&lt;/some_tag&gt;</code>) can be replaced with
- * a variable of the following form: <code>${variable_name}</code>. Use
- * {@link XmlTemplate#setVariable(String, String) setVariable()}
- * methods from the base class to replace template variables with real values
- * at runtime.
- * <p>
- * Typical usage scenario:<p>
- * <ul>
- * <li>Create your XML template and save it to a file (template.xml, for example)
- * <li>Replace hardcoded template values with variables if you want to change them during runtime.
- * For example, RRD path should not be hardcoded in the template - you probably want to create
- * many different RRD files from the same XML template. For example, your XML
- * template could start with:
- * <pre>
- * &lt;rrd_def&gt;
- *     &lt;path&gt;${path}&lt;/path&gt;
- *     &lt;step&gt;300&lt;/step&gt;
- *     ...
- * </pre>
- * <li>In your Java code, create RrdDefTemplate object using your XML template file:
- * <pre>
- * RrdDefTemplate t = new RrdDefTemplate(new File(template.xml));
- * </pre>
- * <li>Then, specify real values for template variables:
- * <pre>
- * t.setVariable("path", "demo/test.rrd");
- * </pre>
- * <li>Once all template variables are set, just use the template object to create RrdDef
- * object. This object is actually used to create JRobin RRD files:
- * <pre>
- * RrdDef def = t.getRrdDef();
- * RrdDb rrd = new RrdDb(def);
- * rrd.close();
- * </pre>
- * </ul>
- * You should create new RrdDefTemplate object only once for each XML template. Single template
- * object can be reused to create as many RrdDef objects as needed, with different values
- * specified for template variables. XML synatax check is performed only once - the first
- * definition object gets created relatively slowly, but it will be created much faster next time.
- */
-public class RrdDefTemplate extends XmlTemplate {
-	/**
-	 * Creates RrdDefTemplate object from any parsable XML input source. Read general information
-	 * for this class to find an example of a properly formatted RrdDef XML source.
-	 *
-	 * @param xmlInputSource Xml input source
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of XML related error (parsing error, for example)
-	 */
-	public RrdDefTemplate(InputSource xmlInputSource) throws IOException, RrdException {
-		super(xmlInputSource);
-	}
-
-	/**
-	 * Creates RrdDefTemplate object from the string containing XML template.
-	 * Read general information for this class to see an example of a properly formatted XML source.
-	 *
-	 * @param xmlString String containing XML template
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of XML related error (parsing error, for example)
-	 */
-	public RrdDefTemplate(String xmlString) throws IOException, RrdException {
-		super(xmlString);
-	}
-
-	/**
-	 * Creates RrdDefTemplate object from the file containing XML template.
-	 * Read general information for this class to see an example of a properly formatted XML source.
-	 *
-	 * @param xmlFile File object representing file with XML template
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of XML related error (parsing error, for example)
-	 */
-	public RrdDefTemplate(File xmlFile) throws IOException, RrdException {
-		super(xmlFile);
-	}
-
-	/**
-	 * Returns RrdDef object constructed from the underlying XML template. Before this method
-	 * is called, values for all non-optional placeholders must be supplied. To specify
-	 * placeholder values at runtime, use some of the overloaded
-	 * {@link XmlTemplate#setVariable(String, String) setVariable()} methods. Once this method
-	 * returns, all placeholder values are preserved. To remove them all, call inhereted
-	 * {@link XmlTemplate#clearValues() clearValues()} method explicitly.<p>
-	 *
-	 * @return RrdDef object constructed from the underlying XML template,
-	 *         with all placeholders replaced with real values. This object can be passed to the constructor
-	 *         of the new RrdDb object.
-	 * @throws RrdException Thrown (in most cases) if the value for some placeholder
-	 *                      was not supplied through {@link XmlTemplate#setVariable(String, String) setVariable()}
-	 *                      method call
-	 */
-	public RrdDef getRrdDef() throws RrdException {
-		if (!root.getTagName().equals("rrd_def")) {
-			throw new RrdException("XML definition must start with <rrd_def>");
-		}
-		validateTagsOnlyOnce(root, new String[] {
-				"path", "start", "step", "datasource*", "archive*"
-		});
-		// PATH must be supplied or exception is thrown
-		String path = getChildValue(root, "path");
-		RrdDef rrdDef = new RrdDef(path);
-		try {
-			String startStr = getChildValue(root, "start");
-			Calendar startGc = Util.getCalendar(startStr);
-			rrdDef.setStartTime(startGc);
-		}
-		catch (RrdException e) {
-			// START is not mandatory
-		}
-		try {
-			long step = getChildValueAsLong(root, "step");
-			rrdDef.setStep(step);
-		}
-		catch (RrdException e) {
-			// STEP is not mandatory
-		}
-		// datsources
-		Node[] dsNodes = getChildNodes(root, "datasource");
-		for (Node dsNode : dsNodes) {
-			validateTagsOnlyOnce(dsNode, new String[] {
-					"name", "type", "heartbeat", "min", "max"
-			});
-			String name = getChildValue(dsNode, "name");
-			String type = getChildValue(dsNode, "type");
-			long heartbeat = getChildValueAsLong(dsNode, "heartbeat");
-			double min = getChildValueAsDouble(dsNode, "min");
-			double max = getChildValueAsDouble(dsNode, "max");
-			rrdDef.addDatasource(name, type, heartbeat, min, max);
-		}
-		// archives
-		Node[] arcNodes = getChildNodes(root, "archive");
-		for (Node arcNode : arcNodes) {
-			validateTagsOnlyOnce(arcNode, new String[] {
-					"cf", "xff", "steps", "rows"
-			});
-			String consolFun = getChildValue(arcNode, "cf");
-			double xff = getChildValueAsDouble(arcNode, "xff");
-			int steps = getChildValueAsInt(arcNode, "steps");
-			int rows = getChildValueAsInt(arcNode, "rows");
-			rrdDef.addArchive(consolFun, xff, steps, rows);
-		}
-		return rrdDef;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdDouble.java b/apps/jrobin/java/src/org/jrobin/core/RrdDouble.java
deleted file mode 100644
index 06b27d76b20f50ba344aeac03be803ad77419607..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdDouble.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-class RrdDouble extends RrdPrimitive {
-	private double cache;
-	private boolean cached = false;
-
-	RrdDouble(final RrdUpdater updater, final boolean isConstant) throws IOException {
-		super(updater, RrdDouble.RRD_DOUBLE, isConstant);
-	}
-
-	RrdDouble(final RrdUpdater updater) throws IOException {
-		super(updater, RrdDouble.RRD_DOUBLE, false);
-	}
-
-	void set(final double value) throws IOException {
-		if (!isCachingAllowed()) {
-			writeDouble(value);
-		}
-		// caching allowed
-		else if (!cached || !Util.equal(cache, value)) {
-			// update cache
-			writeDouble(cache = value);
-			cached = true;
-		}
-	}
-
-	double get() throws IOException {
-		return cached ? cache : readDouble();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdDoubleArray.java b/apps/jrobin/java/src/org/jrobin/core/RrdDoubleArray.java
deleted file mode 100644
index 03f71dc8d1e984f154ffa3403be258227c0e495e..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdDoubleArray.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-class RrdDoubleArray extends RrdPrimitive {
-	private int length;
-
-	RrdDoubleArray(final RrdUpdater updater, final int length) throws IOException {
-		super(updater, RrdPrimitive.RRD_DOUBLE, length, false);
-		this.length = length;
-	}
-
-	void set(final int index, final double value) throws IOException {
-		set(index, value, 1);
-	}
-
-	void set(final int index, final double value, final int count) throws IOException {
-		// rollovers not allowed!
-		assert index + count <= length:	"Invalid robin index supplied: index=" + index +", count=" + count + ", length=" + length;
-		writeDouble(index, value, count);
-	}
-
-	double get(final int index) throws IOException {
-		assert index < length: "Invalid index supplied: " + index + ", length=" + length;
-		return readDouble(index);
-	}
-
-	double[] get(final int index, final int count) throws IOException {
-		assert index + count <= length: "Invalid index/count supplied: " + index + "/" + count + " (length=" + length + ")";
-		return readDouble(index, count);
-	}
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdException.java b/apps/jrobin/java/src/org/jrobin/core/RrdException.java
deleted file mode 100644
index 3ba6ae4d62e419fc39d4855a61399c94b3989127..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdException.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-/**
- * Class to represent various JRobin checked exceptions.
- * JRobin code can throw only <code>RrdException</code>
- * (for various JRobin related errors) or <code>IOException</code>
- * (for various I/O errors).
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class RrdException extends Exception {
-    private static final long serialVersionUID = 6999702149227009855L;
-
-    public RrdException() {
-	    super();
-	}
-
-	public RrdException(final String message) {
-		super(message);
-	}
-
-	public RrdException(final Throwable cause) {
-		super(cause);
-	}
-
-	public RrdException(final String message, final Throwable cause) {
-	    super(message, cause);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdFileBackend.java b/apps/jrobin/java/src/org/jrobin/core/RrdFileBackend.java
deleted file mode 100644
index ee213f6a118a710a37d0c1c0825d048c531c11d7..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdFileBackend.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.io.RandomAccessFile;
-
-/**
- * JRobin backend which is used to store RRD data to ordinary files on the disk. This was the
- * default factory before 1.4.0 version.
- * <p>
- * This backend is based on the RandomAccessFile class (java.io.* package).
- */
-public class RrdFileBackend extends RrdBackend {
-	/**
-	 * radnom access file handle
-	 */
-	protected RandomAccessFile file;
-
-	/**
-	 * Creates RrdFileBackend object for the given file path, backed by RandomAccessFile object.
-	 *
-	 * @param path	 Path to a file
-	 * @param readOnly True, if file should be open in a read-only mode. False otherwise
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	protected RrdFileBackend(final String path, final boolean readOnly) throws IOException {
-		super(path, readOnly);
-		this.file = new RandomAccessFile(path, readOnly ? "r" : "rw");
-	}
-
-	/**
-	 * Closes the underlying RRD file.
-	 *
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public void close() throws IOException {
-		file.close();
-	}
-
-	/**
-	 * Returns canonical path to the file on the disk.
-	 *
-	 * @param path File path
-	 * @return Canonical file path
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public static String getCanonicalPath(String path) throws IOException {
-		return Util.getCanonicalPath(path);
-	}
-
-	/**
-	 * Returns canonical path to the file on the disk.
-	 *
-	 * @return Canonical file path
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public String getCanonicalPath() throws IOException {
-		return RrdFileBackend.getCanonicalPath(getPath());
-	}
-
-	/**
-	 * Writes bytes to the underlying RRD file on the disk
-	 *
-	 * @param offset Starting file offset
-	 * @param b	  Bytes to be written.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	protected void write(long offset, byte[] b) throws IOException {
-		file.seek(offset);
-		file.write(b);
-	}
-
-	/**
-	 * Reads a number of bytes from the RRD file on the disk
-	 *
-	 * @param offset Starting file offset
-	 * @param b	  Buffer which receives bytes read from the file.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected void read(long offset, byte[] b) throws IOException {
-		file.seek(offset);
-		if (file.read(b) != b.length) {
-			throw new IOException("Not enough bytes available in file " + getPath());
-		}
-	}
-
-	/**
-	 * Returns RRD file length.
-	 *
-	 * @return File length.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public long getLength() throws IOException {
-		return file.length();
-	}
-
-	/**
-	 * Sets length of the underlying RRD file. This method is called only once, immediately
-	 * after a new RRD file gets created.
-	 *
-	 * @param length Length of the RRD file
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected void setLength(long length) throws IOException {
-		file.setLength(length);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdFileBackendFactory.java b/apps/jrobin/java/src/org/jrobin/core/RrdFileBackendFactory.java
deleted file mode 100644
index e89959616f7237ad4532bf4c2d38cdc2896f94d9..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdFileBackendFactory.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Factory class which creates actual {@link RrdFileBackend} objects. This was the default
- * backend factory in JRobin before 1.4.0 release.
- */
-public class RrdFileBackendFactory extends RrdBackendFactory {
-	/**
-	 * factory name, "FILE"
-	 */
-	public static final String NAME = "FILE";
-
-	/**
-	 * Creates RrdFileBackend object for the given file path.
-	 *
-	 * @param path	 File path
-	 * @param readOnly True, if the file should be accessed in read/only mode.
-	 *                 False otherwise.
-	 * @return RrdFileBackend object which handles all I/O operations for the given file path
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected RrdBackend open(String path, boolean readOnly) throws IOException {
-		return new RrdFileBackend(path, readOnly);
-	}
-
-	/**
-	 * Method to determine if a file with the given path already exists.
-	 *
-	 * @param path File path
-	 * @return True, if such file exists, false otherwise.
-	 */
-	protected boolean exists(String path) {
-		return Util.fileExists(path);
-	}
-
-	/**
-	 * Returns the name of this factory.
-	 *
-	 * @return Factory name (equals to string "FILE")
-	 */
-	public String getFactoryName() {
-		return NAME;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdInt.java b/apps/jrobin/java/src/org/jrobin/core/RrdInt.java
deleted file mode 100644
index 3213d541c9391f4b1e97f32c007e3ead5a7545f2..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdInt.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-class RrdInt extends RrdPrimitive {
-	private int cache;
-	private boolean cached = false;
-
-	RrdInt(final RrdUpdater updater, final boolean isConstant) throws IOException {
-		super(updater, RrdPrimitive.RRD_INT, isConstant);
-	}
-
-	RrdInt(final RrdUpdater updater) throws IOException {
-		this(updater, false);
-	}
-
-	void set(final int value) throws IOException {
-		if (!isCachingAllowed()) {
-			writeInt(value);
-		}
-		// caching allowed
-		else if (!cached || cache != value) {
-			// update cache
-			writeInt(cache = value);
-			cached = true;
-		}
-	}
-
-	int get() throws IOException {
-		return cached ? cache : readInt();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdJRobin14FileBackend.java b/apps/jrobin/java/src/org/jrobin/core/RrdJRobin14FileBackend.java
deleted file mode 100644
index 7e8ba8b09eefe3a0f3a4f284f907c92a1ad7f4fa..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdJRobin14FileBackend.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/* ============================================================
- * JRobin : Pure java implementation of RRDTool's functionality
- * ============================================================
- *
- * Project Info:  http://www.jrobin.org
- * Project Lead:  Sasa Markovic (saxon@jrobin.org);
- *
- * (C) Copyright 2003, by Sasa Markovic.
- *
- * Developers:    Sasa Markovic (saxon@jrobin.org)
- *                Arne Vandamme (cobralord@jrobin.org)
- *
- * This library is free software; you can redistribute it and/or modify it under the terms
- * of the GNU Lesser General Public License as published by the Free Software Foundation;
- * either version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with this
- * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileLock;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * JRobin backend which is used to store RRD data to ordinary files on the disk. This was the
- * default factory before 1.4.0 version.
- * <p>
- * This backend is based on the RandomAccessFile class (java.io.* package).
- */
-public class RrdJRobin14FileBackend extends RrdBackend {
-	private static final long LOCK_DELAY = 100; // 0.1sec
-
-	private static Set<String> m_openFiles = new HashSet<String>();
-
-    public static enum LockMode {
-        EXCEPTION_IF_LOCKED,
-        WAIT_IF_LOCKED,
-        NO_LOCKS
-    };
-
-	/** locking mode */
-	protected LockMode m_lockMode;
-
-	/** random access file handle */
-	protected RandomAccessFile m_file;
-	/** file lock */
-	protected FileLock m_fileLock;
-
-	/**
-	 * Creates RrdFileBackend object for the given file path, backed by RandomAccessFile object.
-	 * @param path Path to a file
-	 * @param readOnly True, if file should be open in a read-only mode. False otherwise
-	 * @param lockMode Locking mode: Exception if locked, Wait if locked, or no locks.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	protected RrdJRobin14FileBackend(String path, boolean readOnly, LockMode lockMode) throws IOException {
-	    super(path, readOnly);
-		m_lockMode = lockMode;
-		m_file = new RandomAccessFile(path, readOnly ? "r" : "rw");
-		try {
-			lockFile();
-			registerWriter();
-		} catch(final IOException ioe) {
-			close();
-			throw ioe;
-		}
-		System.err.println(String.format("backend initialized with path=%s, readOnly=%s, lockMode=%s", path, Boolean.valueOf(readOnly), lockMode));
-	}
-
-	private void lockFile() throws IOException {
-		switch (m_lockMode) {
-			case EXCEPTION_IF_LOCKED:
-				m_fileLock = m_file.getChannel().tryLock();
-				if (m_fileLock == null) {
-					// could not obtain lock
-					throw new IOException("Access denied. " + "File [" + getPath() + "] already locked");
-				}
-				break;
-			case WAIT_IF_LOCKED:
-				while (m_fileLock == null) {
-					m_fileLock = m_file.getChannel().tryLock();
-					if (m_fileLock == null) {
-						// could not obtain lock, wait a little, than try again
-						try {
-							Thread.sleep(LOCK_DELAY);
-						}
-						catch (final InterruptedException e) {
-						    Thread.currentThread().interrupt();
-						}
-					}
-				}
-				break;
-			case NO_LOCKS:
-				break;
-		}
-	}
-
-	private void registerWriter() throws IOException {
-		if (!isReadOnly()) {
-		    final String canonicalPath = Util.getCanonicalPath(getPath());
-			synchronized (m_openFiles) {
-				if (m_openFiles.contains(canonicalPath)) {
-					throw new IOException("File \"" + getPath() + "\" already open for R/W access. " +
-							"You cannot open the same file for R/W access twice");
-				}
-				else {
-					m_openFiles.add(canonicalPath);
-				}
-			}
-		}
-	}
-
-	/**
-	 * Closes the underlying RRD file.
-	 *
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public void close() throws IOException {
-		unregisterWriter();
-		try {
-			unlockFile();
-		} finally {
-			m_file.close();
-		}
-	}
-
-	private void unlockFile() throws IOException {
-		if (m_fileLock != null) {
-			m_fileLock.release();
-		}
-	}
-
-	private void unregisterWriter() throws IOException {
-		if (!isReadOnly()) {
-			synchronized (m_openFiles) {
-	            m_openFiles.remove(Util.getCanonicalPath(getPath()));
-			}
-		}
-	}
-
-	/**
-	 * Returns canonical path to the file on the disk.
-	 *
-	 * @return Canonical file path
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public String getCanonicalPath() throws IOException {
-		return Util.getCanonicalPath(getPath());
-	}
-
-	/**
-	 * Writes bytes to the underlying RRD file on the disk
-	 *
-	 * @param offset Starting file offset
-	 * @param b      Bytes to be written.
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	protected void write(final long offset, final byte[] b) throws IOException {
-		m_file.seek(offset);
-		m_file.write(b);
-	}
-
-	/**
-	 * Reads a number of bytes from the RRD file on the disk
-	 *
-	 * @param offset Starting file offset
-	 * @param b      Buffer which receives bytes read from the file.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected void read(final long offset, final byte[] b) throws IOException {
-		m_file.seek(offset);
-		if (m_file.read(b) != b.length) {
-			throw new IOException("Not enough bytes available in file " + getPath());
-		}
-	}
-
-	/**
-	 * Returns RRD file length.
-	 *
-	 * @return File length.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	public long getLength() throws IOException {
-		return m_file.length();
-	}
-
-	/**
-	 * Sets length of the underlying RRD file. This method is called only once, immediately
-	 * after a new RRD file gets created.
-	 *
-	 * @param length Length of the RRD file
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected void setLength(final long length) throws IOException {
-		m_file.setLength(length);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdJRobin14FileBackendFactory.java b/apps/jrobin/java/src/org/jrobin/core/RrdJRobin14FileBackendFactory.java
deleted file mode 100644
index 910394e71fc4c4f5dbc2ae0d1d03e66d8455c6ea..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdJRobin14FileBackendFactory.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-import org.jrobin.core.RrdJRobin14FileBackend.LockMode;
-
-/**
- * Factory class which creates actual {@link RrdFileBackend} objects. This was the default
- * backend factory in JRobin before 1.4.0 release.
- */
-public class RrdJRobin14FileBackendFactory extends RrdBackendFactory {
-	/**
-	 * factory name, "FILE"
-	 */
-	public static final String NAME = "14FILE";
-	private LockMode m_lockMode = LockMode.NO_LOCKS;
-
-	public RrdJRobin14FileBackendFactory() {
-	    super();
-	}
-
-    public RrdJRobin14FileBackendFactory(final LockMode lockMode) {
-        super();
-        m_lockMode = lockMode;
-    }
-
-	/**
-	 * Creates RrdFileBackend object for the given file path.
-	 *
-	 * @param path	 File path
-	 * @param readOnly True, if the file should be accessed in read/only mode.
-	 *                 False otherwise.
-	 * @return RrdFileBackend object which handles all I/O operations for the given file path
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected RrdBackend open(final String path, final boolean readOnly) throws IOException {
-	    return new RrdJRobin14FileBackend(path, readOnly, m_lockMode);
-	}
-
-	/**
-	 * Method to determine if a file with the given path already exists.
-	 *
-	 * @param path File path
-	 * @return True, if such file exists, false otherwise.
-	 */
-	protected boolean exists(final String path) {
-		return Util.fileExists(path);
-	}
-
-	/**
-	 * Returns the name of this factory.
-	 *
-	 * @return Factory name (equals to string "FILE")
-	 */
-	public String getFactoryName() {
-		return NAME;
-	}
-
-    public String toString() {
-        return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[name=" + getFactoryName() + ",lockMode=" + m_lockMode + "]";
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdLong.java b/apps/jrobin/java/src/org/jrobin/core/RrdLong.java
deleted file mode 100644
index 7b07b17303aa40e52b08354c60a7c3b3409d8351..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdLong.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-class RrdLong extends RrdPrimitive {
-	private long cache;
-	private boolean cached = false;
-
-	RrdLong(final RrdUpdater updater, final boolean isConstant) throws IOException {
-		super(updater, RrdPrimitive.RRD_LONG, isConstant);
-	}
-
-	RrdLong(final RrdUpdater updater) throws IOException {
-		this(updater, false);
-	}
-
-	void set(final long value) throws IOException {
-		if (!isCachingAllowed()) {
-			writeLong(value);
-		}
-		// caching allowed
-		else if (!cached || cache != value) {
-			// update cache
-			writeLong(cache = value);
-			cached = true;
-		}
-	}
-
-	long get() throws IOException {
-		return cached ? cache : readLong();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdMemoryBackend.java b/apps/jrobin/java/src/org/jrobin/core/RrdMemoryBackend.java
deleted file mode 100644
index 9387c00cded75de5cdb4f36b67d52fb8e48f61e1..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdMemoryBackend.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * Backend to be used to store all RRD bytes in memory.<p>
- */
-public class RrdMemoryBackend extends RrdBackend {
-	private static final ReadWriteLock m_readWritelock = new ReentrantReadWriteLock();
-	private static final Lock m_readLock = m_readWritelock.readLock();
-	private static final Lock m_writeLock = m_readWritelock.writeLock();
-
-	private byte[] buffer = new byte[0];
-
-	protected RrdMemoryBackend(String path) {
-		super(path);
-	}
-
-	protected void write(final long offset, final byte[] b) {
-		m_writeLock.lock();
-		try {
-		    int pos = (int) offset;
-			for (final byte singleByte : b) {
-				buffer[pos++] = singleByte;
-			}
-		} finally {
-			m_writeLock.unlock();
-		}
-	}
-
-	protected void read(final long offset, final byte[] b) throws IOException {
-		m_readLock.lock();
-		try {
-			int pos = (int) offset;
-			if (pos + b.length <= buffer.length) {
-				for (int i = 0; i < b.length; i++) {
-					b[i] = buffer[pos++];
-				}
-			}
-			else {
-				throw new IOException("Not enough bytes available in memory " + getPath());
-			}
-		} finally {
-			m_readLock.unlock();
-		}
-	}
-
-	/**
-	 * Returns the number of RRD bytes held in memory.
-	 *
-	 * @return Number of all RRD bytes.
-	 */
-	public long getLength() {
-		m_readLock.lock();
-		try {
-			return buffer.length;
-		} finally {
-			m_readLock.unlock();
-		}
-	}
-
-	/**
-	 * Reserves a memory section as a RRD storage.
-	 *
-	 * @param newLength Number of bytes held in memory.
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected void setLength(final long newLength) throws IOException {
-		m_writeLock.lock();
-		try {
-			if (newLength > Integer.MAX_VALUE) {
-				throw new IOException("Cannot create this big memory backed RRD");
-			}
-			buffer = new byte[(int) newLength];
-		} finally {
-			m_writeLock.unlock();
-		}
-	}
-
-	/**
-	 * This method is required by the base class definition, but it does not
-	 * releases any memory resources at all.
-	 */
-	public void close() {
-		// NOP
-	}
-
-	/**
-	 * This method is overridden to disable high-level caching in frontend JRobin classes.
-	 *
-	 * @return Always returns <code>false</code>. There is no need to cache anything in high-level classes
-	 *         since all RRD bytes are already in memory.
-	 */
-	protected boolean isCachingAllowed() {
-		return false;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdMemoryBackendFactory.java b/apps/jrobin/java/src/org/jrobin/core/RrdMemoryBackendFactory.java
deleted file mode 100644
index e326deecd51df8245c55f2384809ea25dfc142b7..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdMemoryBackendFactory.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.util.HashMap;
-
-/**
- * Factory class which creates actual {@link RrdMemoryBackend} objects. JRobin's support
- * for in-memory RRDs is still experimental. You should know that all active RrdMemoryBackend
- * objects are held in memory, each backend object stores RRD data in one big byte array. This
- * implementation is therefore quite basic and memory hungry but runs very fast.
- * <p>
- * Calling {@link RrdDb#close() close()} on RrdDb objects does not release any memory at all
- * (RRD data must be available for the next <code>new RrdDb(path)</code> call. To release allocated
- * memory, you'll have to call {@link #delete(String) delete(path)} method of this class.
- */
-public class RrdMemoryBackendFactory extends RrdBackendFactory {
-	/**
-	 * factory name, "MEMORY"
-	 */
-	public static final String NAME = "MEMORY";
-	private HashMap<String, RrdMemoryBackend> backends = new HashMap<String, RrdMemoryBackend>();
-
-	/**
-	 * Creates RrdMemoryBackend object.
-	 *
-	 * @param id	   Since this backend holds all data in memory, this argument is interpreted
-	 *                 as an ID for this memory-based storage.
-	 * @param readOnly This parameter is ignored
-	 * @return RrdMemoryBackend object which handles all I/O operations
-	 */
-	protected synchronized RrdBackend open(String id, boolean readOnly) {
-		RrdMemoryBackend backend;
-		if (backends.containsKey(id)) {
-			backend = backends.get(id);
-		}
-		else {
-			backend = new RrdMemoryBackend(id);
-			backends.put(id, backend);
-		}
-		return backend;
-	}
-
-	/**
-	 * Method to determine if a memory storage with the given ID already exists.
-	 *
-	 * @param id Memory storage ID.
-	 * @return True, if such storage exists, false otherwise.
-	 */
-	protected synchronized boolean exists(String id) {
-		return backends.containsKey(id);
-	}
-
-	/**
-	 * Removes the storage with the given ID from the memory.
-	 *
-	 * @param id Storage ID
-	 * @return True, if the storage with the given ID is deleted, false otherwise.
-	 */
-	public boolean delete(String id) {
-		if (backends.containsKey(id)) {
-			backends.remove(id);
-			return true;
-		}
-		return false;
-	}
-
-	/**
-	 * Returns the name of this factory.
-	 *
-	 * @return Factory name (equals to "MEMORY").
-	 */
-	public String getFactoryName() {
-		return NAME;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdNioBackend.java b/apps/jrobin/java/src/org/jrobin/core/RrdNioBackend.java
deleted file mode 100644
index 73213b36e7bc51afe38ead0ba3edafe61d18683f..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdNioBackend.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
-
-import net.i2p.util.SystemVersion;
-
-import engine.misc.DeallocationHelper;
-
-/**
- * JRobin backend which is used to store RRD data to ordinary disk files by
- * using fast java.nio.* package. This is the default backend engine since
- * JRobin 1.4.0.
- */
-@SuppressWarnings("restriction")
-public class RrdNioBackend extends RrdFileBackend {
-    private final SyncManager m_syncManager;
-    private MappedByteBuffer m_byteBuffer = null;
-    // Too many ominous warnings from Java 9
-    private static final DeallocationHelper _dHelper = SystemVersion.isJava9() ? null : new DeallocationHelper();
-
-    /**
-     * Creates RrdFileBackend object for the given file path, backed by
-     * java.nio.* classes.  This constructor will create a
-     * {@link SyncManager} for each instance, which is very inefficient.
-     * It is recommended that you instead use the
-     * {@link #RrdNioBackend(String, boolean, SyncManager)}
-     * constructor instead.
-     * 
-     * @param path
-     *            Path to a JRB file.
-     * @param readOnly
-     *            True, if file should be open in a read-only mode. False
-     *            otherwise
-     * @param syncPeriod
-     *            How often (in seconds) to sync MMAP'd RRD data to disk
-     * @throws IOException
-     *             Thrown in case of I/O error
-     */
-    protected RrdNioBackend(final String path, final boolean readOnly, final int syncPeriod) throws IOException {
-        this(path, readOnly, new SyncManager(syncPeriod));
-    }
-
-    /**
-     * Creates RrdFileBackend object for the given file path, backed by
-     * java.nio.* classes.
-     * 
-     * @param path
-     *            Path to a file
-     * @param readOnly
-     *            True, if file should be open in a read-only mode. False
-     *            otherwise.
-     * @param syncManager
-     *            An object for managing synchronization of NIO-backed RRDs,
-     *            generally owned by the backend factory.  If null, MMAP'd
-     *            data will only be synchronized to disk upon unmap.  Note
-     *            that if the file is opened read-only, the SyncManager is
-     *            ignored. {@link RrdNioBackend#unmapFile() unmapFile()}
-     * @throws IOException
-     *             Thrown in case of I/O error
-     */
-    protected RrdNioBackend(final String path, final boolean readOnly, final SyncManager syncManager) throws IOException {
-        super(path, readOnly);
-        m_syncManager = syncManager;
-
-        try {
-            mapFile();
-        } catch (final IOException ioe) {
-            stopSchedule();
-            super.close();
-            throw ioe;
-        }
-    }
-
-    private void mapFile() throws IOException {
-        if (!isReadOnly()) {
-            startSchedule();
-        }
-        final long length = getLength();
-        if (length > 0) {
-            final FileChannel.MapMode mapMode = isReadOnly() ? FileChannel.MapMode.READ_ONLY : FileChannel.MapMode.READ_WRITE;
-            m_byteBuffer = file.getChannel().map(mapMode, 0, length);
-        }
-    }
-
-    private void unmapFile() {
-        if (!isReadOnly()) {
-            stopSchedule();
-        }
-        if (_dHelper != null && m_byteBuffer != null) {
-            _dHelper.deallocate(m_byteBuffer);
-            m_byteBuffer = null;
-        }
-    }
-
-    private void startSchedule() {
-        if (m_syncManager != null) {
-            m_syncManager.add(this);
-        }
-    }
-
-    private synchronized void stopSchedule() {
-        if (m_syncManager != null) {
-            m_syncManager.remove(this);
-        }
-        sync();
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        stopSchedule();
-        super.finalize();
-    }
-
-    /**
-     * Sets length of the underlying RRD file. This method is called only
-     * once, immediately after a new RRD file gets created.
-     * 
-     * @param newLength
-     *            Length of the RRD file
-     * @throws IOException
-     *             Thrown in case of I/O error.
-     */
-    protected synchronized void setLength(final long newLength) throws IOException {
-        unmapFile();
-        super.setLength(newLength);
-        mapFile();
-    }
-
-    /**
-     * Writes bytes to the underlying RRD file on the disk
-     * 
-     * @param offset
-     *            Starting file offset
-     * @param b
-     *            Bytes to be written.
-     */
-    protected synchronized void write(final long offset, final byte[] b) throws IOException {
-        if (m_byteBuffer != null) {
-            m_byteBuffer.position((int) offset);
-            m_byteBuffer.put(b);
-        } else {
-            throw new IOException("Write failed, file " + getPath() + " not mapped for I/O");
-        }
-    }
-
-    /**
-     * Reads a number of bytes from the RRD file on the disk
-     * 
-     * @param offset
-     *            Starting file offset
-     * @param b
-     *            Buffer which receives bytes read from the file.
-     */
-    protected synchronized void read(final long offset, final byte[] b) throws IOException {
-        if (m_byteBuffer != null) {
-            m_byteBuffer.position((int) offset);
-            m_byteBuffer.get(b);
-        } else {
-            throw new IOException("Read failed, file " + getPath() + " not mapped for I/O");
-        }
-    }
-
-    /**
-     * Closes the underlying RRD file.
-     * 
-     * @throws IOException
-     *             Thrown in case of I/O error
-     */
-    public synchronized void close() throws IOException {
-        // cancel synchronization
-        try {
-            unmapFile();
-        } finally {
-            super.close();
-        }
-    }
-
-    /**
-     * This method forces all data cached in memory but not yet stored in the
-     * file, to be stored in it.
-     */
-    protected synchronized void sync() {
-        if (m_byteBuffer != null) {
-            m_byteBuffer.force();
-        }
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdNioBackendFactory.java b/apps/jrobin/java/src/org/jrobin/core/RrdNioBackendFactory.java
deleted file mode 100644
index 2855cf37463c4be518b1c8e7b97f0aa05d1c1316..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdNioBackendFactory.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Factory class which creates actual {@link RrdNioBackend} objects. This is
- * the default factory since 1.4.0 version
- */
-public class RrdNioBackendFactory extends RrdFileBackendFactory {
-    /**
-     * Period in seconds between consecutive synchronizations when sync-mode
-     * is set to SYNC_BACKGROUND. By default in-memory cache will be
-     * transferred to the disc every 300 seconds (5 minutes). Default value
-     * can be changed via {@link #setSyncPeriod(int)} method.
-     */
-    public static final int DEFAULT_SYNC_PERIOD = 300; // seconds
-
-    private static SyncManager s_syncManager = new SyncManager(DEFAULT_SYNC_PERIOD);
-
-    /**
-     * factory name, "NIO"
-     */
-    public static final String NAME = "NIO";
-
-    /**
-     * Returns time between two consecutive background synchronizations. If
-     * not changed via {@link #setSyncPeriod(int)} method call, defaults to
-     * {@link #DEFAULT_SYNC_PERIOD}. See {@link #setSyncPeriod(int)} for more
-     * information.
-     * 
-     * @return Time in seconds between consecutive background
-     *         synchronizations.
-     */
-    public static int getSyncPeriod() {
-        return s_syncManager.getSyncPeriod();
-    }
-
-    /**
-     * Sets time between consecutive background synchronizations.
-     * 
-     * @param syncPeriod
-     *            Time in seconds between consecutive background
-     *            synchronizations.
-     */
-    public synchronized static void setSyncPeriod(final int syncPeriod) {
-        s_syncManager.setSyncPeriod(syncPeriod);
-    }
-
-    /**
-     * Creates RrdNioBackend object for the given file path.
-     * 
-     * @param path
-     *            File path
-     * @param readOnly
-     *            True, if the file should be accessed in read/only mode.
-     *            False otherwise.
-     * @return RrdNioBackend object which handles all I/O operations for the
-     *         given file path
-     * @throws IOException
-     *             Thrown in case of I/O error.
-     */
-    protected RrdBackend open(final String path, final boolean readOnly) throws IOException {
-        return new RrdNioBackend(path, readOnly, s_syncManager);
-    }
-
-    public void shutdown() {
-        s_syncManager.shutdown();
-    }
-
-    /**
-     * Returns the name of this factory.
-     * 
-     * @return Factory name (equals to string "NIO")
-     */
-    public String getFactoryName() {
-        return NAME;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        shutdown();
-        super.finalize();
-    }
-
-    SyncManager getSyncManager() {
-        return s_syncManager;
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdNioByteBufferBackend.java b/apps/jrobin/java/src/org/jrobin/core/RrdNioByteBufferBackend.java
deleted file mode 100644
index b9dd5fca78bb757387d7bf3403415d7b27351edb..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdNioByteBufferBackend.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * JRobin backend which is used to store RRD data to ordinary disk files
- * by using fast java.nio.* package. This is the default backend engine since JRobin 1.4.0.
- */
-public class RrdNioByteBufferBackend extends RrdFileBackend {
-
-	private ByteBuffer m_byteBuffer;
-
-	private FileChannel m_ch;
-
-	private static final ReadWriteLock m_readWritelock = new ReentrantReadWriteLock();
-	private static final Lock m_readLock = m_readWritelock.readLock();
-	private static final Lock m_writeLock = m_readWritelock.writeLock();
-
-	/**
-	 * Creates RrdFileBackend object for the given file path, backed by java.nio.* classes.
-	 *
-	 * @param path	   Path to a file
-	 * @param readOnly   True, if file should be open in a read-only mode. False otherwise
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	protected RrdNioByteBufferBackend(final String path, final boolean readOnly) throws IOException, IllegalStateException {
-		super(path, readOnly);
-
-		if (file != null) {
-			m_ch = file.getChannel();
-			m_byteBuffer = ByteBuffer.allocate((int) m_ch.size());
-			m_ch.read(m_byteBuffer, 0);
-		} else {
-			throw new IllegalStateException("File in base class is null.");
-		}
-	}
-
-	/**
-	 * Sets length of the underlying RRD file. This method is called only once, immediately
-	 * after a new RRD file gets created.
-	 *
-	 * @param newLength Length of the RRD file
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	@Override
-	protected void setLength(final long newLength) throws IOException {
-	    m_writeLock.lock();
-	    try {
-			super.setLength(newLength);
-			m_ch = file.getChannel();
-			m_byteBuffer = ByteBuffer.allocate((int) newLength);
-			m_ch.read(m_byteBuffer, 0);
-			m_byteBuffer.position(0);
-		} finally {
-		    m_writeLock.unlock();
-		}
-	}
-
-	/**
-	 * Writes bytes to the underlying RRD file on the disk
-	 *
-	 * @param offset Starting file offset
-	 * @param b	  Bytes to be written.
-	 */
-	@Override
-	protected void write(final long offset, final byte[] b) {
-	    m_writeLock.lock();
-	    try {
-            m_byteBuffer.position((int) offset);
-            m_byteBuffer.put(b);
-	    } finally {
-	        m_writeLock.unlock();
-	    }
-	}
-
-	/**
-	 * Reads a number of bytes from the RRD file on the disk
-	 *
-	 * @param offset Starting file offset
-	 * @param b	  Buffer which receives bytes read from the file.
-	 */
-	@Override
-	protected void read(final long offset, final byte[] b) {
-	    m_readLock.lock();
-	    try {
-            m_byteBuffer.position((int) offset);
-            m_byteBuffer.get(b);
-	    } finally {
-	        m_readLock.unlock();
-	    }
-	}
-
-	/**
-	 * Closes the underlying RRD file.
-	 *
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public void close() throws IOException {
-	    m_writeLock.lock();
-	    try {
-			m_byteBuffer.position(0);
-
-			if (!isReadOnly()) m_ch.write(m_byteBuffer, 0);
-			//just calling close here because the super calls close
-			//on the File object and Java calls close on the channel
-			super.close();
-		} finally {
-		    m_writeLock.unlock();
-		}
-	}
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdNioByteBufferBackendFactory.java b/apps/jrobin/java/src/org/jrobin/core/RrdNioByteBufferBackendFactory.java
deleted file mode 100644
index 9f63fdfcaf265f7ac354cbcf77bdf35553cc2fed..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdNioByteBufferBackendFactory.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Factory class which creates actual {@link RrdNioBackend} objects.
- */
-public class RrdNioByteBufferBackendFactory extends RrdFileBackendFactory {
-
-	public static final String NAME = "MNIO";
-
-	/**
-	 * Creates RrdNioByteBufferBackend object for the given file path.
-	 *
-	 * @param path	 File path
-	 * @param readOnly True, if the file should be accessed in read/only mode.
-	 *                 False otherwise.
-	 * @return RrdNioBackend object which handles all I/O operations for the given file path
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	@Override
-	protected RrdBackend open(String path, boolean readOnly) throws IOException {
-		return new RrdNioByteBufferBackend(path, readOnly);
-	}
-
-	/**
-	 * Returns the name of this factory.
-	 *
-	 * @return Factory name (equals to string "NIOBB")
-	 */
-	@Override
-	public String getFactoryName() {
-		return NAME;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdPrimitive.java b/apps/jrobin/java/src/org/jrobin/core/RrdPrimitive.java
deleted file mode 100644
index 714804010976456d314e60ef088d9af0e9c81c38..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdPrimitive.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-abstract class RrdPrimitive {
-	static final int STRING_LENGTH = 20;
-	static final int RRD_INT = 0, RRD_LONG = 1, RRD_DOUBLE = 2, RRD_STRING = 3;
-	static final int[] RRD_PRIM_SIZES = {4, 8, 8, 2 * STRING_LENGTH};
-
-	private RrdBackend backend;
-	private int byteCount;
-	private final long pointer;
-	private final boolean cachingAllowed;
-
-	RrdPrimitive(final RrdUpdater updater, final int type, final boolean isConstant) throws IOException {
-		this(updater, type, 1, isConstant);
-	}
-
-	RrdPrimitive(final RrdUpdater updater, final int type, final int count, final boolean isConstant) throws IOException {
-		this.backend = updater.getRrdBackend();
-		this.byteCount = RRD_PRIM_SIZES[type] * count;
-		this.pointer = updater.getRrdAllocator().allocate(byteCount);
-		this.cachingAllowed = isConstant || backend.isCachingAllowed();
-	}
-
-	final byte[] readBytes() throws IOException {
-	    final byte[] b = new byte[byteCount];
-		backend.read(pointer, b);
-		return b;
-	}
-
-	final void writeBytes(final byte[] b) throws IOException {
-		assert b.length == byteCount: "Invalid number of bytes supplied to RrdPrimitive.write method";
-		backend.write(pointer, b);
-	}
-
-	final int readInt() throws IOException {
-		return backend.readInt(pointer);
-	}
-
-	final void writeInt(final int value) throws IOException {
-		backend.writeInt(pointer, value);
-	}
-
-	final long readLong() throws IOException {
-		return backend.readLong(pointer);
-	}
-
-	final void writeLong(final long value) throws IOException {
-		backend.writeLong(pointer, value);
-	}
-
-	final double readDouble() throws IOException {
-		return backend.readDouble(pointer);
-	}
-
-	final double readDouble(final int index) throws IOException {
-	    final long offset = pointer + ((long)index * (long)RRD_PRIM_SIZES[RRD_DOUBLE]);
-		return backend.readDouble(offset);
-	}
-
-	final double[] readDouble(final int index, final int count) throws IOException {
-	    final long offset = pointer + ((long)index * (long)RRD_PRIM_SIZES[RRD_DOUBLE]);
-		return backend.readDouble(offset, count);
-	}
-
-	final void writeDouble(final double value) throws IOException {
-		backend.writeDouble(pointer, value);
-	}
-
-	final void writeDouble(final int index, final double value, final int count) throws IOException {
-	    final long offset = pointer + ((long)index * (long)RRD_PRIM_SIZES[RRD_DOUBLE]);
-		backend.writeDouble(offset, value, count);
-	}
-
-	final void writeDouble(final int index, final double[] values) throws IOException {
-	    final long offset = pointer + ((long)index * (long)RRD_PRIM_SIZES[RRD_DOUBLE]);
-		backend.writeDouble(offset, values);
-	}
-
-	final String readString() throws IOException {
-		return backend.readString(pointer);
-	}
-
-	final void writeString(final String value) throws IOException {
-		backend.writeString(pointer, value);
-	}
-
-	final boolean isCachingAllowed() {
-		return cachingAllowed;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdSafeFileBackend.java b/apps/jrobin/java/src/org/jrobin/core/RrdSafeFileBackend.java
deleted file mode 100644
index 3be8bcb006a046a59f69403151d2ecae904f8dd4..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdSafeFileBackend.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.nio.channels.FileLock;
-import java.nio.channels.FileChannel;
-
-/**
- * JRobin backend which is used to store RRD data to ordinary files on the disk. This backend
- * is SAFE: it locks the underlying RRD file during update/fetch operations, and caches only static
- * parts of a RRD file in memory. Therefore, this backend is safe to be used when RRD files should
- * be shared between several JVMs at the same time. However, this backend is a little bit slow
- * since it does not use fast java.nio.* package (it's still based on the RandomAccessFile class).
- */
-public class RrdSafeFileBackend extends RrdFileBackend {
-	private static final Counters counters = new Counters();
-
-	private FileLock m_lock;
-
-	/**
-	 * Creates RrdFileBackend object for the given file path, backed by RandomAccessFile object.
-	 *
-	 * @param path Path to a file
-	 * @param lockWaitTime lock waiting time in milliseconds
-	 * @param lockRetryPeriod lock retry period in milliseconds
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public RrdSafeFileBackend(final String path, final long lockWaitTime, final long lockRetryPeriod) throws IOException {
-		super(path, false);
-		try {
-		    lockFile(lockWaitTime, lockRetryPeriod);
-		}
-		catch (final IOException ioe) {
-			super.close();
-			throw ioe;
-		}
-	}
-
-	private void lockFile(final long lockWaitTime, final long lockRetryPeriod) throws IOException {
-	    final long entryTime = System.currentTimeMillis();
-	    final FileChannel channel = file.getChannel();
-		m_lock = channel.tryLock(0, Long.MAX_VALUE, false);
-		if (m_lock != null) {
-			counters.registerQuickLock();
-			return;
-		}
-		do {
-			try {
-				Thread.sleep(lockRetryPeriod);
-			}
-			catch (final InterruptedException e) {
-			    Thread.currentThread().interrupt();
-			}
-			m_lock = channel.tryLock(0, Long.MAX_VALUE, false);
-			if (m_lock != null) {
-				counters.registerDelayedLock();
-				return;
-			}
-		} while (System.currentTimeMillis() - entryTime <= lockWaitTime);
-		counters.registerError();
-		throw new IOException("Could not obtain exclusive m_lock on file: " + getPath() + "] after " + lockWaitTime + " milliseconds");
-	}
-
-	public void close() throws IOException {
-		try {
-			if (m_lock != null) {
-				m_lock.release();
-				m_lock = null;
-				counters.registerUnlock();
-			}
-		}
-		finally {
-			super.close();
-		}
-	}
-
-	/**
-	 * Defines the caching policy for this backend.
-	 *
-	 * @return <code>false</code>
-	 */
-	protected boolean isCachingAllowed() {
-		return false;
-	}
-
-	public static String getLockInfo() {
-		return counters.getInfo();
-	}
-
-	static class Counters {
-		long locks, quickLocks, unlocks, locked, errors;
-
-		synchronized void registerQuickLock() {
-			locks++;
-			quickLocks++;
-			locked++;
-		}
-
-		synchronized void registerDelayedLock() {
-			locks++;
-			locked++;
-		}
-
-		synchronized void registerUnlock() {
-			unlocks++;
-			locked--;
-		}
-
-		synchronized void registerError() {
-			errors++;
-		}
-
-		synchronized String getInfo() {
-			return "LOCKS=" + locks + ", " + "UNLOCKS=" + unlocks + ", " +
-					"DELAYED_LOCKS=" + (locks - quickLocks) + ", " + "LOCKED=" + locked + ", " +
-					"ERRORS=" + errors;
-		}
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdSafeFileBackendFactory.java b/apps/jrobin/java/src/org/jrobin/core/RrdSafeFileBackendFactory.java
deleted file mode 100644
index ae6fb4e9c4a322fceeed6148b704bd2fd25143a4..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdSafeFileBackendFactory.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core;
-
-import java.io.IOException;
-
-/**
- * Factory class which creates actual {@link RrdSafeFileBackend} objects.
- */
-public class RrdSafeFileBackendFactory extends RrdFileBackendFactory {
-	/**
-	 * Default time (in milliseconds) this backend will wait for a file lock.
-	 */
-	public static final long LOCK_WAIT_TIME = 3000L;
-	private static long lockWaitTime = LOCK_WAIT_TIME;
-
-	/**
-	 * Default time between two consecutive file locking attempts.
-	 */
-	public static final long LOCK_RETRY_PERIOD = 50L;
-	private static long lockRetryPeriod = LOCK_RETRY_PERIOD;
-
-	/**
-	 * factory name, "SAFE"
-	 */
-	public static final String NAME = "SAFE";
-
-	/**
-	 * Creates RrdSafeFileBackend object for the given file path.
-	 *
-	 * @param path	 File path
-	 * @param readOnly This parameter is ignored
-	 * @return RrdSafeFileBackend object which handles all I/O operations for the given file path
-	 * @throws IOException Thrown in case of I/O error.
-	 */
-	protected RrdBackend open(String path, boolean readOnly) throws IOException {
-		return new RrdSafeFileBackend(path, lockWaitTime, lockRetryPeriod);
-	}
-
-	/**
-	 * Returns the name of this factory.
-	 *
-	 * @return Factory name (equals to string "SAFE")
-	 */
-	public String getFactoryName() {
-		return NAME;
-	}
-
-	/**
-	 * Returns time this backend will wait for a file lock.
-	 *
-	 * @return Time (in milliseconds) this backend will wait for a file lock.
-	 */
-	public static long getLockWaitTime() {
-		return lockWaitTime;
-	}
-
-	/**
-	 * Sets time this backend will wait for a file lock.
-	 *
-	 * @param lockWaitTime Maximum lock wait time (in milliseconds)
-	 */
-	public static void setLockWaitTime(long lockWaitTime) {
-		RrdSafeFileBackendFactory.lockWaitTime = lockWaitTime;
-	}
-
-	/**
-	 * Returns time between two consecutive file locking attempts.
-	 *
-	 * @return Time (im milliseconds) between two consecutive file locking attempts.
-	 */
-	public static long getLockRetryPeriod() {
-		return lockRetryPeriod;
-	}
-
-	/**
-	 * Sets time between two consecutive file locking attempts.
-	 *
-	 * @param lockRetryPeriod time (in milliseconds) between two consecutive file locking attempts.
-	 */
-	public static void setLockRetryPeriod(long lockRetryPeriod) {
-		RrdSafeFileBackendFactory.lockRetryPeriod = lockRetryPeriod;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdString.java b/apps/jrobin/java/src/org/jrobin/core/RrdString.java
deleted file mode 100644
index 14be148e60c6689eae718d7e76fa9090c947f3a5..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdString.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-class RrdString extends RrdPrimitive {
-	private String cache;
-
-	RrdString(final RrdUpdater updater, final boolean isConstant) throws IOException {
-		super(updater, RrdPrimitive.RRD_STRING, isConstant);
-	}
-
-	RrdString(final RrdUpdater updater) throws IOException {
-		this(updater, false);
-	}
-
-	void set(final String value) throws IOException {
-		if (!isCachingAllowed()) {
-			writeString(value);
-		}
-		// caching allowed
-		else if (cache == null || !cache.equals(value)) {
-			// update cache
-			writeString(cache = value);
-		}
-	}
-
-	String get() throws IOException {
-		return (cache != null) ? cache : readString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdToolReader.java b/apps/jrobin/java/src/org/jrobin/core/RrdToolReader.java
deleted file mode 100644
index 97f9c121eed99512e5cc411d766164b60ee0fd3c..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdToolReader.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core;
-
-import java.io.IOException;
-
-import org.jrobin.core.jrrd.RRDatabase;
-
-class RrdToolReader extends DataImporter {
-	private RRDatabase rrd;
-
-	RrdToolReader(String rrdPath) throws IOException,RrdException {
-		rrd = new RRDatabase(rrdPath);
-	}
-
-	String getVersion() {
-		return rrd.getHeader().getVersion();
-	}
-
-	long getLastUpdateTime() {
-		return Util.getTimestamp(rrd.getLastUpdate());
-	}
-
-	long getStep() {
-		return rrd.getHeader().getPDPStep();
-	}
-
-	int getDsCount() {
-		return rrd.getHeader().getDSCount();
-	}
-
-	int getArcCount() throws RrdException, IOException {
-		return rrd.getNumArchives();
-	}
-
-	String getDsName(int dsIndex) {
-		return rrd.getDataSource(dsIndex).getName();
-	}
-
-	String getDsType(int dsIndex) {
-		return rrd.getDataSource(dsIndex).getType().toString();
-	}
-
-	long getHeartbeat(int dsIndex) {
-		return rrd.getDataSource(dsIndex).getMinimumHeartbeat();
-	}
-
-	double getMinValue(int dsIndex) {
-		return rrd.getDataSource(dsIndex).getMinimum();
-	}
-
-	double getMaxValue(int dsIndex) {
-		return rrd.getDataSource(dsIndex).getMaximum();
-	}
-
-	double getLastValue(int dsIndex) {
-		String valueStr = rrd.getDataSource(dsIndex).getPDPStatusBlock().getLastReading();
-		return Util.parseDouble(valueStr);
-	}
-
-	double getAccumValue(int dsIndex) {
-		return rrd.getDataSource(dsIndex).getPDPStatusBlock().getValue();
-	}
-
-	long getNanSeconds(int dsIndex) {
-		return rrd.getDataSource(dsIndex).getPDPStatusBlock().getUnknownSeconds();
-	}
-
-	String getConsolFun(int arcIndex) {
-		return rrd.getArchive(arcIndex).getType().toString();
-	}
-
-	double getXff(int arcIndex) {
-		return rrd.getArchive(arcIndex).getXff();
-	}
-
-	int getSteps(int arcIndex) {
-		return rrd.getArchive(arcIndex).getPdpCount();
-	}
-
-	int getRows(int arcIndex) throws RrdException, IOException {
-		return rrd.getArchive(arcIndex).getRowCount();
-	}
-
-	double getStateAccumValue(int arcIndex, int dsIndex) throws RrdException, IOException {
-		return rrd.getArchive(arcIndex).getCDPStatusBlock(dsIndex).getValue();
-	}
-
-	int getStateNanSteps(int arcIndex, int dsIndex) throws RrdException, IOException {
-		return rrd.getArchive(arcIndex).getCDPStatusBlock(dsIndex).getUnknownDatapoints();
-	}
-
-	double[] getValues(int arcIndex, int dsIndex) throws RrdException, IOException,RrdException {
-		return rrd.getArchive(arcIndex).getValues()[dsIndex];
-	}
-
-	void release() throws IOException {
-		if (rrd != null) {
-			rrd.close();
-			rrd = null;
-		}
-	}
-
-	protected void finalize() throws Throwable {
-		super.finalize();
-		release();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdToolkit.java b/apps/jrobin/java/src/org/jrobin/core/RrdToolkit.java
deleted file mode 100644
index 6e68348d62cb91d9d90039addadd5e3487ba2eff..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdToolkit.java
+++ /dev/null
@@ -1,658 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.LinkedList;
-import java.util.Arrays;
-
-/**
- * Class used to perform various complex operations on RRD files. Use an instance of the
- * RrdToolkit class to:
- * <p>
- * <ul>
- * <li>add datasource to a RRD file.
- * <li>add archive to a RRD file.
- * <li>remove datasource from a RRD file.
- * <li>remove archive from a RRD file.
- * </ul>
- * All these operations can be performed on the copy of the original RRD file, or on the
- * original file itself (with possible backup file creation).
- * <p>
- * <b><u>IMPORTANT</u></b>: NEVER use methods found in this class on 'live' RRD files
- * (files which are currently in use).
- */
-public class RrdToolkit {
-	/**
-	 * Creates a new RRD file with one more datasource in it. RRD file is created based on the
-	 * existing one (the original RRD file is not modified at all). All data from
-	 * the original RRD file is copied to the new one.
-	 *
-	 * @param sourcePath	path to a RRD file to import data from (will not be modified)
-	 * @param destPath	  path to a new RRD file (will be created)
-	 * @param newDatasource Datasource definition to be added to the new RRD file
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void addDatasource(String sourcePath, String destPath, DsDef newDatasource)
-			throws IOException, RrdException {
-		if (Util.sameFilePath(sourcePath, destPath)) {
-			throw new RrdException("Source and destination paths are the same");
-		}
-		RrdDb rrdSource = new RrdDb(sourcePath);
-		try {
-			RrdDef rrdDef = rrdSource.getRrdDef();
-			rrdDef.setPath(destPath);
-			rrdDef.addDatasource(newDatasource);
-			RrdDb rrdDest = new RrdDb(rrdDef);
-			try {
-				rrdSource.copyStateTo(rrdDest);
-			}
-			finally {
-				rrdDest.close();
-			}
-		}
-		finally {
-			rrdSource.close();
-		}
-	}
-
-	/**
-	 * <p>Adds one more datasource to a RRD file.</p>
-	 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
-	 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
-	 * should be set to <code>true</code>). The backup file will be created in the same
-	 * directory as the original one with <code>.bak</code> extension added to the
-	 * original name.</p>
-	 * <p>Before applying this method, be sure that the specified RRD file is not in use
-	 * (not open)</p>
-	 *
-	 * @param sourcePath	path to a RRD file to add datasource to.
-	 * @param newDatasource Datasource definition to be added to the RRD file
-	 * @param saveBackup	true, if backup of the original file should be created;
-	 *                      false, otherwise
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void addDatasource(String sourcePath, DsDef newDatasource, boolean saveBackup)
-			throws IOException, RrdException {
-		String destPath = Util.getTmpFilename();
-		addDatasource(sourcePath, destPath, newDatasource);
-		copyFile(destPath, sourcePath, saveBackup);
-	}
-
-	/**
-	 * Creates a new RRD file with one datasource removed. RRD file is created based on the
-	 * existing one (the original RRD file is not modified at all). All remaining data from
-	 * the original RRD file is copied to the new one.
-	 *
-	 * @param sourcePath path to a RRD file to import data from (will not be modified)
-	 * @param destPath   path to a new RRD file (will be created)
-	 * @param dsName	 Name of the Datasource to be removed from the new RRD file
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void removeDatasource(String sourcePath, String destPath, String dsName)
-			throws IOException, RrdException {
-		if (Util.sameFilePath(sourcePath, destPath)) {
-			throw new RrdException("Source and destination paths are the same");
-		}
-		RrdDb rrdSource = new RrdDb(sourcePath);
-		try {
-			RrdDef rrdDef = rrdSource.getRrdDef();
-			rrdDef.setPath(destPath);
-			rrdDef.removeDatasource(dsName);
-			RrdDb rrdDest = new RrdDb(rrdDef);
-			try {
-				rrdSource.copyStateTo(rrdDest);
-			}
-			finally {
-				rrdDest.close();
-			}
-		}
-		finally {
-			rrdSource.close();
-		}
-	}
-
-	/**
-	 * <p>Removes single datasource from a RRD file.</p>
-	 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
-	 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
-	 * should be set to <code>true</code>). The backup file will be created in the same
-	 * directory as the original one with <code>.bak</code> extension added to the
-	 * original name.</p>
-	 * <p>Before applying this method, be sure that the specified RRD file is not in use
-	 * (not open)</p>
-	 *
-	 * @param sourcePath path to a RRD file to remove datasource from.
-	 * @param dsName	 Name of the Datasource to be removed from the RRD file
-	 * @param saveBackup true, if backup of the original file should be created;
-	 *                   false, otherwise
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void removeDatasource(String sourcePath, String dsName, boolean saveBackup)
-			throws IOException, RrdException {
-		String destPath = Util.getTmpFilename();
-		removeDatasource(sourcePath, destPath, dsName);
-		copyFile(destPath, sourcePath, saveBackup);
-	}
-
-	/**
-	 * Renames single datasource in the given RRD file.
-	 *
-	 * @param sourcePath Path to a RRD file
-	 * @param oldDsName  Old datasource name
-	 * @param newDsName  New datasource name
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error (invalid path or datasource names,
-	 *                      for example)
-	 */
-	public static void renameDatasource(String sourcePath, String oldDsName, String newDsName)
-			throws IOException, RrdException {
-		RrdDb rrd = new RrdDb(sourcePath);
-		try {
-			if (rrd.containsDs(oldDsName)) {
-				Datasource datasource = rrd.getDatasource(oldDsName);
-				datasource.setDsName(newDsName);
-			}
-			else {
-				throw new RrdException("Could not find datasource [" + oldDsName + "] in file " + sourcePath);
-			}
-		}
-		finally {
-			rrd.close();
-		}
-	}
-
-	/**
-	 * Updates single or all datasource names in the specified RRD file
-	 * by appending '!' (if not already present). Datasources with names ending with '!'
-	 * will never store NaNs in RRA archives (zero value will be used instead). Might be useful
-	 * from time to time
-	 *
-	 * @param sourcePath Path to a RRD file
-	 * @param dsName	 Datasource name or null if you want to rename all datasources
-	 * @return Number of datasources successfully renamed
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error (invalid path or datasource name,
-	 *                      for example)
-	 */
-	public static int forceZerosForNans(String sourcePath, String dsName) throws IOException, RrdException {
-		RrdDb rrd = new RrdDb(sourcePath);
-		try {
-			Datasource[] datasources;
-			if (dsName == null) {
-				datasources = rrd.getDatasources();
-			}
-			else {
-				if (rrd.containsDs(dsName)) {
-					datasources = new Datasource[] {rrd.getDatasource(dsName)};
-				}
-				else {
-					throw new RrdException("Could not find datasource [" + dsName + "] in file " + sourcePath);
-				}
-			}
-			int count = 0;
-			for (Datasource datasource : datasources) {
-				String currentDsName = datasource.getDsName();
-				if (!currentDsName.endsWith(DsDef.FORCE_ZEROS_FOR_NANS_SUFFIX)) {
-					datasource.setDsName(currentDsName + DsDef.FORCE_ZEROS_FOR_NANS_SUFFIX);
-					count++;
-				}
-			}
-			return count;
-		}
-		finally {
-			rrd.close();
-		}
-	}
-
-	/**
-	 * Creates a new RRD file with one more archive in it. RRD file is created based on the
-	 * existing one (the original RRD file is not modified at all). All data from
-	 * the original RRD file is copied to the new one.
-	 *
-	 * @param sourcePath path to a RRD file to import data from (will not be modified)
-	 * @param destPath   path to a new RRD file (will be created)
-	 * @param newArchive Archive definition to be added to the new RRD file
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void addArchive(String sourcePath, String destPath, ArcDef newArchive)
-			throws IOException, RrdException {
-		if (Util.sameFilePath(sourcePath, destPath)) {
-			throw new RrdException("Source and destination paths are the same");
-		}
-		RrdDb rrdSource = new RrdDb(sourcePath);
-		try {
-			RrdDef rrdDef = rrdSource.getRrdDef();
-			rrdDef.setPath(destPath);
-			rrdDef.addArchive(newArchive);
-			RrdDb rrdDest = new RrdDb(rrdDef);
-			try {
-				rrdSource.copyStateTo(rrdDest);
-			}
-			finally {
-				rrdDest.close();
-			}
-		}
-		finally {
-			rrdSource.close();
-		}
-	}
-
-	/**
-	 * <p>Adds one more archive to a RRD file.</p>
-	 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
-	 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
-	 * should be set to <code>true</code>). The backup file will be created in the same
-	 * directory as the original one with <code>.bak</code> extension added to the
-	 * original name.</p>
-	 * <p>Before applying this method, be sure that the specified RRD file is not in use
-	 * (not open)</p>
-	 *
-	 * @param sourcePath path to a RRD file to add datasource to.
-	 * @param newArchive Archive definition to be added to the RRD file
-	 * @param saveBackup true, if backup of the original file should be created;
-	 *                   false, otherwise
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void addArchive(String sourcePath, ArcDef newArchive, boolean saveBackup)
-			throws IOException, RrdException {
-		String destPath = Util.getTmpFilename();
-		addArchive(sourcePath, destPath, newArchive);
-		copyFile(destPath, sourcePath, saveBackup);
-	}
-
-	/**
-	 * Creates a new RRD file with one archive removed. RRD file is created based on the
-	 * existing one (the original RRD file is not modified at all). All relevant data from
-	 * the original RRD file is copied to the new one.
-	 *
-	 * @param sourcePath path to a RRD file to import data from (will not be modified)
-	 * @param destPath   path to a new RRD file (will be created)
-	 * @param consolFun  Consolidation function of Archive which should be removed
-	 * @param steps	  Number of steps for Archive which should be removed
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void removeArchive(String sourcePath, String destPath, String consolFun, int steps)
-			throws IOException, RrdException {
-		if (Util.sameFilePath(sourcePath, destPath)) {
-			throw new RrdException("Source and destination paths are the same");
-		}
-		RrdDb rrdSource = new RrdDb(sourcePath);
-		try {
-			RrdDef rrdDef = rrdSource.getRrdDef();
-			rrdDef.setPath(destPath);
-			rrdDef.removeArchive(consolFun, steps);
-			RrdDb rrdDest = new RrdDb(rrdDef);
-			try {
-				rrdSource.copyStateTo(rrdDest);
-			}
-			finally {
-				rrdDest.close();
-			}
-		}
-		finally {
-			rrdSource.close();
-		}
-	}
-
-	/**
-	 * <p>Removes one archive from a RRD file.</p>
-	 * <p>WARNING: This method is potentialy dangerous! It will modify your RRD file.
-	 * It is highly recommended to preserve the original RRD file (<i>saveBackup</i>
-	 * should be set to <code>true</code>). The backup file will be created in the same
-	 * directory as the original one with <code>.bak</code> extension added to the
-	 * original name.</p>
-	 * <p>Before applying this method, be sure that the specified RRD file is not in use
-	 * (not open)</p>
-	 *
-	 * @param sourcePath path to a RRD file to add datasource to.
-	 * @param consolFun  Consolidation function of Archive which should be removed
-	 * @param steps	  Number of steps for Archive which should be removed
-	 * @param saveBackup true, if backup of the original file should be created;
-	 *                   false, otherwise
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void removeArchive(String sourcePath, String consolFun, int steps,
-									 boolean saveBackup) throws IOException, RrdException {
-		String destPath = Util.getTmpFilename();
-		removeArchive(sourcePath, destPath, consolFun, steps);
-		copyFile(destPath, sourcePath, saveBackup);
-	}
-
-	private static void copyFile(String sourcePath, String destPath, boolean saveBackup)
-			throws IOException {
-		File source = new File(sourcePath);
-		File dest = new File(destPath);
-		if (saveBackup) {
-			String backupPath = getBackupPath(destPath);
-			File backup = new File(backupPath);
-			deleteFile(backup);
-			if (!dest.renameTo(backup)) {
-				throw new IOException("Could not create backup file " + backupPath);
-			}
-		}
-		deleteFile(dest);
-		if (!source.renameTo(dest)) {
-			throw new IOException("Could not create file " + destPath + " from " + sourcePath);
-		}
-	}
-
-	private static String getBackupPath(String destPath) {
-		StringBuffer sb = new StringBuffer(destPath);
-		do {
-			sb.append(".bak");
-		} while (Util.fileExists(sb.toString()));
-		return sb.toString();
-	}
-
-	/**
-	 * Sets datasource heartbeat to a new value.
-	 *
-	 * @param sourcePath	 Path to exisiting RRD file (will be updated)
-	 * @param datasourceName Name of the datasource in the specified RRD file
-	 * @param newHeartbeat   New datasource heartbeat
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 * @throws IOException  Thrown in case of I/O error
-	 */
-	public static void setDsHeartbeat(String sourcePath, String datasourceName,
-									  long newHeartbeat) throws RrdException, IOException {
-		RrdDb rrd = new RrdDb(sourcePath);
-		try {
-			Datasource ds = rrd.getDatasource(datasourceName);
-			ds.setHeartbeat(newHeartbeat);
-		}
-		finally {
-			rrd.close();
-		}
-	}
-
-	/**
-	 * Sets datasource heartbeat to a new value.
-	 *
-	 * @param sourcePath   Path to exisiting RRD file (will be updated)
-	 * @param dsIndex	  Index of the datasource in the specified RRD file
-	 * @param newHeartbeat New datasource heartbeat
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 * @throws IOException  Thrown in case of I/O error
-	 */
-	public static void setDsHeartbeat(String sourcePath, int dsIndex, long newHeartbeat)
-			throws RrdException, IOException {
-		RrdDb rrd = new RrdDb(sourcePath);
-		try {
-			Datasource ds = rrd.getDatasource(dsIndex);
-			ds.setHeartbeat(newHeartbeat);
-		}
-		finally {
-			rrd.close();
-		}
-	}
-
-	/**
-	 * Sets datasource min value to a new value
-	 *
-	 * @param sourcePath		   Path to exisiting RRD file (will be updated)
-	 * @param datasourceName	   Name of the datasource in the specified RRD file
-	 * @param newMinValue		  New min value for the datasource
-	 * @param filterArchivedValues set to <code>true</code> if archived values less than
-	 *                             <code>newMinValue</code> should be set to NaN; set to false, otherwise.
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 * @throws IOException  Thrown in case of I/O error
-	 */
-	public static void setDsMinValue(String sourcePath, String datasourceName,
-									 double newMinValue, boolean filterArchivedValues) throws RrdException, IOException {
-		RrdDb rrd = new RrdDb(sourcePath);
-		try {
-			Datasource ds = rrd.getDatasource(datasourceName);
-			ds.setMinValue(newMinValue, filterArchivedValues);
-		}
-		finally {
-			rrd.close();
-		}
-	}
-
-	/**
-	 * Sets datasource max value to a new value.
-	 *
-	 * @param sourcePath		   Path to exisiting RRD file (will be updated)
-	 * @param datasourceName	   Name of the datasource in the specified RRD file
-	 * @param newMaxValue		  New max value for the datasource
-	 * @param filterArchivedValues set to <code>true</code> if archived values greater than
-	 *                             <code>newMaxValue</code> should be set to NaN; set to false, otherwise.
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 * @throws IOException  Thrown in case of I/O error
-	 */
-	public static void setDsMaxValue(String sourcePath, String datasourceName,
-									 double newMaxValue, boolean filterArchivedValues) throws RrdException, IOException {
-		RrdDb rrd = new RrdDb(sourcePath);
-		try {
-			Datasource ds = rrd.getDatasource(datasourceName);
-			ds.setMaxValue(newMaxValue, filterArchivedValues);
-		}
-		finally {
-			rrd.close();
-		}
-	}
-
-	/**
-	 * Updates valid value range for the given datasource.
-	 *
-	 * @param sourcePath		   Path to exisiting RRD file (will be updated)
-	 * @param datasourceName	   Name of the datasource in the specified RRD file
-	 * @param newMinValue		  New min value for the datasource
-	 * @param newMaxValue		  New max value for the datasource
-	 * @param filterArchivedValues set to <code>true</code> if archived values outside
-	 *                             of the specified min/max range should be replaced with NaNs.
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 * @throws IOException  Thrown in case of I/O error
-	 */
-	public static void setDsMinMaxValue(String sourcePath, String datasourceName,
-										double newMinValue, double newMaxValue, boolean filterArchivedValues)
-			throws RrdException, IOException {
-		RrdDb rrd = new RrdDb(sourcePath);
-		try {
-			Datasource ds = rrd.getDatasource(datasourceName);
-			ds.setMinMaxValue(newMinValue, newMaxValue, filterArchivedValues);
-		}
-		finally {
-			rrd.close();
-		}
-	}
-
-	/**
-	 * Sets single archive's X-files factor to a new value.
-	 *
-	 * @param sourcePath Path to existing RRD file (will be updated)
-	 * @param consolFun  Consolidation function of the target archive
-	 * @param steps	  Number of sptes of the target archive
-	 * @param newXff	 New X-files factor for the target archive
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 * @throws IOException  Thrown in case of I/O error
-	 */
-	public static void setArcXff(String sourcePath, String consolFun, int steps,
-								 double newXff) throws RrdException, IOException {
-		RrdDb rrd = new RrdDb(sourcePath);
-		try {
-			Archive arc = rrd.getArchive(consolFun, steps);
-			arc.setXff(newXff);
-		}
-		finally {
-			rrd.close();
-		}
-	}
-
-	/**
-	 * Creates new RRD file based on the existing one, but with a different
-	 * size (number of rows) for a single archive. The archive to be resized
-	 * is identified by its consolidation function and the number of steps.
-	 *
-	 * @param sourcePath Path to the source RRD file (will not be modified)
-	 * @param destPath   Path to the new RRD file (will be created)
-	 * @param consolFun  Consolidation function of the archive to be resized
-	 * @param numSteps   Number of steps of the archive to be resized
-	 * @param newRows	New archive size (number of archive rows)
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void resizeArchive(String sourcePath, String destPath, String consolFun,
-									 int numSteps, int newRows)
-			throws IOException, RrdException {
-		if (Util.sameFilePath(sourcePath, destPath)) {
-			throw new RrdException("Source and destination paths are the same");
-		}
-		if (newRows < 2) {
-			throw new RrdException("New arcihve size must be at least 2");
-		}
-		RrdDb rrdSource = new RrdDb(sourcePath);
-		try {
-			RrdDef rrdDef = rrdSource.getRrdDef();
-			ArcDef arcDef = rrdDef.findArchive(consolFun, numSteps);
-			if (arcDef.getRows() != newRows) {
-				arcDef.setRows(newRows);
-				rrdDef.setPath(destPath);
-				RrdDb rrdDest = new RrdDb(rrdDef);
-				try {
-					rrdSource.copyStateTo(rrdDest);
-				}
-				finally {
-					rrdDest.close();
-				}
-			}
-		}
-		finally {
-			rrdSource.close();
-		}
-	}
-
-	/**
-	 * Modifies existing RRD file, by resizing its chosen archive. The archive to be resized
-	 * is identified by its consolidation function and the number of steps.
-	 *
-	 * @param sourcePath Path to the RRD file (will be modified)
-	 * @param consolFun  Consolidation function of the archive to be resized
-	 * @param numSteps   Number of steps of the archive to be resized
-	 * @param newRows	New archive size (number of archive rows)
-	 * @param saveBackup true, if backup of the original file should be created;
-	 *                   false, otherwise
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void resizeArchive(String sourcePath, String consolFun,
-									 int numSteps, int newRows, boolean saveBackup)
-			throws IOException, RrdException {
-		String destPath = Util.getTmpFilename();
-		resizeArchive(sourcePath, destPath, consolFun, numSteps, newRows);
-		copyFile(destPath, sourcePath, saveBackup);
-	}
-
-	private static void deleteFile(File file) throws IOException {
-		if (file.exists() && !file.delete()) {
-			throw new IOException("Could not delete file: " + file.getCanonicalPath());
-		}
-	}
-
-	/**
-	 * Splits single RRD file with several datasources into a number of smaller RRD files
-	 * with a single datasource in it. All archived values are preserved. If
-	 * you have a RRD file named 'traffic.rrd' with two datasources, 'in' and 'out', this
-	 * method will create two files (with a single datasource, in the same directory)
-	 * named 'in-traffic.rrd' and 'out-traffic.rrd'.
-	 *
-	 * @param sourcePath Path to a RRD file with multiple datasources defined
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public static void split(String sourcePath) throws IOException, RrdException {
-		RrdDb rrdSource = new RrdDb(sourcePath);
-		try {
-			String[] dsNames = rrdSource.getDsNames();
-			for (String dsName : dsNames) {
-				RrdDef rrdDef = rrdSource.getRrdDef();
-				rrdDef.setPath(createSplitPath(dsName, sourcePath));
-				rrdDef.saveSingleDatasource(dsName);
-				RrdDb rrdDest = new RrdDb(rrdDef);
-				try {
-					rrdSource.copyStateTo(rrdDest);
-				}
-				finally {
-					rrdDest.close();
-				}
-			}
-		}
-		finally {
-			rrdSource.close();
-		}
-	}
-
-	/**
-	 * Returns list of canonical file names with the specified extension in the given directory. This
-	 * method is not RRD related, but might come handy to create a quick list of all RRD files
-	 * in the given directory.
-	 *
-	 * @param directory Source directory
-	 * @param extension File extension (like ".rrd", ".jrb", ".rrd.jrb")
-	 * @param resursive true if all subdirectories should be traversed for the same extension, false otherwise
-	 * @return Array of sorted canonical file names with the given extension
-	 * @throws IOException Thrown in case of I/O error
-	 */
-	public static String[] getCanonicalPaths(String directory, final String extension, boolean resursive)
-			throws IOException {
-		File baseDir = new File(directory);
-		if (!baseDir.isDirectory()) {
-			throw new IOException("Not a directory: " + directory);
-		}
-		List<String> fileList = new LinkedList<String>();
-		traverseDirectory(new File(directory), extension, resursive, fileList);
-		String[] result = fileList.toArray(new String[fileList.size()]);
-		Arrays.sort(result);
-		return result;
-	}
-
-	private static void traverseDirectory(File directory, String extension, boolean recursive, List<String> list)
-			throws IOException {
-		File[] files = directory.listFiles();
-		for (File file : files) {
-			if (file.isDirectory() && recursive) {
-				// traverse subdirectories only if recursive flag is specified
-				traverseDirectory(file, extension, recursive, list);
-			}
-			else if (file.isFile() && file.getName().endsWith(extension)) {
-				list.add(file.getCanonicalPath());
-			}
-		}
-	}
-
-	private static String createSplitPath(String dsName, String sourcePath) {
-		File file = new File(sourcePath);
-		String newName = dsName + "-" + file.getName();
-		String path = file.getAbsolutePath();
-		String parentDir = path.substring(0, 1 + path.lastIndexOf(Util.getFileSeparator()));
-		return parentDir + newName;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/RrdUpdater.java b/apps/jrobin/java/src/org/jrobin/core/RrdUpdater.java
deleted file mode 100644
index 065cec1b019813eea3a908635ab8e538dde798dd..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/RrdUpdater.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-
-interface RrdUpdater {
-	public RrdBackend getRrdBackend();
-
-	public void copyStateTo(RrdUpdater updater) throws IOException, RrdException;
-
-	public RrdAllocator getRrdAllocator();
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/Sample.java b/apps/jrobin/java/src/org/jrobin/core/Sample.java
deleted file mode 100644
index 43212d8f6efc78b269266121332bbdad9a6e2aa7..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/Sample.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.io.IOException;
-import java.util.Date;
-import java.util.StringTokenizer;
-
-/**
- * Class to represent data source values for the given timestamp. Objects of this
- * class are never created directly (no public constructor is provided). To learn more how
- * to update RRDs, see RRDTool's
- * <a href="../../../../man/rrdupdate.html" target="man">rrdupdate man page</a>.
- * <p>
- * To update a RRD with JRobin use the following procedure:
- * <p>
- * <ol>
- * <li>Obtain empty Sample object by calling method {@link RrdDb#createSample(long)
- * createSample()} on respective {@link RrdDb RrdDb} object.
- * <li>Adjust Sample timestamp if necessary (see {@link #setTime(long) setTime()} method).
- * <li>Supply data source values (see {@link #setValue(String, double) setValue()}).
- * <li>Call Sample's {@link #update() update()} method.
- * </ol>
- * <p>
- * Newly created Sample object contains all data source values set to 'unknown'.
- * You should specifify only 'known' data source values. However, if you want to specify
- * 'unknown' values too, use <code>Double.NaN</code>.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class Sample {
-	private RrdDb parentDb;
-	private long time;
-	private String[] dsNames;
-	private double[] values;
-
-	Sample(final RrdDb parentDb, final long time) throws IOException {
-		this.parentDb = parentDb;
-		this.time = time;
-		this.dsNames = parentDb.getDsNames();
-		values = new double[dsNames.length];
-		clearCurrentValues();
-	}
-
-	private Sample clearCurrentValues() {
-		for (int i = 0; i < values.length; i++) {
-			values[i] = Double.NaN;
-		}
-		return this;
-	}
-
-	/**
-	 * Sets single data source value in the sample.
-	 *
-	 * @param dsName Data source name.
-	 * @param value  Data source value.
-	 * @return This <code>Sample</code> object
-	 * @throws RrdException Thrown if invalid data source name is supplied.
-	 */
-	public Sample setValue(final String dsName, final double value) throws RrdException {
-		for (int i = 0; i < values.length; i++) {
-			if (dsNames[i].equals(dsName)) {
-				values[i] = value;
-				return this;
-			}
-		}
-		throw new RrdException("Datasource " + dsName + " not found");
-	}
-
-	/**
-	 * Sets single datasource value using data source index. Data sources are indexed by
-	 * the order specified during RRD creation (zero-based).
-	 *
-	 * @param i	 Data source index
-	 * @param value Data source values
-	 * @return This <code>Sample</code> object
-	 * @throws RrdException Thrown if data source index is invalid.
-	 */
-	public Sample setValue(final int i, final double value) throws RrdException {
-		if (i < values.length) {
-			values[i] = value;
-			return this;
-		}
-		else {
-			throw new RrdException("Sample datasource index " + i + " out of bounds");
-		}
-	}
-
-	/**
-	 * Sets some (possibly all) data source values in bulk. Data source values are
-	 * assigned in the order of their definition inside the RRD.
-	 *
-	 * @param values Data source values.
-	 * @return This <code>Sample</code> object
-	 * @throws RrdException Thrown if the number of supplied values is zero or greater
-	 *                      than the number of data sources defined in the RRD.
-	 */
-	public Sample setValues(final double[] values) throws RrdException {
-		if (values.length <= this.values.length) {
-			System.arraycopy(values, 0, this.values, 0, values.length);
-			return this;
-		}
-		else {
-			throw new RrdException("Invalid number of values specified (found " + values.length + ", only " + dsNames.length + " allowed)");
-		}
-	}
-
-	/**
-	 * Returns all current data source values in the sample.
-	 *
-	 * @return Data source values.
-	 */
-	public double[] getValues() {
-		return values;
-	}
-
-	/**
-	 * Returns sample timestamp (in seconds, without milliseconds).
-	 *
-	 * @return Sample timestamp.
-	 */
-	public long getTime() {
-		return time;
-	}
-
-	/**
-	 * Sets sample timestamp. Timestamp should be defined in seconds (without milliseconds).
-	 *
-	 * @param time New sample timestamp.
-	 * @return This <code>Sample</code> object
-	 */
-	public Sample setTime(final long time) {
-		this.time = time;
-		return this;
-	}
-
-	/**
-	 * Returns an array of all data source names. If you try to set value for the data source
-	 * name not in this array, an exception is thrown.
-	 *
-	 * @return Acceptable data source names.
-	 */
-	public String[] getDsNames() {
-		return dsNames;
-	}
-
-	/**
-	 * Sets sample timestamp and data source values in a fashion similar to RRDTool.
-	 * Argument string should be composed in the following way:
-	 * <code>timestamp:value1:value2:...:valueN</code>.
-	 * <p>
-	 * You don't have to supply all datasource values. Unspecified values will be treated
-	 * as unknowns. To specify unknown value in the argument string, use letter 'U'.
-	 *
-	 * @param timeAndValues String made by concatenating sample timestamp with corresponding
-	 *                      data source values delmited with colons. For example:<p>
-	 *                      <pre>
-	 *                      1005234132:12.2:35.6:U:24.5
-	 *                      NOW:12.2:35.6:U:24.5
-	 *                      </pre>
-	 *                      'N' stands for the current timestamp (can be replaced with 'NOW')<p>
-	 *                      Method will throw an exception if timestamp is invalid (cannot be parsed as Long, and is not 'N'
-	 *                      or 'NOW'). Datasource value which cannot be parsed as 'double' will be silently set to NaN.<p>
-	 * @return This <code>Sample</code> object
-	 * @throws RrdException Thrown if too many datasource values are supplied
-	 */
-	public Sample set(final String timeAndValues) throws RrdException {
-	    final StringTokenizer tokenizer = new StringTokenizer(timeAndValues, ":", false);
-		final int tokenCount = tokenizer.countTokens();
-		if (tokenCount > values.length + 1) {
-			throw new RrdException("Invalid number of values specified (found " + values.length + ", " + dsNames.length + " allowed)");
-		}
-		final String timeToken = tokenizer.nextToken();
-		try {
-			time = Long.parseLong(timeToken);
-		}
-		catch (final NumberFormatException nfe) {
-			if (timeToken.equalsIgnoreCase("N") || timeToken.equalsIgnoreCase("NOW")) {
-				time = Util.getTime();
-			}
-			else {
-				throw new RrdException("Invalid sample timestamp: " + timeToken);
-			}
-		}
-		for (int i = 0; tokenizer.hasMoreTokens(); i++) {
-			try {
-				values[i] = Double.parseDouble(tokenizer.nextToken());
-			}
-			catch (final NumberFormatException nfe) {
-				// NOP, value is already set to NaN
-			}
-		}
-		return this;
-	}
-
-	/**
-	 * Stores sample in the corresponding RRD. If the update operation succeedes,
-	 * all datasource values in the sample will be set to Double.NaN (unknown) values.
-	 *
-	 * @throws IOException  Thrown in case of I/O error.
-	 * @throws RrdException Thrown in case of JRobin related error.
-	 */
-	public void update() throws IOException, RrdException {
-		parentDb.store(this);
-		clearCurrentValues();
-	}
-
-	/**
-	 * <p>Creates sample with the timestamp and data source values supplied
-	 * in the argument string and stores sample in the corresponding RRD.
-	 * This method is just a shortcut for:</p>
-	 * <pre>
-	 *     set(timeAndValues);
-	 *     update();
-	 * </pre>
-	 *
-	 * @param timeAndValues String made by concatenating sample timestamp with corresponding
-	 *                      data source values delmited with colons. For example:<br>
-	 *                      <code>1005234132:12.2:35.6:U:24.5</code><br>
-	 *                      <code>NOW:12.2:35.6:U:24.5</code>
-	 * @throws IOException  Thrown in case of I/O error.
-	 * @throws RrdException Thrown in case of JRobin related error.
-	 */
-	public void setAndUpdate(final String timeAndValues) throws IOException, RrdException {
-		set(timeAndValues);
-		update();
-	}
-
-	/**
-	 * Dumps sample content using the syntax of RRDTool's update command.
-	 *
-	 * @return Sample dump.
-	 */
-	public String dump() {
-	    final StringBuffer buffer = new StringBuffer("update \"");
-		buffer.append(parentDb.getRrdBackend().getPath()).append("\" ").append(time);
-		for (final double value : values) {
-			buffer.append(":");
-			buffer.append(Util.formatDouble(value, "U", false));
-		}
-		return buffer.toString();
-	}
-
-	String getRrdToolCommand() {
-		return dump();
-	}
-
-	public String toString() {
-	    return getClass().getSimpleName() + "@" + "[parentDb=" + parentDb + ",time=" + new Date(time * 1000L) + ",dsNames=[" + printList(dsNames) + "],values=[" + printList(values) + "]]";
-	}
-
-    private String printList(final Object[] dsNames) {
-        if (dsNames == null) return "null";
-        final StringBuffer sb = new StringBuffer();
-        for (int i = 0; i < dsNames.length; i++) {
-            if (i == dsNames.length - 1) {
-                sb.append(dsNames[i]);
-            } else {
-                sb.append(dsNames[i]).append(", ");
-            }
-        }
-        return sb.toString();
-    }
-
-    private String printList(final double[] values) {
-        if (values == null) return "null";
-        final StringBuffer sb = new StringBuffer();
-        for (int i = 0; i < values.length; i++) {
-            if (i == values.length - 1) {
-                sb.append(values[i]);
-            } else {
-                sb.append(values[i]).append(", ");
-            }
-        }
-        return sb.toString();
-    }
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/SyncManager.java b/apps/jrobin/java/src/org/jrobin/core/SyncManager.java
deleted file mode 100644
index 71bba51cb229f9e119de0778e4ef08e1cfcff1a1..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/SyncManager.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.jrobin.core;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-
-
-public final class SyncManager {
-    private int m_syncPeriod = RrdNioBackendFactory.DEFAULT_SYNC_PERIOD;
-    private Timer m_timer = null;
-    private Map<RrdNioBackend,TimerTask> m_tasks = new HashMap<RrdNioBackend,TimerTask>();
-
-    public SyncManager(final int syncPeriod) {
-        m_syncPeriod = syncPeriod;
-    }
-
-    public int getSyncPeriod() {
-        return m_syncPeriod;
-    }
-
-    public void setSyncPeriod(final int syncPeriod) {
-        m_syncPeriod = syncPeriod;
-        synchronized(m_tasks) {
-            final Timer oldTimer = m_timer;
-            m_timer = new SyncTimer();
-            for (final RrdNioBackend backend : m_tasks.keySet()) {
-                m_tasks.get(backend).cancel();
-                scheduleTask(backend);
-            }
-            cancelTimer(oldTimer);
-        }
-    }
-
-    public void add(final RrdNioBackend rrdNioBackend) {
-        synchronized(m_tasks) {
-            if (m_tasks.size() == 0) {
-                m_timer = new SyncTimer();
-            }
-            scheduleTask(rrdNioBackend);
-        }
-    }
-
-    public void remove(final RrdNioBackend rrdNioBackend) {
-        synchronized (m_tasks) {
-            final TimerTask oldTask = m_tasks.remove(rrdNioBackend);
-            if (oldTask != null) oldTask.cancel();
-            if (m_tasks.size() == 0) {
-                cancelTimer(m_timer);
-                m_timer = null;
-            }
-        }
-    }
-
-    public void shutdown() {
-        synchronized(m_tasks) {
-            for (final Map.Entry<RrdNioBackend, TimerTask> entry : m_tasks.entrySet()) {
-                entry.getValue().cancel();
-            }
-            cancelTimer(m_timer);
-        }
-    }
-
-    private void cancelTimer(final Timer timer) {
-        if (timer == null) return;
-        timer.cancel();
-        timer.purge();
-    }
-
-    private void scheduleTask(final RrdNioBackend rrdNioBackend) {
-        final TimerTask task = new SyncTimerTask(rrdNioBackend);
-        if (m_tasks.containsKey(rrdNioBackend)) {
-            m_tasks.get(rrdNioBackend).cancel();
-        }
-        m_tasks.put(rrdNioBackend, task);
-        m_timer.schedule(task, getSyncPeriod() * 1000L, getSyncPeriod() * 1000L);
-    }
-
-    Timer getTimer() {
-        return m_timer;
-    }
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/core/SyncTimer.java b/apps/jrobin/java/src/org/jrobin/core/SyncTimer.java
deleted file mode 100644
index a9fcc3e1e382b9c6365252f91540cae45b1a263d..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/SyncTimer.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.jrobin.core;
-
-import java.util.Timer;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class SyncTimer extends Timer {
-    private static AtomicInteger m_serialNumber = new AtomicInteger();
-
-    public SyncTimer() {
-        super("SyncManager-" + m_serialNumber.getAndIncrement(), true);
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/SyncTimerTask.java b/apps/jrobin/java/src/org/jrobin/core/SyncTimerTask.java
deleted file mode 100644
index 2d7bf676a944920832e16f38058b5485a04288ff..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/SyncTimerTask.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.jrobin.core;
-
-import java.util.TimerTask;
-
-public final class SyncTimerTask extends TimerTask {
-    private final RrdNioBackend m_rrdNioBackend;
-
-    SyncTimerTask(final RrdNioBackend rrdNioBackend) {
-        m_rrdNioBackend = rrdNioBackend;
-    }
-
-    @Override public void run() {
-        m_rrdNioBackend.sync();
-    }
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/core/Util.java b/apps/jrobin/java/src/org/jrobin/core/Util.java
deleted file mode 100644
index 567a701793ac8494fc429cdb2264fbee07b96339..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/Util.java
+++ /dev/null
@@ -1,757 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import org.jrobin.core.timespec.TimeParser;
-import org.jrobin.core.timespec.TimeSpec;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.awt.*;
-import java.io.*;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Locale;
-
-/**
- * Class defines various utility functions used in JRobin.
- *
- * @author <a href="mailto:saxon@jrobin.org">Sasa Markovic</a>
- */
-public class Util {
-
-	public static final long MAX_LONG = Long.MAX_VALUE;
-	public static final long MIN_LONG = -Long.MAX_VALUE;
-
-	public static final double MAX_DOUBLE = Double.MAX_VALUE;
-	public static final double MIN_DOUBLE = -Double.MAX_VALUE;
-
-	// pattern RRDTool uses to format doubles in XML files
-	static final String PATTERN = "0.0000000000E00";
-	// directory under $USER_HOME used for demo graphs storing
-	static final String JROBIN_DIR = "jrobin-demo";
-
-	static final DecimalFormat df;
-
-	static {
-		df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.ENGLISH);
-		df.applyPattern(PATTERN);
-		df.setPositivePrefix("+");
-	}
-
-	/**
-	 * Converts an array of long primitives to an array of doubles.
-	 *
-	 * @param array input array of long values.
-	 * @return Same array but with all values as double.
-	 */
-	public static double[] toDoubleArray(final long[] array) {
-		double[] values = new double[ array.length ];
-		for (int i = 0; i < array.length; i++) {
-			values[i] = array[i];
-		}
-		return values;
-	}
-
-	/**
-	 * Returns current timestamp in seconds (without milliseconds). Returned timestamp
-	 * is obtained with the following expression:
-	 * <p>
-	 * <code>(System.currentTimeMillis() + 500L) / 1000L</code>
-	 *
-	 * @return Current timestamp
-	 */
-	public static long getTime() {
-		return (System.currentTimeMillis() + 500L) / 1000L;
-	}
-
-	/**
-	 * Just an alias for {@link #getTime()} method.
-	 *
-	 * @return Current timestamp (without milliseconds)
-	 */
-	public static long getTimestamp() {
-		return getTime();
-	}
-
-	/**
-	 * Rounds the given timestamp to the nearest whole &quot;step&quot;. Rounded value is obtained
-	 * from the following expression:
-	 * <p>
-	 * <code>timestamp - timestamp % step;</code>
-	 *
-	 * @param timestamp Timestamp in seconds
-	 * @param step	  Step in seconds
-	 * @return "Rounded" timestamp
-	 */
-	public static long normalize(long timestamp, long step) {
-		return timestamp - timestamp % step;
-	}
-
-	/**
-	 * Returns the greater of two double values, but treats NaN as the smallest possible
-	 * value. Note that <code>Math.max()</code> behaves differently for NaN arguments.
-	 *
-	 * @param x an argument
-	 * @param y another argument
-	 * @return the lager of arguments
-	 */
-	public static double max(double x, double y) {
-		return Double.isNaN(x) ? y : Double.isNaN(y) ? x : Math.max(x, y);
-	}
-
-	/**
-	 * Returns the smaller of two double values, but treats NaN as the greatest possible
-	 * value. Note that <code>Math.min()</code> behaves differently for NaN arguments.
-	 *
-	 * @param x an argument
-	 * @param y another argument
-	 * @return the smaller of arguments
-	 */
-	public static double min(double x, double y) {
-		return Double.isNaN(x) ? y : Double.isNaN(y) ? x : Math.min(x, y);
-	}
-
-	/**
-	 * Calculates sum of two doubles, but treats NaNs as zeros.
-	 *
-	 * @param x First double
-	 * @param y Second double
-	 * @return Sum(x,y) calculated as <code>Double.isNaN(x)? y: Double.isNaN(y)? x: x + y;</code>
-	 */
-	public static double sum(double x, double y) {
-		return Double.isNaN(x) ? y : Double.isNaN(y) ? x : x + y;
-	}
-
-	static String formatDouble(double x, String nanString, boolean forceExponents) {
-		if (Double.isNaN(x)) {
-			return nanString;
-		}
-		if (forceExponents) {
-			return df.format(x);
-		}
-		return "" + x;
-	}
-
-	static String formatDouble(double x, boolean forceExponents) {
-		return formatDouble(x, "" + Double.NaN, forceExponents);
-	}
-
-	/**
-	 * Formats double as a string using exponential notation (RRDTool like). Used for debugging
-	 * throught the project.
-	 *
-	 * @param x value to be formatted
-	 * @return string like "+1.234567E+02"
-	 */
-	public static String formatDouble(double x) {
-		return formatDouble(x, true);
-	}
-
-	/**
-	 * Returns <code>Date</code> object for the given timestamp (in seconds, without
-	 * milliseconds)
-	 *
-	 * @param timestamp Timestamp in seconds.
-	 * @return Corresponding Date object.
-	 */
-	public static Date getDate(long timestamp) {
-		return new Date(timestamp * 1000L);
-	}
-
-	/**
-	 * Returns <code>Calendar</code> object for the given timestamp
-	 * (in seconds, without milliseconds)
-	 *
-	 * @param timestamp Timestamp in seconds.
-	 * @return Corresponding Calendar object.
-	 */
-	public static Calendar getCalendar(long timestamp) {
-		Calendar calendar = Calendar.getInstance();
-		calendar.setTimeInMillis(timestamp * 1000L);
-		return calendar;
-	}
-
-	/**
-	 * Returns <code>Calendar</code> object for the given Date object
-	 *
-	 * @param date Date object
-	 * @return Corresponding Calendar object.
-	 */
-	public static Calendar getCalendar(Date date) {
-		Calendar calendar = Calendar.getInstance();
-		calendar.setTime(date);
-		return calendar;
-	}
-
-	/**
-	 * Returns timestamp (unix epoch) for the given Date object
-	 *
-	 * @param date Date object
-	 * @return Corresponding timestamp (without milliseconds)
-	 */
-	public static long getTimestamp(final Date date) {
-		// round to whole seconds, ignore milliseconds
-		return (date.getTime() + 499L) / 1000L;
-	}
-
-	/**
-	 * Returns timestamp (unix epoch) for the given Calendar object
-	 *
-	 * @param gc Calendar object
-	 * @return Corresponding timestamp (without milliseconds)
-	 */
-	public static long getTimestamp(final Calendar gc) {
-		return getTimestamp(gc.getTime());
-	}
-
-	/**
-	 * Returns timestamp (unix epoch) for the given year, month, day, hour and minute.
-	 *
-	 * @param year  Year
-	 * @param month Month (zero-based)
-	 * @param day   Day in month
-	 * @param hour  Hour
-	 * @param min   Minute
-	 * @return Corresponding timestamp
-	 */
-	public static long getTimestamp(final int year, final int month, final int day, final int hour, final int min) {
-	    final Calendar calendar = Calendar.getInstance();
-		calendar.clear();
-		calendar.set(year, month, day, hour, min);
-		return Util.getTimestamp(calendar);
-	}
-
-	/**
-	 * Returns timestamp (unix epoch) for the given year, month and day.
-	 *
-	 * @param year  Year
-	 * @param month Month (zero-based)
-	 * @param day   Day in month
-	 * @return Corresponding timestamp
-	 */
-	public static long getTimestamp(int year, int month, int day) {
-		return Util.getTimestamp(year, month, day, 0, 0);
-	}
-
-	/**
-	 * Parses at-style time specification and returns the corresponding timestamp. For example:<p>
-	 * <pre>
-	 * long t = Util.getTimestamp("now-1d");
-	 * </pre>
-	 *
-	 * @param atStyleTimeSpec at-style time specification. For the complete explanation of the syntax
-	 *                        allowed see RRDTool's <code>rrdfetch</code> man page.<p>
-	 * @return timestamp in seconds since epoch.
-	 * @throws RrdException Thrown if invalid time specification is supplied.
-	 */
-	public static long getTimestamp(final String atStyleTimeSpec) throws RrdException {
-	    final TimeSpec timeSpec = new TimeParser(atStyleTimeSpec).parse();
-		return timeSpec.getTimestamp();
-	}
-
-	/**
-	 * Parses two related at-style time specifications and returns corresponding timestamps. For example:<p>
-	 * <pre>
-	 * long[] t = Util.getTimestamps("end-1d","now");
-	 * </pre>
-	 *
-	 * @param atStyleTimeSpec1 Starting at-style time specification. For the complete explanation of the syntax
-	 *                         allowed see RRDTool's <code>rrdfetch</code> man page.<p>
-	 * @param atStyleTimeSpec2 Ending at-style time specification. For the complete explanation of the syntax
-	 *                         allowed see RRDTool's <code>rrdfetch</code> man page.<p>
-	 * @return An array of two longs representing starting and ending timestamp in seconds since epoch.
-	 * @throws RrdException Thrown if any input time specification is invalid.
-	 */
-	public static long[] getTimestamps(final String atStyleTimeSpec1, final String atStyleTimeSpec2) throws RrdException {
-	    final TimeSpec timeSpec1 = new TimeParser(atStyleTimeSpec1).parse();
-		final TimeSpec timeSpec2 = new TimeParser(atStyleTimeSpec2).parse();
-		return TimeSpec.getTimestamps(timeSpec1, timeSpec2);
-	}
-
-	/**
-	 * Parses input string as a double value. If the value cannot be parsed, Double.NaN
-	 * is returned (NumberFormatException is never thrown).
-	 *
-	 * @param valueStr String representing double value
-	 * @return a double corresponding to the input string
-	 */
-	public static double parseDouble(final String valueStr) {
-		double value;
-		try {
-			value = Double.parseDouble(valueStr);
-		}
-		catch (final NumberFormatException nfe) {
-			value = Double.NaN;
-		}
-		return value;
-	}
-
-	/**
-	 * Checks if a string can be parsed as double.
-	 *
-	 * @param s Input string
-	 * @return <code>true</code> if the string can be parsed as double, <code>false</code> otherwise
-	 */
-	public static boolean isDouble(final String s) {
-		try {
-			Double.parseDouble(s);
-			return true;
-		}
-		catch (final NumberFormatException nfe) {
-			return false;
-		}
-	}
-
-	/**
-	 * Parses input string as a boolean value. The parser is case insensitive.
-	 *
-	 * @param valueStr String representing boolean value
-	 * @return <code>true</code>, if valueStr equals to 'true', 'on', 'yes', 'y' or '1';
-	 *         <code>false</code> in all other cases.
-	 */
-	public static boolean parseBoolean(final String valueStr) {
-		return valueStr.equalsIgnoreCase("true") ||
-				valueStr.equalsIgnoreCase("on") ||
-				valueStr.equalsIgnoreCase("yes") ||
-				valueStr.equalsIgnoreCase("y") ||
-				valueStr.equalsIgnoreCase("1");
-	}
-
-	/**
-	 * Parses input string as color. The color string should be of the form #RRGGBB (no alpha specified,
-	 * opaque color) or #RRGGBBAA (alpa specified, transparent colors). Leading character '#' is
-	 * optional.
-	 *
-	 * @param valueStr Input string, for example #FFAA24, #AABBCC33, 010203 or ABC13E4F
-	 * @return Paint object
-	 * @throws RrdException If the input string is not 6 or 8 characters long (without optional '#')
-	 */
-	public static Paint parseColor(final String valueStr) throws RrdException {
-	    final String c = valueStr.startsWith("#") ? valueStr.substring(1) : valueStr;
-		if (c.length() != 6 && c.length() != 8) {
-			throw new RrdException("Invalid color specification: " + valueStr);
-		}
-		final String r = c.substring(0, 2), g = c.substring(2, 4), b = c.substring(4, 6);
-		if (c.length() == 6) {
-			return new Color(Integer.parseInt(r, 16), Integer.parseInt(g, 16), Integer.parseInt(b, 16));
-		}
-		else {
-		    final String a = c.substring(6);
-			return new Color(Integer.parseInt(r, 16), Integer.parseInt(g, 16),
-					Integer.parseInt(b, 16), Integer.parseInt(a, 16));
-		}
-	}
-
-	/**
-	 * Returns file system separator string.
-	 *
-	 * @return File system separator ("/" on Unix, "\" on Windows)
-	 */
-	public static String getFileSeparator() {
-		return System.getProperty("file.separator");
-	}
-
-	/**
-	 * Returns path to user's home directory.
-	 *
-	 * @return Path to users home directory, with file separator appended.
-	 */
-	public static String getUserHomeDirectory() {
-		return System.getProperty("user.home") + getFileSeparator();
-	}
-
-	/**
-	 * Returns path to directory used for placement of JRobin demo graphs and creates it
-	 * if necessary.
-	 *
-	 * @return Path to demo directory (defaults to $HOME/jrobin/) if directory exists or
-	 *         was successfully created. Null if such directory could not be created.
-	 */
-	public static String getJRobinDemoDirectory() {
-		final String homeDirPath = getUserHomeDirectory() + JROBIN_DIR + getFileSeparator();
-		final File homeDirFile = new File(homeDirPath);
-		return (homeDirFile.exists() || homeDirFile.mkdirs()) ? homeDirPath : null;
-	}
-
-	/**
-	 * Returns full path to the file stored in the demo directory of JRobin
-	 *
-	 * @param filename Partial path to the file stored in the demo directory of JRobin
-	 *                 (just name and extension, without parent directories)
-	 * @return Full path to the file
-	 */
-	public static String getJRobinDemoPath(final String filename) {
-		final String demoDir = getJRobinDemoDirectory();
-		if (demoDir != null) {
-			return demoDir + filename;
-		}
-		else {
-			return null;
-		}
-	}
-
-	static boolean sameFilePath(final String path1, final String path2) throws IOException {
-	    final File file1 = new File(path1);
-		final File file2 = new File(path2);
-		return file1.getCanonicalPath().equals(file2.getCanonicalPath());
-	}
-
-	static int getMatchingDatasourceIndex(final RrdDb rrd1, final int dsIndex, final RrdDb rrd2) throws IOException {
-	    final String dsName = rrd1.getDatasource(dsIndex).getDsName();
-		try {
-			return rrd2.getDsIndex(dsName);
-		}
-		catch (final RrdException e) {
-			return -1;
-		}
-	}
-
-	static int getMatchingArchiveIndex(final RrdDb rrd1, final int arcIndex, final RrdDb rrd2) throws IOException {
-	    final Archive archive = rrd1.getArchive(arcIndex);
-		final String consolFun = archive.getConsolFun();
-		final int steps = archive.getSteps();
-		try {
-			return rrd2.getArcIndex(consolFun, steps);
-		}
-		catch (final RrdException e) {
-			return -1;
-		}
-	}
-
-	static String getTmpFilename() throws IOException {
-		return File.createTempFile("JROBIN_", ".tmp").getCanonicalPath();
-	}
-
-	static final String ISO_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";   // ISO
-
-	/**
-	 * Creates Calendar object from a string. The string should represent
-	 * either a long integer (UNIX timestamp in seconds without milliseconds,
-	 * like "1002354657") or a human readable date string in the format "yyyy-MM-dd HH:mm:ss"
-	 * (like "2004-02-25 12:23:45").
-	 *
-	 * @param timeStr Input string
-	 * @return Calendar object
-	 */
-	public static Calendar getCalendar(final String timeStr) {
-		// try to parse it as long
-		try {
-		    return Util.getCalendar(Long.parseLong(timeStr));
-		}
-		catch (final NumberFormatException nfe) {
-			// not a long timestamp, try to parse it as data
-		    final SimpleDateFormat df = new SimpleDateFormat(ISO_DATE_FORMAT);
-			df.setLenient(false);
-			try {
-			    return Util.getCalendar(df.parse(timeStr));
-			}
-			catch (final ParseException pe) {
-				throw new IllegalArgumentException("Time/date not in " + ISO_DATE_FORMAT + " format: " + timeStr);
-			}
-		}
-	}
-
-	/**
-	 * Various DOM utility functions
-	 */
-	public static class Xml {
-		public static Node[] getChildNodes(final Node parentNode) {
-			return getChildNodes(parentNode, null);
-		}
-
-		public static Node[] getChildNodes(final Node parentNode, final String childName) {
-		    final ArrayList<Node> nodes = new ArrayList<Node>();
-			final NodeList nodeList = parentNode.getChildNodes();
-			for (int i = 0; i < nodeList.getLength(); i++) {
-				final Node node = nodeList.item(i);
-				if (childName == null || node.getNodeName().equals(childName)) {
-					nodes.add(node);
-				}
-			}
-			return nodes.toArray(new Node[0]);
-		}
-
-		public static Node getFirstChildNode(final Node parentNode, final String childName) throws RrdException {
-		    final Node[] childs = getChildNodes(parentNode, childName);
-			if (childs.length > 0) {
-				return childs[0];
-			}
-			throw new RrdException("XML Error, no such child: " + childName);
-		}
-
-		public static boolean hasChildNode(final Node parentNode, final String childName) {
-		    final Node[] childs = getChildNodes(parentNode, childName);
-			return childs.length > 0;
-		}
-
-		// -- Wrapper around getChildValue with trim
-		public static String getChildValue(final Node parentNode, final String childName) throws RrdException {
-			return getChildValue(parentNode, childName, true);
-		}
-
-		public static String getChildValue(final Node parentNode, final String childName, final boolean trim) throws RrdException {
-		    final NodeList children = parentNode.getChildNodes();
-			for (int i = 0; i < children.getLength(); i++) {
-				final Node child = children.item(i);
-				if (child.getNodeName().equals(childName)) {
-					return getValue(child, trim);
-				}
-			}
-			throw new RrdException("XML Error, no such child: " + childName);
-		}
-
-		// -- Wrapper around getValue with trim
-		public static String getValue(final Node node) {
-			return getValue(node, true);
-		}
-
-		public static String getValue(final Node node, final boolean trimValue) {
-			String value = null;
-			final Node child = node.getFirstChild();
-			if (child != null) {
-				value = child.getNodeValue();
-				if (value != null && trimValue) {
-					value = value.trim();
-				}
-			}
-			return value;
-		}
-
-		public static int getChildValueAsInt(final Node parentNode, final String childName) throws RrdException {
-		    final String valueStr = getChildValue(parentNode, childName);
-			return Integer.parseInt(valueStr);
-		}
-
-		public static int getValueAsInt(final Node node) {
-			return Integer.parseInt(getValue(node));
-		}
-
-		public static long getChildValueAsLong(final Node parentNode, final String childName) throws RrdException {
-			final String valueStr = getChildValue(parentNode, childName);
-			return Long.parseLong(valueStr);
-		}
-
-		public static long getValueAsLong(final Node node) {
-			return Long.parseLong(getValue(node));
-		}
-
-		public static double getChildValueAsDouble(final Node parentNode, final String childName) throws RrdException {
-			return Util.parseDouble(getChildValue(parentNode, childName));
-		}
-
-		public static double getValueAsDouble(final Node node) {
-			return Util.parseDouble(getValue(node));
-		}
-
-		public static boolean getChildValueAsBoolean(final Node parentNode, final String childName) throws RrdException {
-			return Util.parseBoolean(getChildValue(parentNode, childName));
-		}
-
-		public static boolean getValueAsBoolean(final Node node) {
-			return Util.parseBoolean(getValue(node));
-		}
-
-		public static Element getRootElement(final InputSource inputSource) throws RrdException, IOException {
-		    final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-			factory.setValidating(false);
-			factory.setNamespaceAware(false);
-			try {
-			    final DocumentBuilder builder = factory.newDocumentBuilder();
-			    final Document doc = builder.parse(inputSource);
-				return doc.getDocumentElement();
-			}
-			catch (final ParserConfigurationException e) {
-				throw new RrdException(e);
-			}
-			catch (final SAXException e) {
-				throw new RrdException(e);
-			}
-		}
-
-		public static Element getRootElement(final String xmlString) throws RrdException, IOException {
-			return getRootElement(new InputSource(new StringReader(xmlString)));
-		}
-
-		public static Element getRootElement(final File xmlFile) throws RrdException, IOException {
-			Reader reader = null;
-			try {
-				reader = new FileReader(xmlFile);
-				return getRootElement(new InputSource(reader));
-			}
-			finally {
-				if (reader != null) {
-					reader.close();
-				}
-			}
-		}
-	}
-
-	private static long lastLap = System.currentTimeMillis();
-
-	/**
-	 * Function used for debugging purposes and performance bottlenecks detection.
-	 * Probably of no use for end users of JRobin.
-	 *
-	 * @return String representing time in seconds since last
-	 *         <code>getLapTime()</code> method call.
-	 */
-	public static String getLapTime() {
-	    final long newLap = System.currentTimeMillis();
-		final double seconds = (newLap - lastLap) / 1000.0;
-		lastLap = newLap;
-		return "[" + seconds + " sec]";
-	}
-
-	/**
-	 * Returns the root directory of the JRobin distribution. Useful in some demo applications,
-	 * probably of no use anywhere else.
-	 * <p>
-	 * The function assumes that all JRobin .class files are placed under
-	 * the &lt;root&gt;/classes subdirectory and that all jars (libraries) are placed in the
-	 * &lt;root&gt;/lib subdirectory (the original JRobin directory structure).
-	 *
-	 * @return absolute path to JRobin's home directory
-	 */
-	public static String getJRobinHomeDirectory() {
-	    final String className = Util.class.getName().replace('.', '/');
-		String uri = Util.class.getResource("/" + className + ".class").toString();
-		//System.out.println(uri);
-		if (uri.startsWith("file:/")) {
-			uri = uri.substring(6);
-			File file = new File(uri);
-			// let's go 5 steps backwards
-			for (int i = 0; i < 5; i++) {
-				file = file.getParentFile();
-			}
-			uri = file.getAbsolutePath();
-		}
-		else if (uri.startsWith("jar:file:/")) {
-			uri = uri.substring(9, uri.lastIndexOf('!'));
-			File file = new File(uri);
-			// let's go 2 steps backwards
-			for (int i = 0; i < 2; i++) {
-				file = file.getParentFile();
-			}
-			uri = file.getAbsolutePath();
-		}
-		else {
-			uri = null;
-		}
-		return uri;
-	}
-
-	/**
-	 * Compares two doubles but treats all NaNs as equal.
-	 * In Java (by default) Double.NaN == Double.NaN always returns <code>false</code>
-	 *
-	 * @param x the first value
-	 * @param y the second value
-	 * @return <code>true</code> if x and y are both equal to Double.NaN, or if x == y. <code>false</code> otherwise
-	 */
-	public static boolean equal(final double x, final double y) {
-		return (Double.isNaN(x) && Double.isNaN(y)) || (x == y);
-	}
-
-	/**
-	 * Returns canonical file path for the given file path
-	 *
-	 * @param path Absolute or relative file path
-	 * @return Canonical file path
-	 * @throws IOException Thrown if canonical file path could not be resolved
-	 */
-	public static String getCanonicalPath(final String path) throws IOException {
-		return new File(path).getCanonicalPath();
-	}
-
-	/**
-	 * Returns last modification time for the given file.
-	 *
-	 * @param file File object representing file on the disk
-	 * @return Last modification time in seconds (without milliseconds)
-	 */
-	public static long getLastModified(final String file) {
-		return (new File(file).lastModified() + 500L) / 1000L;
-	}
-
-	/**
-	 * Checks if the file with the given file name exists
-	 *
-	 * @param filename File name
-	 * @return <code>true</code> if file exists, <code>false</code> otherwise
-	 */
-	public static boolean fileExists(final String filename) {
-		return new File(filename).exists();
-	}
-
-	/**
-	 * Finds max value for an array of doubles (NaNs are ignored). If all values in the array
-	 * are NaNs, NaN is returned.
-	 *
-	 * @param values Array of double values
-	 * @return max value in the array (NaNs are ignored)
-	 */
-	public static double max(final double[] values) {
-		double max = Double.NaN;
-		for (final double value : values) {
-			max = Util.max(max, value);
-		}
-		return max;
-	}
-
-	/**
-	 * Finds min value for an array of doubles (NaNs are ignored). If all values in the array
-	 * are NaNs, NaN is returned.
-	 *
-	 * @param values Array of double values
-	 * @return min value in the array (NaNs are ignored)
-	 */
-	public static double min(final double[] values) {
-		double min = Double.NaN;
-		for (final double value : values) {
-			min = Util.min(min, value);
-		}
-		return min;
-	}
-
-	/**
-	 * Equivalent of the C-style sprintf function. Sorry, it works only in Java5.
-	 *
-	 * @param format Format string
-	 * @param args   Arbitrary list of arguments
-	 * @return Formatted string
-	 */
-	public static String sprintf(final String format, final Object ... args) {
-	    final String fmt = format.replaceAll("([^%]|^)%([^a-zA-Z%]*)l(f|g|e)", "$1%$2$3");
-		return String.format(fmt, args);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/XmlReader.java b/apps/jrobin/java/src/org/jrobin/core/XmlReader.java
deleted file mode 100644
index c373287bb00d1b29a0889a54799e084315eb78d3..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/XmlReader.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import java.io.File;
-import java.io.IOException;
-
-class XmlReader extends DataImporter {
-
-	private Element root;
-	private Node[] dsNodes, arcNodes;
-
-	XmlReader(String xmlFilePath) throws IOException, RrdException {
-		root = Util.Xml.getRootElement(new File(xmlFilePath));
-		dsNodes = Util.Xml.getChildNodes(root, "ds");
-		arcNodes = Util.Xml.getChildNodes(root, "rra");
-	}
-
-	String getVersion() throws RrdException {
-		return Util.Xml.getChildValue(root, "version");
-	}
-
-	long getLastUpdateTime() throws RrdException {
-		return Util.Xml.getChildValueAsLong(root, "lastupdate");
-	}
-
-	long getStep() throws RrdException {
-		return Util.Xml.getChildValueAsLong(root, "step");
-	}
-
-	int getDsCount() {
-		return dsNodes.length;
-	}
-
-	int getArcCount() {
-		return arcNodes.length;
-	}
-
-	String getDsName(int dsIndex) throws RrdException {
-		return Util.Xml.getChildValue(dsNodes[dsIndex], "name");
-	}
-
-	String getDsType(int dsIndex) throws RrdException {
-		return Util.Xml.getChildValue(dsNodes[dsIndex], "type");
-	}
-
-	long getHeartbeat(int dsIndex) throws RrdException {
-		return Util.Xml.getChildValueAsLong(dsNodes[dsIndex], "minimal_heartbeat");
-	}
-
-	double getMinValue(int dsIndex) throws RrdException {
-		return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "min");
-	}
-
-	double getMaxValue(int dsIndex) throws RrdException {
-		return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "max");
-	}
-
-	double getLastValue(int dsIndex) throws RrdException {
-		return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "last_ds");
-	}
-
-	double getAccumValue(int dsIndex) throws RrdException {
-		return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "value");
-	}
-
-	long getNanSeconds(int dsIndex) throws RrdException {
-		return Util.Xml.getChildValueAsLong(dsNodes[dsIndex], "unknown_sec");
-	}
-
-	String getConsolFun(int arcIndex) throws RrdException {
-		return Util.Xml.getChildValue(arcNodes[arcIndex], "cf");
-	}
-
-	double getXff(int arcIndex) throws RrdException {
-		return Util.Xml.getChildValueAsDouble(arcNodes[arcIndex], "xff");
-	}
-
-	int getSteps(int arcIndex) throws RrdException {
-		return Util.Xml.getChildValueAsInt(arcNodes[arcIndex], "pdp_per_row");
-	}
-
-	double getStateAccumValue(int arcIndex, int dsIndex) throws RrdException {
-		Node cdpNode = Util.Xml.getFirstChildNode(arcNodes[arcIndex], "cdp_prep");
-		Node[] dsNodes = Util.Xml.getChildNodes(cdpNode, "ds");
-		return Util.Xml.getChildValueAsDouble(dsNodes[dsIndex], "value");
-	}
-
-	int getStateNanSteps(int arcIndex, int dsIndex) throws RrdException {
-		Node cdpNode = Util.Xml.getFirstChildNode(arcNodes[arcIndex], "cdp_prep");
-		Node[] dsNodes = Util.Xml.getChildNodes(cdpNode, "ds");
-		return Util.Xml.getChildValueAsInt(dsNodes[dsIndex], "unknown_datapoints");
-	}
-
-	int getRows(int arcIndex) throws RrdException {
-		Node dbNode = Util.Xml.getFirstChildNode(arcNodes[arcIndex], "database");
-		Node[] rows = Util.Xml.getChildNodes(dbNode, "row");
-		return rows.length;
-	}
-
-	double[] getValues(int arcIndex, int dsIndex) throws RrdException {
-		Node dbNode = Util.Xml.getFirstChildNode(arcNodes[arcIndex], "database");
-		Node[] rows = Util.Xml.getChildNodes(dbNode, "row");
-		double[] values = new double[rows.length];
-		for (int i = 0; i < rows.length; i++) {
-			Node[] vNodes = Util.Xml.getChildNodes(rows[i], "v");
-			Node vNode = vNodes[dsIndex];
-			values[i] = Util.parseDouble(vNode.getFirstChild().getNodeValue().trim());
-		}
-		return values;
-	}
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/core/XmlTemplate.java b/apps/jrobin/java/src/org/jrobin/core/XmlTemplate.java
deleted file mode 100644
index 595bf68a284adf934986a99ffc3fe69e465a1071..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/XmlTemplate.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
-
-import java.awt.*;
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Class used as a base class for various XML template related classes. Class provides
- * methods for XML source parsing and XML tree traversing. XML source may have unlimited
- * number of placeholders (variables) in the format <code>${variable_name}</code>.
- * Methods are provided to specify variable values at runtime.
- * Note that this class has limited functionality: XML source gets parsed, and variable
- * values are collected. You have to extend this class to do something more useful.<p>
- */
-public abstract class XmlTemplate {
-	private static final String PATTERN_STRING = "\\$\\{(\\w+)\\}";
-	private static final Pattern PATTERN = Pattern.compile(PATTERN_STRING);
-
-	protected Element root;
-	private HashMap<String, Object> valueMap = new HashMap<String, Object>();
-	private HashSet<Node> validatedNodes = new HashSet<Node>();
-
-	protected XmlTemplate(InputSource xmlSource) throws IOException, RrdException {
-		root = Util.Xml.getRootElement(xmlSource);
-	}
-
-	protected XmlTemplate(String xmlString) throws IOException, RrdException {
-		root = Util.Xml.getRootElement(xmlString);
-	}
-
-	protected XmlTemplate(File xmlFile) throws IOException, RrdException {
-		root = Util.Xml.getRootElement(xmlFile);
-	}
-
-	/**
-	 * Removes all placeholder-value mappings.
-	 */
-	public void clearValues() {
-		valueMap.clear();
-	}
-
-	/**
-	 * Sets value for a single XML template variable. Variable name should be specified
-	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
-	 * <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
-	 *
-	 * @param name  variable name
-	 * @param value value to be set in the XML template
-	 */
-	public void setVariable(String name, String value) {
-		valueMap.put(name, value);
-	}
-
-	/**
-	 * Sets value for a single XML template variable. Variable name should be specified
-	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
-	 * <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
-	 *
-	 * @param name  variable name
-	 * @param value value to be set in the XML template
-	 */
-	public void setVariable(String name, int value) {
-		valueMap.put(name, value);
-	}
-
-	/**
-	 * Sets value for a single XML template variable. Variable name should be specified
-	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
-	 * <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
-	 *
-	 * @param name  variable name
-	 * @param value value to be set in the XML template
-	 */
-	public void setVariable(String name, long value) {
-		valueMap.put(name, value);
-	}
-
-	/**
-	 * Sets value for a single XML template variable. Variable name should be specified
-	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
-	 * <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
-	 *
-	 * @param name  variable name
-	 * @param value value to be set in the XML template
-	 */
-	public void setVariable(String name, double value) {
-		valueMap.put(name, value);
-	}
-
-	/**
-	 * Sets value for a single XML template variable. Variable name should be specified
-	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
-	 * <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
-	 *
-	 * @param name  variable name
-	 * @param value value to be set in the XML template
-	 */
-	public void setVariable(String name, Color value) {
-		String r = byteToHex(value.getRed());
-		String g = byteToHex(value.getGreen());
-		String b = byteToHex(value.getBlue());
-		String a = byteToHex(value.getAlpha());
-		valueMap.put(name, "#" + r + g + b + a);
-	}
-
-	private String byteToHex(int i) {
-		String s = Integer.toHexString(i);
-		while (s.length() < 2) {
-			s = "0" + s;
-		}
-		return s;
-	}
-
-	/**
-	 * Sets value for a single XML template variable. Variable name should be specified
-	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
-	 * <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
-	 *
-	 * @param name  variable name
-	 * @param value value to be set in the XML template
-	 */
-	public void setVariable(String name, Date value) {
-		setVariable(name, Util.getTimestamp(value));
-	}
-
-	/**
-	 * Sets value for a single XML template variable. Variable name should be specified
-	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
-	 * <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
-	 *
-	 * @param name  variable name
-	 * @param value value to be set in the XML template
-	 */
-	public void setVariable(String name, Calendar value) {
-		setVariable(name, Util.getTimestamp(value));
-	}
-
-	/**
-	 * Sets value for a single XML template variable. Variable name should be specified
-	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
-	 * <code>${start}</code>, specify <code>start</code> for the <code>name</code> parameter.
-	 *
-	 * @param name  variable name
-	 * @param value value to be set in the XML template
-	 */
-	public void setVariable(String name, boolean value) {
-		valueMap.put(name, "" + value);
-	}
-
-	/**
-	 * Searches the XML template to see if there are variables in there that
-	 * will need to be set.
-	 *
-	 * @return True if variables were detected, false if not.
-	 */
-	public boolean hasVariables() {
-		return PATTERN.matcher(root.toString()).find();
-	}
-
-	/**
-	 * Returns the list of variables that should be set in this template.
-	 *
-	 * @return List of variable names as an array of strings.
-	 */
-	public String[] getVariables() {
-		ArrayList<String> list = new ArrayList<String>();
-		Matcher m = PATTERN.matcher(root.toString());
-
-		while (m.find()) {
-			String var = m.group(1);
-			if (!list.contains(var)) {
-				list.add(var);
-			}
-		}
-
-		return list.toArray(new String[list.size()]);
-	}
-
-	protected static Node[] getChildNodes(Node parentNode, String childName) {
-		return Util.Xml.getChildNodes(parentNode, childName);
-	}
-
-	protected static Node[] getChildNodes(Node parentNode) {
-		return Util.Xml.getChildNodes(parentNode, null);
-	}
-
-	protected static Node getFirstChildNode(Node parentNode, String childName) throws RrdException {
-		return Util.Xml.getFirstChildNode(parentNode, childName);
-	}
-
-	protected boolean hasChildNode(Node parentNode, String childName) {
-		return Util.Xml.hasChildNode(parentNode, childName);
-	}
-
-	protected String getChildValue(Node parentNode, String childName) throws RrdException {
-		return getChildValue(parentNode, childName, true);
-	}
-
-	protected String getChildValue(Node parentNode, String childName, boolean trim) throws RrdException {
-		String value = Util.Xml.getChildValue(parentNode, childName, trim);
-		return resolveMappings(value);
-	}
-
-	protected String getValue(Node parentNode) {
-		return getValue(parentNode, true);
-	}
-
-	protected String getValue(Node parentNode, boolean trim) {
-		String value = Util.Xml.getValue(parentNode, trim);
-		return resolveMappings(value);
-	}
-
-	private String resolveMappings(String templateValue) {
-		if (templateValue == null) {
-			return null;
-		}
-		Matcher matcher = PATTERN.matcher(templateValue);
-		StringBuffer result = new StringBuffer();
-		int lastMatchEnd = 0;
-		while (matcher.find()) {
-			String var = matcher.group(1);
-			if (valueMap.containsKey(var)) {
-				// mapping found
-				result.append(templateValue.substring(lastMatchEnd, matcher.start()));
-				result.append(valueMap.get(var).toString());
-				lastMatchEnd = matcher.end();
-			}
-			else {
-				// no mapping found - this is illegal
-				// throw runtime exception
-				throw new IllegalArgumentException("No mapping found for template variable ${" + var + "}");
-			}
-		}
-		result.append(templateValue.substring(lastMatchEnd));
-		return result.toString();
-	}
-
-	protected int getChildValueAsInt(Node parentNode, String childName) throws RrdException {
-		String valueStr = getChildValue(parentNode, childName);
-		return Integer.parseInt(valueStr);
-	}
-
-	protected int getValueAsInt(Node parentNode) {
-		String valueStr = getValue(parentNode);
-		return Integer.parseInt(valueStr);
-	}
-
-	protected long getChildValueAsLong(Node parentNode, String childName) throws RrdException {
-		String valueStr = getChildValue(parentNode, childName);
-		return Long.parseLong(valueStr);
-	}
-
-	protected long getValueAsLong(Node parentNode) {
-		String valueStr = getValue(parentNode);
-		return Long.parseLong(valueStr);
-	}
-
-	protected double getChildValueAsDouble(Node parentNode, String childName) throws RrdException {
-		String valueStr = getChildValue(parentNode, childName);
-		return Util.parseDouble(valueStr);
-	}
-
-	protected double getValueAsDouble(Node parentNode) {
-		String valueStr = getValue(parentNode);
-		return Util.parseDouble(valueStr);
-	}
-
-	protected boolean getChildValueAsBoolean(Node parentNode, String childName) throws RrdException {
-		String valueStr = getChildValue(parentNode, childName);
-		return Util.parseBoolean(valueStr);
-	}
-
-	protected boolean getValueAsBoolean(Node parentNode) {
-		String valueStr = getValue(parentNode);
-		return Util.parseBoolean(valueStr);
-	}
-
-	protected Paint getValueAsColor(Node parentNode) throws RrdException {
-		String rgbStr = getValue(parentNode);
-		return Util.parseColor(rgbStr);
-	}
-
-	protected boolean isEmptyNode(Node node) {
-		// comment node or empty text node
-		return node.getNodeName().equals("#comment") ||
-				(node.getNodeName().equals("#text") && node.getNodeValue().trim().length() == 0);
-	}
-
-	protected void validateTagsOnlyOnce(Node parentNode, String[] allowedChildNames) throws RrdException {
-		// validate node only once
-		if (validatedNodes.contains(parentNode)) {
-			return;
-		}
-		Node[] childs = getChildNodes(parentNode);
-		main:
-		for (Node child : childs) {
-			String childName = child.getNodeName();
-			for (int j = 0; j < allowedChildNames.length; j++) {
-				if (allowedChildNames[j].equals(childName)) {
-					// only one such tag is allowed
-					allowedChildNames[j] = "<--removed-->";
-					continue main;
-				}
-				else if (allowedChildNames[j].equals(childName + "*")) {
-					// several tags allowed
-					continue main;
-				}
-			}
-			if (!isEmptyNode(child)) {
-				throw new RrdException("Unexpected tag encountered: <" + childName + ">");
-			}
-		}
-		// everything is OK
-		validatedNodes.add(parentNode);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/XmlWriter.java b/apps/jrobin/java/src/org/jrobin/core/XmlWriter.java
deleted file mode 100644
index bda0ea080d0b6e5ef5b46b19d4ebb084ec3ae59d..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/XmlWriter.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core;
-
-import java.awt.*;
-import java.io.File;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.Stack;
-
-/**
- * Extremely simple utility class used to create XML documents.
- */
-public class XmlWriter {
-	static final String INDENT_STR = "   ";
-
-	private PrintWriter writer;
-	private StringBuffer indent = new StringBuffer("");
-	private Stack<String> openTags = new Stack<String>();
-
-	/**
-	 * Creates XmlWriter with the specified output stream to send XML code to.
-	 *
-	 * @param stream Output stream which receives XML code
-	 */
-	public XmlWriter(OutputStream stream) {
-		writer = new PrintWriter(stream, true);
-	}
-
-	/**
-	 * Opens XML tag
-	 *
-	 * @param tag XML tag name
-	 */
-	public void startTag(String tag) {
-		writer.println(indent + "<" + tag + ">");
-		openTags.push(tag);
-		indent.append(INDENT_STR);
-	}
-
-	/**
-	 * Closes the corresponding XML tag
-	 */
-	public void closeTag() {
-		String tag = openTags.pop();
-		indent.setLength(indent.length() - INDENT_STR.length());
-		writer.println(indent + "</" + tag + ">");
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 */
-	public void writeTag(String tag, Object value) {
-		if (value != null) {
-			writer.println(indent + "<" + tag + ">" +
-					escape(value.toString()) + "</" + tag + ">");
-		}
-		else {
-			writer.println(indent + "<" + tag + "></" + tag + ">");
-		}
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 */
-	public void writeTag(String tag, int value) {
-		writeTag(tag, "" + value);
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 */
-	public void writeTag(String tag, long value) {
-		writeTag(tag, "" + value);
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 * @param nanString string to display if the value is NaN.
-	 */
-	public void writeTag(String tag, double value, String nanString) {
-		writeTag(tag, Util.formatDouble(value, nanString, true));
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 */
-	public void writeTag(String tag, double value) {
-		writeTag(tag, Util.formatDouble(value, true));
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 */
-	public void writeTag(String tag, boolean value) {
-		writeTag(tag, "" + value);
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 */
-	public void writeTag(String tag, Color value) {
-		int rgb = value.getRGB() & 0xFFFFFF;
-		writeTag(tag, "#" + Integer.toHexString(rgb).toUpperCase());
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 */
-	public void writeTag(String tag, Font value) {
-		startTag(tag);
-		writeTag("name", value.getName());
-		int style = value.getStyle();
-		if ((style & Font.BOLD) != 0 && (style & Font.ITALIC) != 0) {
-			writeTag("style", "BOLDITALIC");
-		}
-		else if ((style & Font.BOLD) != 0) {
-			writeTag("style", "BOLD");
-		}
-		else if ((style & Font.ITALIC) != 0) {
-			writeTag("style", "ITALIC");
-		}
-		else {
-			writeTag("style", "PLAIN");
-		}
-		writeTag("size", value.getSize());
-		closeTag();
-	}
-
-	/**
-	 * Writes &lt;tag&gt;value&lt;/tag&gt; to output stream
-	 *
-	 * @param tag   XML tag name
-	 * @param value value to be placed between <code>&lt;tag&gt;</code> and <code>&lt;/tag&gt;</code>
-	 */
-	public void writeTag(String tag, File value) {
-		writeTag(tag, value.getPath());
-	}
-
-	/**
-	 * Flushes the output stream
-	 */
-	public void flush() {
-		writer.flush();
-	}
-
-	protected void finalize() throws Throwable {
-		super.finalize();
-		writer.close();
-	}
-
-	/**
-	 * Writes XML comment to output stream
-	 *
-	 * @param comment comment string
-	 */
-	public void writeComment(Object comment) {
-		writer.println(indent + "<!-- " + escape(comment.toString()) + " -->");
-	}
-
-	private static String escape(String s) {
-		return s.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/Archive.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/Archive.java
deleted file mode 100644
index 92da227094f973b8759dc82c2dd8505c3f955ab1..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/Archive.java
+++ /dev/null
@@ -1,426 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Iterator;
-
-import org.jrobin.core.RrdException;
-
-/**
- * Instances of this class model an archive section of an RRD file.
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class Archive {
-
-	RRDatabase db;
-	long offset;
-	long dataOffset;
-	long size;
-	ConsolidationFunctionType type;
-	int rowCount;
-	int pdpCount;
-	double xff;
-	ArrayList<CDPStatusBlock> cdpStatusBlocks;
-	int currentRow;
-
-	private double[][] values;
-
-	Archive(RRDatabase db) throws IOException,RrdException {
-
-		this.db = db;
-
-		RRDFile file = db.rrdFile;
-
-		offset = file.getFilePointer();
-		type =
-				ConsolidationFunctionType.get(file.readString(Constants.CF_NAM_SIZE));
-		rowCount = file.readInt();
-		pdpCount = file.readInt();
-
-		file.align();
-
-		xff = file.readDouble();
-
-		// Skip rest of rra_def_t.par[]
-		file.align();
-		file.skipBytes(72);
-
-		size = file.getFilePointer() - offset;
-	}
-
-	/**
-	 * Returns the type of function used to calculate the consolidated data point.
-	 *
-	 * @return the type of function used to calculate the consolidated data point.
-	 */
-	public ConsolidationFunctionType getType() {
-		return type;
-	}
-
-	void loadCDPStatusBlocks(RRDFile file, int numBlocks) throws IOException, RrdException {
-
-		cdpStatusBlocks = new ArrayList<CDPStatusBlock>();
-
-		for (int i = 0; i < numBlocks; i++) {
-			cdpStatusBlocks.add(new CDPStatusBlock(file));
-		}
-	}
-
-	/**
-	 * Returns the <code>CDPStatusBlock</code> at the specified position in this archive.
-	 *
-	 * @param index index of <code>CDPStatusBlock</code> to return.
-	 * @return the <code>CDPStatusBlock</code> at the specified position in this archive.
-	 */
-	public CDPStatusBlock getCDPStatusBlock(int index) {
-		return cdpStatusBlocks.get(index);
-	}
-
-	/**
-	 * Returns an iterator over the CDP status blocks in this archive in proper sequence.
-	 *
-	 * @return an iterator over the CDP status blocks in this archive in proper sequence.
-	 * @see CDPStatusBlock
-	 */
-	public Iterator<CDPStatusBlock> getCDPStatusBlocks() {
-		return cdpStatusBlocks.iterator();
-	}
-
-	void loadCurrentRow(RRDFile file) throws IOException,RrdException {
-		currentRow = file.readInt();
-	}
-
-	void loadData(RRDFile file, int dsCount) throws IOException {
-
-		dataOffset = file.getFilePointer();
-
-		// Skip over the data to position ourselves at the start of the next archive
-		file.skipBytes(8 * rowCount * dsCount);
-	}
-
-	DataChunk loadData(DataChunk chunk) throws IOException,RrdException {
-
-		Calendar end = Calendar.getInstance();
-		Calendar start = (Calendar) end.clone();
-
-		start.add(Calendar.DATE, -1);
-
-		loadData(chunk, start.getTime().getTime() / 1000,
-				end.getTime().getTime() / 1000);
-		return chunk;
-	}
-
-	void loadData(DataChunk chunk, long startTime, long endTime)
-			throws IOException,RrdException {
-
-		long pointer;
-
-		if (chunk.start < 0) {
-			pointer = currentRow + 1;
-		}
-		else {
-			pointer = currentRow + chunk.start + 1;
-		}
-
-		db.rrdFile.ras.seek(dataOffset + (pointer * 8));
-		//cat.debug("Archive Base: " + dataOffset + " Archive Pointer: " + pointer);
-		//cat.debug("Start Offset: " + chunk.start + " End Offset: "
-		//          + (rowCount - chunk.end));
-
-		double[][] data = chunk.data;
-
-		/*
-		 * This is also terrible - cleanup - CT
-		 */
-		int row = 0;
-		for (int i = chunk.start; i < rowCount - chunk.end; i++, row++) {
-			if (i < 0) {				   // no valid data yet
-				for (int ii = 0; ii < chunk.dsCount; ii++) {
-					data[row][ii] = Double.NaN;
-				}
-			}
-			else if (i >= rowCount) {	// past valid data area
-				for (int ii = 0; ii < chunk.dsCount; ii++) {
-					data[row][ii] = Double.NaN;
-				}
-			}
-			else {					   // inside the valid are but the pointer has to be wrapped
-				if (pointer >= rowCount) {
-					pointer -= rowCount;
-
-					db.rrdFile.ras.seek(dataOffset + (pointer * 8));
-				}
-
-				for (int ii = 0; ii < chunk.dsCount; ii++) {
-					data[row][ii] = db.rrdFile.readDouble();
-				}
-
-				pointer++;
-			}
-		}
-	}
-
-	void printInfo(PrintStream s, NumberFormat numberFormat, int index) {
-
-		StringBuffer sb = new StringBuffer("rra[");
-
-		sb.append(index);
-		s.print(sb);
-		s.print("].cf = \"");
-		s.print(type);
-		s.println("\"");
-		s.print(sb);
-		s.print("].rows = ");
-		s.println(rowCount);
-		s.print(sb);
-		s.print("].pdp_per_row = ");
-		s.println(pdpCount);
-		s.print(sb);
-		s.print("].xff = ");
-		s.println(xff);
-		sb.append("].cdp_prep[");
-
-		int cdpIndex = 0;
-
-		for (Iterator<CDPStatusBlock> i = cdpStatusBlocks.iterator(); i.hasNext();) {
-			CDPStatusBlock cdp = i.next();
-
-			s.print(sb);
-			s.print(cdpIndex);
-			s.print("].value = ");
-
-			double value = cdp.value;
-
-			s.println(Double.isNaN(value)
-					? "NaN"
-					: numberFormat.format(value));
-			s.print(sb);
-			s.print(cdpIndex++);
-			s.print("].unknown_datapoints = ");
-			s.println(cdp.unknownDatapoints);
-		}
-	}
-
-	void toXml(PrintStream s) throws RrdException {
-
-		try {
-			s.println("\t<rra>");
-			s.print("\t\t<cf> ");
-			s.print(type);
-			s.println(" </cf>");
-			s.print("\t\t<pdp_per_row> ");
-			s.print(pdpCount);
-			s.print(" </pdp_per_row> <!-- ");
-			s.print(db.header.pdpStep * pdpCount);
-			s.println(" seconds -->");
-			s.print("\t\t<xff> ");
-			s.print(xff);
-			s.println(" </xff>");
-			s.println();
-			s.println("\t\t<cdp_prep>");
-
-			for (int i = 0; i < cdpStatusBlocks.size(); i++) {
-				cdpStatusBlocks.get(i).toXml(s);
-			}
-
-			s.println("\t\t</cdp_prep>");
-			s.println("\t\t<database>");
-
-			long timer = -(rowCount - 1);
-			int counter = 0;
-			int row = currentRow;
-
-			db.rrdFile.ras.seek(dataOffset + (row + 1) * 16);
-
-			long lastUpdate = db.lastUpdate.getTime() / 1000;
-			int pdpStep = db.header.pdpStep;
-			NumberFormat numberFormat = new DecimalFormat("0.0000000000E0");
-			SimpleDateFormat dateFormat =
-					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
-
-			while (counter++ < rowCount) {
-				row++;
-
-				if (row == rowCount) {
-					row = 0;
-
-					db.rrdFile.ras.seek(dataOffset);
-				}
-
-				long now = (lastUpdate - lastUpdate % (pdpCount * pdpStep))
-						+ (timer * pdpCount * pdpStep);
-
-				timer++;
-
-				s.print("\t\t\t<!-- ");
-				s.print(dateFormat.format(new Date(now * 1000)));
-				s.print(" / ");
-				s.print(now);
-				s.print(" --> ");
-
-				for (int col = 0; col < db.header.dsCount; col++) {
-					s.print("<v> ");
-
-					double value = db.rrdFile.readDouble();
-
-					// NumberFormat doesn't know how to handle NaN
-					if (Double.isNaN(value)) {
-						s.print("NaN");
-					}
-					else {
-						s.print(numberFormat.format(value));
-					}
-
-					s.print(" </v>");
-				}
-
-				s.println("</row>");
-			}
-
-			s.println("\t\t</database>");
-			s.println("\t</rra>");
-		}
-		catch (IOException e) {	// Is the best thing to do here?
-			throw new RuntimeException(e.getMessage());
-		}
-	}
-
-	/*
-	// THIS IS THE ORIGINAL CODE: BUGGY! Replaced by Sasa Markovic with a new method
-	// Funny: the bug will appear only if dsCount != 2 :)
-	public double[][] getValuesOriginal() throws IOException {
-		if (values != null) {
-			return values;
-		}
-		values = new double[db.header.dsCount][rowCount];
-		int row = currentRow;
-		db.rrdFile.ras.seek(dataOffset + (row + 1) * 16); // <----- BUG (resolved below)
-		for (int counter = 0; counter < rowCount; counter++) {
-			row++;
-			if (row == rowCount) {
-				row = 0;
-				db.rrdFile.ras.seek(dataOffset);
-			}
-			for (int col = 0; col < db.header.dsCount; col++) {
-				double value = db.rrdFile.readDouble();
-				values[col][counter] = value;
-			}
-		}
-		return values;
-	}
-    */
-
-	// Resolved bug from the original method (see above)
-	public double[][] getValues() throws IOException,RrdException {
-		// OK PART
-		if (values != null) {
-			return values;
-		}
-		values = new double[db.header.dsCount][rowCount];
-		int row = currentRow;
-		// HERE ARE THE DRAGONS!
-		db.rrdFile.ras.seek(dataOffset + (row + 1) * db.header.dsCount * 8);
-		// OK, TOO!
-		for (int counter = 0; counter < rowCount; counter++) {
-			row++;
-			if (row == rowCount) {
-				row = 0;
-				db.rrdFile.ras.seek(dataOffset);
-			}
-			for (int col = 0; col < db.header.dsCount; col++) {
-				double value = db.rrdFile.readDouble();
-				values[col][counter] = value;
-			}
-		}
-		return values;
-	}
-
-	/**
-	 * Returns the number of primary data points required for a consolidated
-	 * data point in this archive.
-	 *
-	 * @return the number of primary data points required for a consolidated
-	 *         data point in this archive.
-	 */
-	public int getPdpCount() {
-		return pdpCount;
-	}
-
-	/**
-	 * Returns the number of entries in this archive.
-	 *
-	 * @return the number of entries in this archive.
-	 */
-	public int getRowCount() {
-		return rowCount;
-	}
-
-	/**
-	 * Returns the X-Files Factor for this archive.
-	 *
-	 * @return the X-Files Factor for this archive.
-	 */
-	public double getXff() {
-		return xff;
-	}
-
-	/**
-	 * Returns a summary the contents of this archive.
-	 *
-	 * @return a summary of the information contained in this archive.
-	 */
-	public String toString() {
-
-		StringBuffer sb = new StringBuffer("[Archive: OFFSET=0x");
-
-		sb.append(Long.toHexString(offset));
-		sb.append(", SIZE=0x");
-		sb.append(Long.toHexString(size));
-		sb.append(", type=");
-		sb.append(type);
-		sb.append(", rowCount=");
-		sb.append(rowCount);
-		sb.append(", pdpCount=");
-		sb.append(pdpCount);
-		sb.append(", xff=");
-		sb.append(xff);
-		sb.append(", currentRow=");
-		sb.append(currentRow);
-		sb.append("]");
-
-		for (Iterator<CDPStatusBlock> i = cdpStatusBlocks.iterator(); i.hasNext();) {
-			CDPStatusBlock cdp = i.next();
-
-			sb.append("\n\t\t");
-			sb.append(cdp.toString());
-		}
-
-		return sb.toString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/CDPStatusBlock.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/CDPStatusBlock.java
deleted file mode 100644
index f46ab808af3ce4dda8a0600e86fda311a4769f87..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/CDPStatusBlock.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-import java.io.IOException;
-import java.io.PrintStream;
-
-import org.jrobin.core.RrdException;
-
-/**
- * Instances of this class model the consolidation data point status from an RRD file.
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class CDPStatusBlock {
-
-	long offset;
-	long size;
-	int unknownDatapoints;
-	double value;
-
-	CDPStatusBlock(RRDFile file) throws IOException, RrdException {
-
-		offset = file.getFilePointer();
-		value = file.readDouble();
-		unknownDatapoints = file.readInt();
-		file.align(8);
-		// Skip rest of cdp_prep_t.scratch
-		file.skipBytes(64);
-
-		size = file.getFilePointer() - offset;
-	}
-
-	/**
-	 * Returns the number of unknown primary data points that were integrated.
-	 *
-	 * @return the number of unknown primary data points that were integrated.
-	 */
-	public int getUnknownDatapoints() {
-		return unknownDatapoints;
-	}
-
-	/**
-	 * Returns the value of this consolidated data point.
-	 *
-	 * @return the value of this consolidated data point.
-	 */
-	public double getValue() {
-		return value;
-	}
-
-	void toXml(PrintStream s) {
-
-		s.print("\t\t\t<ds><value> ");
-		s.print(value);
-		s.print(" </value>  <unknown_datapoints> ");
-		s.print(unknownDatapoints);
-		s.println(" </unknown_datapoints></ds>");
-	}
-
-	/**
-	 * Returns a summary the contents of this CDP status block.
-	 *
-	 * @return a summary of the information contained in the CDP status block.
-	 */
-	public String toString() {
-
-		StringBuffer sb = new StringBuffer("[CDPStatusBlock: OFFSET=0x");
-
-		sb.append(Long.toHexString(offset));
-		sb.append(", SIZE=0x");
-		sb.append(Long.toHexString(size));
-		sb.append(", unknownDatapoints=");
-		sb.append(unknownDatapoints);
-		sb.append(", value=");
-		sb.append(value);
-		sb.append("]");
-
-		return sb.toString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/ConsolidationFunctionType.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/ConsolidationFunctionType.java
deleted file mode 100644
index e5f98da739a4e3cd6ca84b3b2084de1d0c15fb82..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/ConsolidationFunctionType.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-/**
- * Class ConsolidationFunctionType
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class ConsolidationFunctionType {
-
-	private static final int _AVERAGE = 0;
-	private static final String STR_AVERAGE = "AVERAGE";
-
-	/**
-	 * Field AVERAGE
-	 */
-	public static final ConsolidationFunctionType AVERAGE = new ConsolidationFunctionType(_AVERAGE);
-	private static final int _MIN = 1;
-	private static final String STR_MIN = "MIN";
-
-	/**
-	 * Field MIN
-	 */
-	public static final ConsolidationFunctionType MIN = new ConsolidationFunctionType(_MIN);
-	private static final int _MAX = 2;
-	private static final String STR_MAX = "MAX";
-
-	/**
-	 * Field MAX
-	 */
-	public static final ConsolidationFunctionType MAX = new ConsolidationFunctionType(_MAX);
-	private static final int _LAST = 3;
-	private static final String STR_LAST = "LAST";
-
-	/**
-	 * Field LAST
-	 */
-	public static final ConsolidationFunctionType LAST = new ConsolidationFunctionType(_LAST);
-	private int type;
-
-	private ConsolidationFunctionType(int type) {
-		this.type = type;
-	}
-
-	/**
-	 * Returns a <code>ConsolidationFunctionType</code> with the given name.
-	 *
-	 * @param s name of the <code>ConsolidationFunctionType</code> required.
-	 * @return a <code>ConsolidationFunctionType</code> with the given name.
-	 */
-	public static ConsolidationFunctionType get(final String s) {
-
-		if (STR_AVERAGE.equalsIgnoreCase(s)) {
-			return AVERAGE;
-		}
-		else if (STR_MIN.equalsIgnoreCase(s)) {
-			return MIN;
-		}
-		else if (STR_MAX.equalsIgnoreCase(s)) {
-			return MAX;
-		}
-		else if (STR_LAST.equalsIgnoreCase(s)) {
-			return LAST;
-		}
-		else {
-			throw new IllegalArgumentException("Invalid ConsolidationFunctionType: " + s);
-		}
-	}
-
-	/**
-	 * Compares this object against the specified object.
-	 *
-	 * @return <code>true</code> if the objects are the same,
-	 *         <code>false</code> otherwise.
-	 */
-	public boolean equals(final Object o) {
-
-		if (!(o instanceof ConsolidationFunctionType)) {
-			throw new IllegalArgumentException("Not a ConsolidationFunctionType");
-		}
-
-		return (((ConsolidationFunctionType) o).type == type)
-				? true
-				: false;
-	}
-
-	public int hashCode() {
-		return type * 93;
-	}
-
-	/**
-	 * Returns a string representation of this object.
-	 *
-	 * @return a string representation of this object.
-	 */
-	public String toString() {
-
-		String strType;
-
-		switch (type) {
-
-			case _AVERAGE:
-				strType = STR_AVERAGE;
-				break;
-
-			case _MIN:
-				strType = STR_MIN;
-				break;
-
-			case _MAX:
-				strType = STR_MAX;
-				break;
-
-			case _LAST:
-				strType = STR_LAST;
-				break;
-
-			default :
-				throw new RuntimeException("This should never happen");
-		}
-
-		return strType;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/Constants.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/Constants.java
deleted file mode 100644
index c7f6ca3a531950c5759d80c6b923fcfe75a98337..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/Constants.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-interface Constants {
-
-	int DS_NAM_SIZE = 20;
-	int DST_SIZE = 20;
-	int CF_NAM_SIZE = 20;
-	int LAST_DS_LEN = 30;
-	static String COOKIE = "RRD";
-	static String VERSION = "0001";
-	static String VERSION3 = "0003";
-	double FLOAT_COOKIE = 8.642135E130;
-	static byte[] FLOAT_COOKIE_BIG_ENDIAN = {0x5B, 0x1F, 0x2B, 0x43,
-			(byte) 0xC7, (byte) 0xC0, 0x25,
-			0x2F};
-	static byte[] FLOAT_COOKIE_LITTLE_ENDIAN = {0x2F, 0x25, (byte) 0xC0,
-			(byte) 0xC7, 0x43, 0x2B, 0x1F,
-			0x5B};
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/DataChunk.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/DataChunk.java
deleted file mode 100644
index 48b5a6aa6737bf7a0dcecdabe78692e6a1102749..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/DataChunk.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-/**
- * Models a chunk of result data from an RRDatabase.
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class DataChunk {
-
-	private static final String NEWLINE = System.getProperty("line.separator");
-	long startTime;
-	int start;
-	int end;
-	long step;
-	int dsCount;
-	double[][] data;
-	int rows;
-
-	DataChunk(long startTime, int start, int end, long step, int dsCount, int rows) {
-		this.startTime = startTime;
-		this.start = start;
-		this.end = end;
-		this.step = step;
-		this.dsCount = dsCount;
-		this.rows = rows;
-		data = new double[rows][dsCount];
-	}
-
-	/**
-	 * Returns a summary of the contents of this data chunk. The first column is
-	 * the time (RRD format) and the following columns are the data source
-	 * values.
-	 *
-	 * @return a summary of the contents of this data chunk.
-	 */
-	public String toString() {
-
-		StringBuffer sb = new StringBuffer();
-		long time = startTime;
-
-		for (int row = 0; row < rows; row++, time += step) {
-			sb.append(time);
-			sb.append(": ");
-
-			for (int ds = 0; ds < dsCount; ds++) {
-				sb.append(data[row][ds]);
-				sb.append(" ");
-			}
-
-			sb.append(NEWLINE);
-		}
-
-		return sb.toString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/DataSource.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/DataSource.java
deleted file mode 100644
index ac90eca33105d8006e4c3a2513ecc1e9c8c18dc5..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/DataSource.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.text.NumberFormat;
-
-import org.jrobin.core.RrdException;
-
-/**
- * Instances of this class model a data source in an RRD file.
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class DataSource {
-
-	long offset;
-	long size;
-	String name;
-	DataSourceType type;
-	int minimumHeartbeat;
-	double minimum;
-	double maximum;
-	PDPStatusBlock pdpStatusBlock;
-
-	DataSource(RRDFile file) throws IOException,RrdException {
-
-		offset = file.getFilePointer();
-		name = file.readString(Constants.DS_NAM_SIZE);
-		type = DataSourceType.get(file.readString(Constants.DST_SIZE));
-
-		file.align(8);
-
-		minimumHeartbeat = file.readInt(true);
-
-		file.align(8);
-
-		minimum = file.readDouble();
-		maximum = file.readDouble();
-
-		// Skip rest of ds_def_t.par[]
-		file.align();
-		file.skipBytes(56);
-
-		size = file.getFilePointer() - offset;
-	}
-
-	void loadPDPStatusBlock(RRDFile file) throws IOException,RrdException {
-		pdpStatusBlock = new PDPStatusBlock(file);
-	}
-
-	/**
-	 * Returns the primary data point status block for this data source.
-	 *
-	 * @return the primary data point status block for this data source.
-	 */
-	public PDPStatusBlock getPDPStatusBlock() {
-		return pdpStatusBlock;
-	}
-
-	/**
-	 * Returns the minimum required heartbeat for this data source.
-	 *
-	 * @return the minimum required heartbeat for this data source.
-	 */
-	public int getMinimumHeartbeat() {
-		return minimumHeartbeat;
-	}
-
-	/**
-	 * Returns the minimum value input to this data source can have.
-	 *
-	 * @return the minimum value input to this data source can have.
-	 */
-	public double getMinimum() {
-		return minimum;
-	}
-
-	/**
-	 * Returns the type this data source is.
-	 *
-	 * @return the type this data source is.
-	 * @see DataSourceType
-	 */
-	public DataSourceType getType() {
-		return type;
-	}
-
-	/**
-	 * Returns the maximum value input to this data source can have.
-	 *
-	 * @return the maximum value input to this data source can have.
-	 */
-	public double getMaximum() {
-		return maximum;
-	}
-
-	/**
-	 * Returns the name of this data source.
-	 *
-	 * @return the name of this data source.
-	 */
-	public String getName() {
-		return name;
-	}
-
-	void printInfo(PrintStream s, NumberFormat numberFormat) {
-
-		StringBuffer sb = new StringBuffer("ds[");
-
-		sb.append(name);
-		s.print(sb);
-		s.print("].type = \"");
-		s.print(type);
-		s.println("\"");
-		s.print(sb);
-		s.print("].minimal_heartbeat = ");
-		s.println(minimumHeartbeat);
-		s.print(sb);
-		s.print("].min = ");
-		s.println(Double.isNaN(minimum)
-				? "NaN"
-				: numberFormat.format(minimum));
-		s.print(sb);
-		s.print("].max = ");
-		s.println(Double.isNaN(maximum)
-				? "NaN"
-				: numberFormat.format(maximum));
-		s.print(sb);
-		s.print("].last_ds = ");
-		s.println(pdpStatusBlock.lastReading);
-		s.print(sb);
-		s.print("].value = ");
-
-		double value = pdpStatusBlock.value;
-
-		s.println(Double.isNaN(value)
-				? "NaN"
-				: numberFormat.format(value));
-		s.print(sb);
-		s.print("].unknown_sec = ");
-		s.println(pdpStatusBlock.unknownSeconds);
-	}
-
-	void toXml(PrintStream s) {
-
-		s.println("\t<ds>");
-		s.print("\t\t<name> ");
-		s.print(name);
-		s.println(" </name>");
-		s.print("\t\t<type> ");
-		s.print(type);
-		s.println(" </type>");
-		s.print("\t\t<minimal_heartbeat> ");
-		s.print(minimumHeartbeat);
-		s.println(" </minimal_heartbeat>");
-		s.print("\t\t<min> ");
-		s.print(minimum);
-		s.println(" </min>");
-		s.print("\t\t<max> ");
-		s.print(maximum);
-		s.println(" </max>");
-		s.println();
-		s.println("\t\t<!-- PDP Status -->");
-		s.print("\t\t<last_ds> ");
-		s.print(pdpStatusBlock.lastReading);
-		s.println(" </last_ds>");
-		s.print("\t\t<value> ");
-		s.print(pdpStatusBlock.value);
-		s.println(" </value>");
-		s.print("\t\t<unknown_sec> ");
-		s.print(pdpStatusBlock.unknownSeconds);
-		s.println(" </unknown_sec>");
-		s.println("\t</ds>");
-		s.println();
-	}
-
-	/**
-	 * Returns a summary the contents of this data source.
-	 *
-	 * @return a summary of the information contained in this data source.
-	 */
-	public String toString() {
-
-		StringBuffer sb = new StringBuffer("[DataSource: OFFSET=0x");
-
-		sb.append(Long.toHexString(offset));
-		sb.append(", SIZE=0x");
-		sb.append(Long.toHexString(size));
-		sb.append(", name=");
-		sb.append(name);
-		sb.append(", type=");
-		sb.append(type.toString());
-		sb.append(", minHeartbeat=");
-		sb.append(minimumHeartbeat);
-		sb.append(", min=");
-		sb.append(minimum);
-		sb.append(", max=");
-		sb.append(maximum);
-		sb.append("]");
-		sb.append("\n\t\t");
-		sb.append(pdpStatusBlock.toString());
-
-		return sb.toString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/DataSourceType.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/DataSourceType.java
deleted file mode 100644
index 336e115b81bfa1779e4cd0d7bef1e1c6e6d5d101..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/DataSourceType.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-/**
- * Class DataSourceType
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class DataSourceType {
-
-	private static final int _COUNTER = 0;
-	private static final String STR_COUNTER = "COUNTER";
-
-	/**
-	 * Field COUNTER
-	 */
-	public static final DataSourceType COUNTER = new DataSourceType(_COUNTER);
-	private static final int _ABSOLUTE = 1;
-	private static final String STR_ABSOLUTE = "ABSOLUTE";
-
-	/**
-	 * Field ABSOLUTE
-	 */
-	public static final DataSourceType ABSOLUTE = new DataSourceType(_ABSOLUTE);
-	private static final int _GAUGE = 2;
-	private static final String STR_GAUGE = "GAUGE";
-
-	/**
-	 * Field GAUGE
-	 */
-	public static final DataSourceType GAUGE = new DataSourceType(_GAUGE);
-	private static final int _DERIVE = 3;
-	private static final String STR_DERIVE = "DERIVE";
-
-	/**
-	 * Field DERIVE
-	 */
-	public static final DataSourceType DERIVE = new DataSourceType(_DERIVE);
-	private int type;
-
-	private DataSourceType(final int type) {
-		this.type = type;
-	}
-
-	/**
-	 * Returns a <code>DataSourceType</code> with the given name.
-	 *
-	 * @param s name of the <code>DataSourceType</code> required.
-	 * @return a <code>DataSourceType</code> with the given name.
-	 */
-	public static DataSourceType get(final String s) {
-
-		if (STR_COUNTER.equalsIgnoreCase(s)) {
-			return COUNTER;
-		}
-		else if (STR_ABSOLUTE.equalsIgnoreCase(s)) {
-			return ABSOLUTE;
-		}
-		else if (STR_GAUGE.equalsIgnoreCase(s)) {
-			return GAUGE;
-		}
-		else if (STR_DERIVE.equalsIgnoreCase(s)) {
-			return DERIVE;
-		}
-		else {
-			throw new IllegalArgumentException("Invalid DataSourceType");
-		}
-	}
-
-	/**
-	 * Compares this object against the specified object.
-	 *
-	 * @return <code>true</code> if the objects are the same,
-	 *         <code>false</code> otherwise.
-	 */
-	public boolean equals(final Object obj) {
-
-		if (!(obj instanceof DataSourceType)) {
-			throw new IllegalArgumentException("Not a DataSourceType");
-		}
-
-		return (((DataSourceType) obj).type == type)
-				? true
-				: false;
-	}
-
-	public int hashCode() {
-		return type * 37;
-	}
-
-	/**
-	 * Returns a string representation of this object.
-	 *
-	 * @return a string representation of this object.
-	 */
-	public String toString() {
-
-		String strType;
-
-		switch (type) {
-
-			case _COUNTER:
-				strType = STR_COUNTER;
-				break;
-
-			case _ABSOLUTE:
-				strType = STR_ABSOLUTE;
-				break;
-
-			case _GAUGE:
-				strType = STR_GAUGE;
-				break;
-
-			case _DERIVE:
-				strType = STR_DERIVE;
-				break;
-
-			default :
-				// Don't you just hate it when you see a line like this?
-				throw new RuntimeException("This should never happen");
-		}
-
-		return strType;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/Header.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/Header.java
deleted file mode 100644
index c301e3acebcf6fdaef437a0b133f3974b50f235d..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/Header.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-import java.io.IOException;
-
-import org.jrobin.core.RrdException;
-
-/**
- * Instances of this class model the header section of an RRD file.
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class Header implements Constants {
-
-	static final long offset = 0;
-	long size;
-	String version;
-	int intVersion;
-	int dsCount;
-	int rraCount;
-	int pdpStep;
-
-	Header(RRDFile file) throws IOException,RrdException {
-
-		if (!file.readString(4).equals(COOKIE)) {
-			throw new IOException("Invalid COOKIE");
-		}
-
-		version = file.readString(5);
-		intVersion = Integer.parseInt(version);
-		if( intVersion > 3 ) {
-			throw new IOException("Unsupported RRD version (" + version + ")");
-		}
-
-		file.align();
-
-		// Consume the FLOAT_COOKIE
-		file.readDouble();
-
-		dsCount = file.readInt();
-		rraCount = file.readInt();
-		pdpStep = file.readInt();
-
-		// Skip rest of stat_head_t.par
-		file.align();
-		file.skipBytes(80);
-
-		size = file.getFilePointer() - offset;
-	}
-
-	/**
-	 * Returns the version of the database.
-	 *
-	 * @return the version of the database.
-	 */
-	public String getVersion() {
-		return version;
-	}
-
-	public int getIntVersion() {
-		return intVersion;
-	}
-
-	/**
-	 * Returns the number of <code>DataSource</code>s in the database.
-	 *
-	 * @return the number of <code>DataSource</code>s in the database.
-	 */
-	public int getDSCount() {
-		return dsCount;
-	}
-
-	/**
-	 * Returns the number of <code>Archive</code>s in the database.
-	 *
-	 * @return the number of <code>Archive</code>s in the database.
-	 */
-	public int getRRACount() {
-		return rraCount;
-	}
-
-	/**
-	 * Returns the primary data point interval in seconds.
-	 *
-	 * @return the primary data point interval in seconds.
-	 */
-	public int getPDPStep() {
-		return pdpStep;
-	}
-
-	/**
-	 * Returns a summary the contents of this header.
-	 *
-	 * @return a summary of the information contained in this header.
-	 */
-	public String toString() {
-
-		StringBuffer sb = new StringBuffer("[Header: OFFSET=0x00, SIZE=0x");
-
-		sb.append(Long.toHexString(size));
-		sb.append(", version=");
-		sb.append(version);
-		sb.append(", dsCount=");
-		sb.append(dsCount);
-		sb.append(", rraCount=");
-		sb.append(rraCount);
-		sb.append(", pdpStep=");
-		sb.append(pdpStep);
-		sb.append("]");
-
-		return sb.toString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/Main.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/Main.java
deleted file mode 100644
index c7f17b7a8983cc8e9cdd2fad7df425fac505f1ac..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/Main.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-import java.io.IOException;
-
-import org.jrobin.core.RrdException;
-
-/**
- * Show some of the things jRRD can do.
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class Main {
-
-	public Main(String rrdFile) {
-
-		RRDatabase rrd = null;
-		DataChunk chunk = null;
-
-		try {
-			rrd = new RRDatabase(rrdFile);
-			chunk = rrd.getData(ConsolidationFunctionType.AVERAGE);
-		} catch (Exception e) {
-			e.printStackTrace();
-
-			return;
-		}
-
-		try {
-			rrd.toXml(System.out);
-		} catch (RrdException e) {
-			e.printStackTrace();
-			return;
-		}
-		// Dump the database as XML.
-		rrd.printInfo(System.out);    // Dump the database header information.
-		System.out.println(rrd);      // Dump a summary of the contents of the database.
-		System.out.println(chunk);    // Dump the chunk.
-
-		try {
-			rrd.close();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
-
-	static void usage(int status) {
-		System.err.println("Usage: " + Main.class.getName() + " rrdfile");
-		System.exit(status);
-	}
-
-	public static void main(String[] args) {
-		if (args.length != 1) {
-			usage(1);
-		}
-		new Main(args[0]);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/PDPStatusBlock.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/PDPStatusBlock.java
deleted file mode 100644
index 449897bf67d43d8b0f35f42672991589c771a9b6..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/PDPStatusBlock.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-import java.io.IOException;
-
-import org.jrobin.core.RrdException;
-
-/**
- * Instances of this class model the primary data point status from an RRD file.
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class PDPStatusBlock {
-
-	long offset;
-	long size;
-	String lastReading;
-	int unknownSeconds;
-	double value;
-
-	PDPStatusBlock(RRDFile file) throws IOException,RrdException {
-
-		offset = file.getFilePointer();
-		lastReading = file.readString(Constants.LAST_DS_LEN);
-
-		file.align(4);
-
-		unknownSeconds = file.readInt();
-
-		file.align(8); //8 bytes per scratch value in pdp_prep; align on that
-
-		value = file.readDouble();
-
-		// Skip rest of pdp_prep_t.par[]
-		file.skipBytes(64);
-
-		size = file.getFilePointer() - offset;
-	}
-
-	/**
-	 * Returns the last reading from the data source.
-	 *
-	 * @return the last reading from the data source.
-	 */
-	public String getLastReading() {
-		return lastReading;
-	}
-
-	/**
-	 * Returns the current value of the primary data point.
-	 *
-	 * @return the current value of the primary data point.
-	 */
-	public double getValue() {
-		return value;
-	}
-
-	/**
-	 * Returns the number of seconds of the current primary data point is
-	 * unknown data.
-	 *
-	 * @return the number of seconds of the current primary data point is unknown data.
-	 */
-	public int getUnknownSeconds() {
-		return unknownSeconds;
-	}
-
-	/**
-	 * Returns a summary the contents of this PDP status block.
-	 *
-	 * @return a summary of the information contained in this PDP status block.
-	 */
-	public String toString() {
-
-		StringBuffer sb = new StringBuffer("[PDPStatus: OFFSET=0x");
-
-		sb.append(Long.toHexString(offset));
-		sb.append(", SIZE=0x");
-		sb.append(Long.toHexString(size));
-		sb.append(", lastReading=");
-		sb.append(lastReading);
-		sb.append(", unknownSeconds=");
-		sb.append(unknownSeconds);
-		sb.append(", value=");
-		sb.append(value);
-		sb.append("]");
-
-		return sb.toString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/RRDFile.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/RRDFile.java
deleted file mode 100644
index 7badf305e21282dce4b605d424958dc132895e61..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/RRDFile.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-
-import org.jrobin.core.RrdException;
-
-/**
- * This class is a quick hack to read information from an RRD file. Writing
- * to RRD files is not currently supported. As I said, this is a quick hack.
- * Some thought should be put into the overall design of the file IO.
- * <p>
- * Currently this can read RRD files that were generated on Solaris (Sparc)
- * and Linux (x86).
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class RRDFile implements Constants {
-
-	boolean bigEndian;
-	boolean debug;
-	int alignment;
-	RandomAccessFile ras;
-	byte[] buffer;
-
-	RRDFile(String name) throws IOException, RrdException {
-		this(new File(name));
-	}
-
-	RRDFile(File file) throws IOException, RrdException {
-
-		ras = new RandomAccessFile(file, "r");
-		buffer = new byte[128];
-
-		this.debug = false;
-		initDataLayout(file);
-	}
-
-	private void initDataLayout(File file) throws IOException, RrdException {
-
-		if (file.exists()) {	// Load the data formats from the file
-			int bytes = ras.read(buffer, 0, 24);
-			if (bytes < 24) {
-				throw new RrdException("Invalid RRD file");
-			}
-
-			int index;
-
-			if ((index = indexOf(FLOAT_COOKIE_BIG_ENDIAN, buffer)) != -1) {
-				bigEndian = true;
-			}
-			else if ((index = indexOf(FLOAT_COOKIE_LITTLE_ENDIAN, buffer))
-					!= -1) {
-				bigEndian = false;
-			}
-			else {
-				throw new RrdException("Invalid RRD file");
-			}
-
-			switch (index) {
-
-				case 12:
-					alignment = 4;
-					break;
-
-				case 16:
-					alignment = 8;
-					break;
-
-				default :
-					throw new RuntimeException("Unsupported architecture - neither 32-bit nor 64-bit, or maybe the file is corrupt");
-			}
-		}
-		else {				// Default to data formats for this hardware architecture
-		}
-
-		ras.seek(0);	// Reset file pointer to start of file
-	}
-
-	private int indexOf(byte[] pattern, byte[] array) {
-		return (new String(array)).indexOf(new String(pattern));
-	}
-
-	boolean isBigEndian() {
-		return bigEndian;
-	}
-
-	int getAlignment() {
-		return alignment;
-	}
-
-	double readDouble() throws IOException, RrdException {
-		if(debug) {
-			System.out.print("Read 8 bytes (Double) from offset "+ras.getFilePointer()+":");
-		}
-
-		//double value;
-		byte[] tx = new byte[8];
-
-		if(ras.read(buffer, 0, 8) != 8) {
-			throw new RrdException("Invalid RRD file");
-		}
-
-		if (bigEndian) {
-			tx = buffer;
-		}
-		else {
-			for (int i = 0; i < 8; i++) {
-				tx[7 - i] = buffer[i];
-			}
-		}
-
-		DataInputStream reverseDis =
-				new DataInputStream(new ByteArrayInputStream(tx));
-
-		Double result = reverseDis.readDouble();
-		if(this.debug) {
-			System.out.println(result);
-		}
-		return result;
-	}
-
-	int readInt() throws IOException, RrdException {
-		return readInt(false);
-	}
-
-	/**
-	 * Reads the next integer (4 or 8 bytes depending on alignment), advancing the file pointer
-	 *  and returns it
-	 *  If the alignment is 8-bytes (64-bit), then 8 bytes are read, but only the lower 4-bytes (32-bits) are
-	 *  returned.  The upper 4 bytes are ignored.
-	 *
-	 * @return the 32-bit integer read from the file
-	 * @throws IOException - A file access error
-	 * @throws RrdException - Not enough bytes were left in the file to read the integer.
-	 */
-	int readInt(boolean dump) throws IOException, RrdException {
-		//An integer is "alignment" bytes long - 4 bytes on 32-bit, 8 on 64-bit.
-		if(this.debug) {
-			System.out.print("Read "+alignment+" bytes (int) from offset "+ras.getFilePointer()+":");
-		}
-
-		if(ras.read(buffer, 0, alignment) != alignment) {
-			throw new RrdException("Invalid RRD file");
-		}
-
-		int value;
-
-		if (bigEndian) {
-			if(alignment == 8) {
-				//For big-endian, the low 4-bytes of the 64-bit integer are the last 4 bytes
-				value = (0xFF & buffer[7]) | ((0xFF & buffer[6]) << 8)
-						| ((0xFF & buffer[5]) << 16) | ((0xFF & buffer[4]) << 24);
-			} else {
-				value = (0xFF & buffer[3]) | ((0xFF & buffer[2]) << 8)
-						| ((0xFF & buffer[1]) << 16) | ((0xFF & buffer[0]) << 24);
-			}
-		}
-		else {
-			//For little-endian, there's no difference between 4 and 8 byte alignment.
-			// The first 4 bytes are the low end of a 64-bit number
-			value = (0xFF & buffer[0]) | ((0xFF & buffer[1]) << 8)
-				| ((0xFF & buffer[2]) << 16) | ((0xFF & buffer[3]) << 24);
-		}
-
-		if(this.debug) {
-			System.out.println(value);
-		}
-		return value;
-	}
-
-	String readString(int maxLength) throws IOException, RrdException {
-		if(this.debug) {
-			System.out.print("Read "+maxLength+" bytes (string) from offset "+ras.getFilePointer()+":");
-		}
-		maxLength = ras.read(buffer, 0, maxLength);
-		if(maxLength == -1) {
-			throw new RrdException("Invalid RRD file");
-		}
-
-		String result = new String(buffer, 0, maxLength).trim();
-		if(this.debug) {
-			System.out.println( result +":");
-		}
-		return result;
-	}
-
-	void skipBytes(final int n) throws IOException {
-		int bytesSkipped = ras.skipBytes(n);
-		if(this.debug) {
-			System.out.println("Skipping "+bytesSkipped+" bytes");
-		}
-	}
-
-	int align(int boundary) throws IOException {
-
-		int skip = (int) (boundary - (ras.getFilePointer() % boundary)) % boundary;
-
-		if (skip != 0) {
-			skip = ras.skipBytes(skip);
-		}
-		if(this.debug) {
-			System.out.println("Aligning to boundary "+ boundary +".  Offset is now "+ras.getFilePointer());
-		}
-		return skip;
-	}
-
-	int align() throws IOException {
-		return align(alignment);
-	}
-
-	long info() throws IOException {
-		return ras.getFilePointer();
-	}
-
-	long getFilePointer() throws IOException {
-		return ras.getFilePointer();
-	}
-
-	void close() throws IOException {
-		ras.close();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/jrrd/RRDatabase.java b/apps/jrobin/java/src/org/jrobin/core/jrrd/RRDatabase.java
deleted file mode 100644
index 0a39f1f1616a79264f42f40bac8ad42595b471c0..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/jrrd/RRDatabase.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.jrrd;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Iterator;
-
-import org.jrobin.core.RrdException;
-
-/**
- * Instances of this class model
- * <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/">Round Robin Database</a>
- * (RRD) files.
- *
- * @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
- * @version $Revision$
- */
-public class RRDatabase {
-
-	RRDFile rrdFile;
-
-	// RRD file name
-	private String name;
-	Header header;
-	ArrayList<DataSource> dataSources;
-	ArrayList<Archive> archives;
-	Date lastUpdate;
-
-	/**
-	 * Creates a database to read from.
-	 *
-	 * @param name the filename of the file to read from.
-	 * @throws IOException if an I/O error occurs.
-	 */
-	public RRDatabase(String name) throws IOException,RrdException {
-		this(new File(name));
-	}
-
-	/**
-	 * Creates a database to read from.
-	 *
-	 * @param file the file to read from.
-	 * @throws IOException if an I/O error occurs.
-	 */
-	public RRDatabase(File file) throws IOException,RrdException {
-
-		name = file.getName();
-		rrdFile = new RRDFile(file);
-		header = new Header(rrdFile);
-
-		// Load the data sources
-		dataSources = new ArrayList<DataSource>();
-
-		for (int i = 0; i < header.dsCount; i++) {
-			DataSource ds = new DataSource(rrdFile);
-
-			dataSources.add(ds);
-		}
-
-		// Load the archives
-		archives = new ArrayList<Archive>();
-
-		for (int i = 0; i < header.rraCount; i++) {
-			Archive archive = new Archive(this);
-
-			archives.add(archive);
-		}
-
-		rrdFile.align();
-
-		long timestamp = (long)(rrdFile.readInt()) * 1000;
-		if(header.getIntVersion() >= 3) {
-			//Version 3 has an additional microsecond field
-			int microSeconds = rrdFile.readInt();
-			timestamp += (microSeconds/1000); //Date only does up to milliseconds
-		}
-		lastUpdate = new Date( timestamp );
-		// Load PDPStatus(s)
-		for (int i = 0; i < header.dsCount; i++) {
-			DataSource ds = dataSources.get(i);
-
-			ds.loadPDPStatusBlock(rrdFile);
-		}
-
-		// Load CDPStatus(s)
-		for (int i = 0; i < header.rraCount; i++) {
-			Archive archive = archives.get(i);
-
-			archive.loadCDPStatusBlocks(rrdFile, header.dsCount);
-		}
-
-		// Load current row information for each archive
-		for (int i = 0; i < header.rraCount; i++) {
-			Archive archive = archives.get(i);
-
-			archive.loadCurrentRow(rrdFile);
-		}
-
-		// Now load the data
-		for (int i = 0; i < header.rraCount; i++) {
-			Archive archive = archives.get(i);
-
-			archive.loadData(rrdFile, header.dsCount);
-		}
-	}
-
-	/**
-	 * Returns the <code>Header</code> for this database.
-	 *
-	 * @return the <code>Header</code> for this database.
-	 */
-	public Header getHeader() {
-		return header;
-	}
-
-	/**
-	 * Returns the date this database was last updated. To convert this date to
-	 * the form returned by <code>rrdtool last</code> call Date.getTime() and
-	 * divide the result by 1000.
-	 *
-	 * @return the date this database was last updated.
-	 */
-	public Date getLastUpdate() {
-		return lastUpdate;
-	}
-
-	/**
-	 * Returns the <code>DataSource</code> at the specified position in this database.
-	 *
-	 * @param index index of <code>DataSource</code> to return.
-	 * @return the <code>DataSource</code> at the specified position in this database
-	 */
-	public DataSource getDataSource(int index) {
-		return dataSources.get(index);
-	}
-
-	/**
-	 * Returns an iterator over the data sources in this database in proper sequence.
-	 *
-	 * @return an iterator over the data sources in this database in proper sequence.
-	 */
-	public Iterator<DataSource> getDataSources() {
-		return dataSources.iterator();
-	}
-
-	/**
-	 * Returns the <code>Archive</code> at the specified position in this database.
-	 *
-	 * @param index index of <code>Archive</code> to return.
-	 * @return the <code>Archive</code> at the specified position in this database.
-	 */
-	public Archive getArchive(int index) {
-		return archives.get(index);
-	}
-
-	/**
-	 * Returns an iterator over the archives in this database in proper sequence.
-	 *
-	 * @return an iterator over the archives in this database in proper sequence.
-	 */
-	public Iterator<Archive> getArchives() {
-		return archives.iterator();
-	}
-
-	/**
-	 * Returns the number of archives in this database.
-	 *
-	 * @return the number of archives in this database.
-	 */
-	public int getNumArchives() {
-		return header.rraCount;
-	}
-
-	/**
-	 * Returns an iterator over the archives in this database of the given type
-	 * in proper sequence.
-	 *
-	 * @param type the consolidation function that should have been applied to
-	 *             the data.
-	 * @return an iterator over the archives in this database of the given type
-	 *         in proper sequence.
-	 */
-	public Iterator<Archive> getArchives(ConsolidationFunctionType type) {
-		return getArchiveList(type).iterator();
-	}
-
-	ArrayList<Archive> getArchiveList(ConsolidationFunctionType type) {
-
-		ArrayList<Archive> subset = new ArrayList<Archive>();
-
-		for (int i = 0; i < archives.size(); i++) {
-			Archive archive = archives.get(i);
-
-			if (archive.getType().equals(type)) {
-				subset.add(archive);
-			}
-		}
-
-		return subset;
-	}
-
-	/**
-	 * Closes this database stream and releases any associated system resources.
-	 *
-	 * @throws IOException if an I/O error occurs.
-	 */
-	public void close() throws IOException {
-		rrdFile.close();
-	}
-
-	/**
-	 * Outputs the header information of the database to the given print stream
-	 * using the default number format. The default format for <code>double</code>
-	 * is 0.0000000000E0.
-	 *
-	 * @param s the PrintStream to print the header information to.
-	 */
-	public void printInfo(PrintStream s) {
-
-		NumberFormat numberFormat = new DecimalFormat("0.0000000000E0");
-
-		printInfo(s, numberFormat);
-	}
-
-	/**
-	 * Returns data from the database corresponding to the given consolidation
-	 * function and a step size of 1.
-	 *
-	 * @param type the consolidation function that should have been applied to
-	 *             the data.
-	 * @return the raw data.
-	 * @throws RrdException if there was a problem locating a data archive with
-	 *                      the requested consolidation function.
-	 * @throws IOException  if there was a problem reading data from the database.
-	 */
-	public DataChunk getData(ConsolidationFunctionType type)
-			throws RrdException, IOException {
-		return getData(type, 1L);
-	}
-
-	/**
-	 * Returns data from the database corresponding to the given consolidation
-	 * function.
-	 *
-	 * @param type the consolidation function that should have been applied to
-	 *             the data.
-	 * @param step the step size to use.
-	 * @return the raw data.
-	 * @throws RrdException if there was a problem locating a data archive with
-	 *                      the requested consolidation function.
-	 * @throws IOException  if there was a problem reading data from the database.
-	 */
-	public DataChunk getData(ConsolidationFunctionType type, long step)
-			throws RrdException, IOException {
-
-		ArrayList<Archive> possibleArchives = getArchiveList(type);
-
-		if (possibleArchives.size() == 0) {
-			throw new RrdException("Database does not contain an Archive of consolidation function type "
-					+ type);
-		}
-
-		Calendar endCal = Calendar.getInstance();
-
-		endCal.set(Calendar.MILLISECOND, 0);
-
-		Calendar startCal = (Calendar) endCal.clone();
-
-		startCal.add(Calendar.DATE, -1);
-
-		long end = endCal.getTime().getTime() / 1000;
-		long start = startCal.getTime().getTime() / 1000;
-		Archive archive = findBestArchive(start, end, step, possibleArchives);
-
-		// Tune the parameters
-		step = header.pdpStep * archive.pdpCount;
-		start -= start % step;
-
-		if (end % step != 0) {
-			end += step - end % step;
-		}
-
-		int rows = (int) ((end - start) / step + 1);
-
-		//cat.debug("start " + start + " end " + end + " step " + step + " rows "
-		//          + rows);
-
-		// Find start and end offsets
-		// This is terrible - some of this should be encapsulated in Archive - CT.
-		long lastUpdateLong = lastUpdate.getTime() / 1000;
-		long archiveEndTime = lastUpdateLong - (lastUpdateLong % step);
-		long archiveStartTime = archiveEndTime - (step * (archive.rowCount - 1));
-		int startOffset = (int) ((start - archiveStartTime) / step);
-		int endOffset = (int) ((archiveEndTime - end) / step);
-
-		//cat.debug("start " + archiveStartTime + " end " + archiveEndTime
-		//          + " startOffset " + startOffset + " endOffset "
-		//          + (archive.rowCount - endOffset));
-
-		DataChunk chunk = new DataChunk(start, startOffset, endOffset, step,
-				header.dsCount, rows);
-
-		archive.loadData(chunk);
-
-		return chunk;
-	}
-
-	/*
-	 * This is almost a verbatim copy of the original C code by Tobias Oetiker.
-	 * I need to put more of a Java style on it - CT
-	 */
-	private Archive findBestArchive(long start, long end, long step,
-									ArrayList<Archive> archives) {
-
-		Archive archive = null;
-		Archive bestFullArchive = null;
-		Archive bestPartialArchive = null;
-		long lastUpdateLong = lastUpdate.getTime() / 1000;
-		int firstPart = 1;
-		int firstFull = 1;
-		long bestMatch = 0;
-		//long bestPartRRA = 0;
-		long bestStepDiff = 0;
-		long tmpStepDiff = 0;
-
-		for (int i = 0; i < archives.size(); i++) {
-			archive = archives.get(i);
-
-			long calEnd = lastUpdateLong
-					- (lastUpdateLong
-					% (archive.pdpCount * header.pdpStep));
-			long calStart = calEnd
-					- (archive.pdpCount * archive.rowCount
-					* header.pdpStep);
-			long fullMatch = end - start;
-
-			if ((calEnd >= end) && (calStart < start)) {	// Best full match
-				tmpStepDiff = Math.abs(step - (header.pdpStep * archive.pdpCount));
-
-				if ((firstFull != 0) || (tmpStepDiff < bestStepDiff)) {
-					firstFull = 0;
-					bestStepDiff = tmpStepDiff;
-					bestFullArchive = archive;
-				}
-			}
-			else {										// Best partial match
-				long tmpMatch = fullMatch;
-
-				if (calStart > start) {
-					tmpMatch -= calStart - start;
-				}
-
-				if (calEnd < end) {
-					tmpMatch -= end - calEnd;
-				}
-
-				if ((firstPart != 0) || (bestMatch < tmpMatch)) {
-					firstPart = 0;
-					bestMatch = tmpMatch;
-					bestPartialArchive = archive;
-				}
-			}
-		}
-
-		// See how the matching went
-		// optimise this
-		if (firstFull == 0) {
-			archive = bestFullArchive;
-		}
-		else if (firstPart == 0) {
-			archive = bestPartialArchive;
-		}
-
-		return archive;
-	}
-
-	/**
-	 * Outputs the header information of the database to the given print stream
-	 * using the given number format. The format is almost identical to that
-	 * produced by
-	 * <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/manual/rrdinfo.html">rrdtool info</a>
-	 *
-	 * @param s			the PrintStream to print the header information to.
-	 * @param numberFormat the format to print <code>double</code>s as.
-	 */
-	public void printInfo(PrintStream s, NumberFormat numberFormat) {
-
-		s.print("filename = \"");
-		s.print(name);
-		s.println("\"");
-		s.print("rrd_version = \"");
-		s.print(header.version);
-		s.println("\"");
-		s.print("step = ");
-		s.println(header.pdpStep);
-		s.print("last_update = ");
-		s.println(lastUpdate.getTime() / 1000);
-
-		for (Iterator<DataSource> i = dataSources.iterator(); i.hasNext();) {
-			DataSource ds = i.next();
-
-			ds.printInfo(s, numberFormat);
-		}
-
-		int index = 0;
-
-		for (Iterator<Archive> i = archives.iterator(); i.hasNext();) {
-			Archive archive = i.next();
-
-			archive.printInfo(s, numberFormat, index++);
-		}
-	}
-
-	/**
-	 * Outputs the content of the database to the given print stream
-	 * as a stream of XML. The XML format is almost identical to that produced by
-	 * <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/manual/rrddump.html">rrdtool dump</a>
-	 *
-	 * @param s the PrintStream to send the XML to.
-	 */
-	public void toXml(PrintStream s) throws RrdException {
-
-		s.println("<!--");
-		s.println("  -- Round Robin RRDatabase Dump ");
-		s.println("  -- Generated by jRRD <ciaran@codeloop.com>");
-		s.println("  -->");
-		s.println("<rrd>");
-		s.print("\t<version> ");
-		s.print(header.version);
-		s.println(" </version>");
-		s.print("\t<step> ");
-		s.print(header.pdpStep);
-		s.println(" </step> <!-- Seconds -->");
-		s.print("\t<lastupdate> ");
-		s.print(lastUpdate.getTime() / 1000);
-		s.print(" </lastupdate> <!-- ");
-		s.print(lastUpdate.toString());
-		s.println(" -->");
-		s.println();
-
-		for (int i = 0; i < header.dsCount; i++) {
-			DataSource ds = dataSources.get(i);
-
-			ds.toXml(s);
-		}
-
-		s.println("<!-- Round Robin Archives -->");
-
-		for (int i = 0; i < header.rraCount; i++) {
-			Archive archive = archives.get(i);
-
-			archive.toXml(s);
-		}
-
-		s.println("</rrd>");
-	}
-
-	/**
-	 * Returns a summary the contents of this database.
-	 *
-	 * @return a summary of the information contained in this database.
-	 */
-	public String toString() {
-
-		StringBuffer sb = new StringBuffer("\n");
-
-		sb.append(header.toString());
-
-		for (Iterator<DataSource> i = dataSources.iterator(); i.hasNext();) {
-			DataSource ds = i.next();
-
-			sb.append("\n\t");
-			sb.append(ds.toString());
-		}
-
-		for (Iterator<Archive> i = archives.iterator(); i.hasNext();) {
-			Archive archive = i.next();
-
-			sb.append("\n\t");
-			sb.append(archive.toString());
-		}
-
-		return sb.toString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/timespec/Epoch.java b/apps/jrobin/java/src/org/jrobin/core/timespec/Epoch.java
deleted file mode 100644
index 7a5c70f34bd061f6865ad8688d7c26e6c9da02af..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/timespec/Epoch.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.timespec;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- * Small swing-based utility to convert timestamps (seconds since epoch) to readable dates and vice versa.
- * Supports at-style time specification (like "now-2d", "noon yesterday") and other human-readable
- * data formats:<p>
- * <ul>
- * <li>MM/dd/yy HH:mm:ss
- * <li>dd.MM.yy HH:mm:ss
- * <li>dd.MM.yy HH:mm:ss
- * <li>MM/dd/yy HH:mm
- * <li>dd.MM.yy HH:mm
- * <li>yy-MM-dd HH:mm
- * <li>MM/dd/yy
- * <li>dd.MM.yy
- * <li>yy-MM-dd
- * <li>HH:mm MM/dd/yy
- * <li>HH:mm dd.MM.yy
- * <li>HH:mm yy-MM-dd
- * <li>HH:mm:ss MM/dd/yy
- * <li>HH:mm:ss dd.MM.yy
- * <li>HH:mm:ss yy-MM-dd
- * </ul>
- * The current timestamp is displayed in the title bar :)<p>
- */
-public class Epoch extends JFrame {
-	private static final long serialVersionUID = 1L;
-	private static final String[] supportedFormats = {
-			"MM/dd/yy HH:mm:ss", "dd.MM.yy HH:mm:ss", "yy-MM-dd HH:mm:ss", "MM/dd/yy HH:mm",
-			"dd.MM.yy HH:mm", "yy-MM-dd HH:mm", "MM/dd/yy", "dd.MM.yy", "yy-MM-dd", "HH:mm MM/dd/yy",
-			"HH:mm dd.MM.yy", "HH:mm yy-MM-dd", "HH:mm:ss MM/dd/yy", "HH:mm:ss dd.MM.yy", "HH:mm:ss yy-MM-dd"
-	};
-
-	private static final SimpleDateFormat[] parsers = new SimpleDateFormat[supportedFormats.length];
-	private static final String helpText;
-
-	private Timer timer = new Timer(1000, new ActionListener() {
-		public void actionPerformed(ActionEvent e) {
-			showTimestamp();
-		}
-	});
-
-	static {
-		for (int i = 0; i < parsers.length; i++) {
-			parsers[i] = new SimpleDateFormat(supportedFormats[i]);
-			parsers[i].setLenient(true);
-		}
-		StringBuffer tooltipBuff = new StringBuffer("<html><b>Supported input formats:</b><br>");
-		for (String supportedFormat : supportedFormats) {
-			tooltipBuff.append(supportedFormat).append("<br>");
-		}
-		tooltipBuff.append("<b>AT-style time specification</b><br>");
-		tooltipBuff.append("timestamp<br><br>");
-		tooltipBuff.append("Copyright (C) 2003-2005 Sasa Markovic, All Rights Reserved</html>");
-		helpText = tooltipBuff.toString();
-	}
-
-	private JLabel topLabel = new JLabel("Enter timestamp or readable date:");
-	private JTextField inputField = new JTextField(25);
-	private JButton convertButton = new JButton("Convert");
-	private JButton helpButton = new JButton("Help");
-
-	private static final SimpleDateFormat OUTPUT_DATE_FORMAT =
-			new SimpleDateFormat("MM/dd/yy HH:mm:ss EEE");
-
-	Epoch() {
-		super("Epoch");
-		constructUI();
-		timer.start();
-	}
-
-	private void constructUI() {
-		JPanel c = (JPanel) getContentPane();
-		c.setLayout(new BorderLayout(3, 3));
-		c.add(topLabel, BorderLayout.NORTH);
-		c.add(inputField, BorderLayout.WEST);
-		c.add(convertButton, BorderLayout.CENTER);
-		convertButton.setToolTipText(helpText);
-		convertButton.addActionListener(new ActionListener() {
-			public void actionPerformed(ActionEvent e) {
-				convert();
-			}
-		});
-		c.add(helpButton, BorderLayout.EAST);
-		helpButton.addActionListener(new ActionListener() {
-			public void actionPerformed(ActionEvent e) {
-				JOptionPane.showMessageDialog(helpButton, helpText, "Epoch Help", JOptionPane.INFORMATION_MESSAGE);
-			}
-		});
-		inputField.requestFocus();
-		getRootPane().setDefaultButton(convertButton);
-		setResizable(false);
-		setDefaultCloseOperation(EXIT_ON_CLOSE);
-		pack();
-		centerOnScreen();
-		setVisible(true);
-	}
-
-	void centerOnScreen() {
-		Toolkit t = Toolkit.getDefaultToolkit();
-		Dimension screenSize = t.getScreenSize();
-		Dimension frameSize = getPreferredSize();
-		double x = (screenSize.getWidth() - frameSize.getWidth()) / 2;
-		double y = (screenSize.getHeight() - frameSize.getHeight()) / 2;
-		setLocation((int) x, (int) y);
-	}
-
-	private void convert() {
-		String time = inputField.getText().trim();
-		if (time.length() > 0) {
-			// try simple timestamp
-			try {
-				long timestamp = Long.parseLong(time);
-				Date date = new Date(timestamp * 1000L);
-				formatDate(date);
-			}
-			catch (NumberFormatException nfe) {
-				// failed, try as a date
-				try {
-					inputField.setText("" + parseDate(time));
-				}
-				catch (RrdException e) {
-					inputField.setText("Could not convert, sorry");
-				}
-			}
-		}
-	}
-
-	private void showTimestamp() {
-		long timestamp = Util.getTime();
-		setTitle(timestamp + " seconds since epoch");
-	}
-
-	void formatDate(Date date) {
-		inputField.setText(OUTPUT_DATE_FORMAT.format(date));
-	}
-
-	private long parseDate(String time) throws RrdException {
-		for (SimpleDateFormat parser : parsers) {
-			try {
-				return Util.getTimestamp(parser.parse(time));
-			}
-			catch (ParseException e) {
-				// NOP
-			}
-		}
-		return new TimeParser(time).parse().getTimestamp();
-	}
-
-	/**
-	 * Main method which runs this utility.
-	 *
-	 * @param args Not used.
-	 */
-	public static void main(String[] args) {
-		new Epoch();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/timespec/TimeParser.java b/apps/jrobin/java/src/org/jrobin/core/timespec/TimeParser.java
deleted file mode 100644
index b957ecc140847b2fcac0283c7f3776d1c7716a62..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/timespec/TimeParser.java
+++ /dev/null
@@ -1,443 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-/*
- * Java port of Tobi's original parsetime.c routine
- */
-package org.jrobin.core.timespec;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-
-/**
- * Class which parses at-style time specification (described in detail on the rrdfetch man page),
- * used in all RRDTool commands. This code is in most parts just a java port of Tobi's parsetime.c
- * code.
- */
-public class TimeParser {
-	private static final int PREVIOUS_OP = -1;
-
-	TimeToken token;
-	TimeScanner scanner;
-	TimeSpec spec;
-
-	int op = TimeToken.PLUS;
-	int prev_multiplier = -1;
-
-	/**
-	 * Constructs TimeParser instance from the given input string.
-	 *
-	 * @param dateString at-style time specification (read rrdfetch man page
-	 *                   for the complete explanation)
-	 */
-	public TimeParser(String dateString) {
-		scanner = new TimeScanner(dateString);
-		spec = new TimeSpec(dateString);
-	}
-
-	private void expectToken(int desired, String errorMessage) throws RrdException {
-		token = scanner.nextToken();
-		if (token.id != desired) {
-			throw new RrdException(errorMessage);
-		}
-	}
-
-	private void plusMinus(int doop) throws RrdException {
-		if (doop >= 0) {
-			op = doop;
-			expectToken(TimeToken.NUMBER, "There should be number after " +
-					(op == TimeToken.PLUS ? '+' : '-'));
-			prev_multiplier = -1; /* reset months-minutes guessing mechanics */
-		}
-		int delta = Integer.parseInt(token.value);
-		token = scanner.nextToken();
-		if (token.id == TimeToken.MONTHS_MINUTES) {
-			/* hard job to guess what does that -5m means: -5mon or -5min? */
-			switch (prev_multiplier) {
-				case TimeToken.DAYS:
-				case TimeToken.WEEKS:
-				case TimeToken.MONTHS:
-				case TimeToken.YEARS:
-					token = scanner.resolveMonthsMinutes(TimeToken.MONTHS);
-					break;
-				case TimeToken.SECONDS:
-				case TimeToken.MINUTES:
-				case TimeToken.HOURS:
-					token = scanner.resolveMonthsMinutes(TimeToken.MINUTES);
-					break;
-				default:
-					if (delta < 6) {
-						token = scanner.resolveMonthsMinutes(TimeToken.MONTHS);
-					}
-					else {
-						token = scanner.resolveMonthsMinutes(TimeToken.MINUTES);
-					}
-			}
-		}
-		prev_multiplier = token.id;
-		delta *= (op == TimeToken.PLUS) ? +1 : -1;
-		switch (token.id) {
-			case TimeToken.YEARS:
-				spec.dyear += delta;
-				break;
-			case TimeToken.MONTHS:
-				spec.dmonth += delta;
-				break;
-			case TimeToken.WEEKS:
-				delta *= 7;
-				spec.dday += delta;
-				break;
-			case TimeToken.DAYS:
-				spec.dday += delta;
-				break;
-			case TimeToken.HOURS:
-				spec.dhour += delta;
-				break;
-			case TimeToken.MINUTES:
-				spec.dmin += delta;
-				break;
-			case TimeToken.SECONDS:
-			default: // default is 'seconds'
-				spec.dsec += delta;
-				break;
-		}
-		// unreachable statement
-		// throw new RrdException("Well-known time unit expected after " + delta);
-	}
-
-	/**
-	 * Try and read a "timeofday" specification.  This method will be called
-	 * when we see a plain number at the start of a time, which means we could be
-	 * reading a time, or a day.  If it turns out to be a date, then this method restores
-	 * the scanner state to what it was at entry, and returns without setting anything.
-	 * @throws RrdException
-	 */
-	private void timeOfDay() throws RrdException {
-		int hour, minute = 0;
-		/* save token status in case we must abort */
-		scanner.saveState();
-		/* first pick out the time of day - we assume a HH (COLON|DOT) MM time */
-		if (token.value.length() > 2) {
-			//Definitely not an hour specification; probably a date or something.  Give up now
-			return;
-		}
-		hour = Integer.parseInt(token.value);
-		token = scanner.nextToken();
-		if (token.id == TimeToken.SLASH) {
-			/* guess we are looking at a date */
-			token = scanner.restoreState();
-			return;
-		}
-		if (token.id == TimeToken.COLON || token.id == TimeToken.DOT) {
-			expectToken(TimeToken.NUMBER, "Parsing HH:MM or HH.MM syntax, expecting MM as number, got none");
-			minute = Integer.parseInt(token.value);
-			if (minute > 59) {
-				throw new RrdException("Parsing HH:MM or HH.MM syntax, got MM = " +
-						minute + " (>59!)");
-			}
-			token = scanner.nextToken();
-			if(token.id == TimeToken.DOT) {
-				//Oh look, another dot; must have actually been a date in DD.MM.YYYY format.  Give up and return
-				token = scanner.restoreState();
-				return;
-			}
-
-		}
-		/* check if an AM or PM specifier was given */
-		if (token.id == TimeToken.AM || token.id == TimeToken.PM) {
-			if (hour > 12) {
-				throw new RrdException("There cannot be more than 12 AM or PM hours");
-			}
-			if (token.id == TimeToken.PM) {
-				if (hour != 12) {
-					/* 12:xx PM is 12:xx, not 24:xx */
-					hour += 12;
-				}
-			}
-			else {
-				if (hour == 12) {
-					/* 12:xx AM is 00:xx, not 12:xx */
-					hour = 0;
-				}
-			}
-			token = scanner.nextToken();
-		}
-		else if (hour > 23) {
-			/* guess it was not a time then, probably a date ... */
-			token = scanner.restoreState();
-			return;
-		}
-
-		spec.hour = hour;
-		spec.min = minute;
-		spec.sec = 0;
-		if (spec.hour == 24) {
-			spec.hour = 0;
-			spec.day++;
-		}
-	}
-
-	private void assignDate(long mday, long mon, long year) throws RrdException {
-		if (year > 138) {
-			if (year > 1970) {
-				year -= 1900;
-			}
-			else {
-				throw new RrdException("Invalid year " + year +
-						" (should be either 00-99 or >1900)");
-			}
-		}
-		else if (year >= 0 && year < 38) {
-			year += 100;		 /* Allow year 2000-2037 to be specified as   */
-		}						 /* 00-37 until the problem of 2038 year will */
-		/* arise for unices with 32-bit time_t     */
-		if (year < 70) {
-			throw new RrdException("Won't handle dates before epoch (01/01/1970), sorry");
-		}
-		spec.year = (int) year;
-		spec.month = (int) mon;
-		spec.day = (int) mday;
-	}
-
-	private void day() throws RrdException {
-		long mday = 0, wday, mon, year = spec.year;
-		switch (token.id) {
-			case TimeToken.YESTERDAY:
-				spec.day--;
-				token = scanner.nextToken();
-				break;
-			case TimeToken.TODAY:	/* force ourselves to stay in today - no further processing */
-				token = scanner.nextToken();
-				break;
-			case TimeToken.TOMORROW:
-				spec.day++;
-				token = scanner.nextToken();
-				break;
-			case TimeToken.JAN:
-			case TimeToken.FEB:
-			case TimeToken.MAR:
-			case TimeToken.APR:
-			case TimeToken.MAY:
-			case TimeToken.JUN:
-			case TimeToken.JUL:
-			case TimeToken.AUG:
-			case TimeToken.SEP:
-			case TimeToken.OCT:
-			case TimeToken.NOV:
-			case TimeToken.DEC:
-				/* do month mday [year] */
-				mon = (token.id - TimeToken.JAN);
-				expectToken(TimeToken.NUMBER, "the day of the month should follow month name");
-				mday = Long.parseLong(token.value);
-				token = scanner.nextToken();
-				if (token.id == TimeToken.NUMBER) {
-					year = Long.parseLong(token.value);
-					token = scanner.nextToken();
-				}
-				else {
-					year = spec.year;
-				}
-				assignDate(mday, mon, year);
-				break;
-			case TimeToken.SUN:
-			case TimeToken.MON:
-			case TimeToken.TUE:
-			case TimeToken.WED:
-			case TimeToken.THU:
-			case TimeToken.FRI:
-			case TimeToken.SAT:
-				/* do a particular day of the week */
-				wday = (token.id - TimeToken.SUN);
-				spec.day += (wday - spec.wday);
-				token = scanner.nextToken();
-				break;
-			case TimeToken.NUMBER:
-				/* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY */
-				// int tlen = token.value.length();
-				mon = Long.parseLong(token.value);
-				if (mon > 10L * 365L * 24L * 60L * 60L) {
-					spec.localtime(mon);
-					token = scanner.nextToken();
-					break;
-				}
-				if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */
-					year = mon / 10000;
-					mday = mon % 100;
-					mon = (mon / 100) % 100;
-					token = scanner.nextToken();
-				}
-				else {
-					token = scanner.nextToken();
-					if (mon <= 31 && (token.id == TimeToken.SLASH || token.id == TimeToken.DOT)) {
-						int sep = token.id;
-						expectToken(TimeToken.NUMBER, "there should be " +
-								(sep == TimeToken.DOT ? "month" : "day") +
-								" number after " +
-								(sep == TimeToken.DOT ? '.' : '/'));
-						mday = Long.parseLong(token.value);
-						token = scanner.nextToken();
-						if (token.id == sep) {
-							expectToken(TimeToken.NUMBER, "there should be year number after " +
-									(sep == TimeToken.DOT ? '.' : '/'));
-							year = Long.parseLong(token.value);
-							token = scanner.nextToken();
-						}
-						/* flip months and days for European timing */
-						if (sep == TimeToken.DOT) {
-							long x = mday;
-							mday = mon;
-							mon = x;
-						}
-					}
-				}
-				mon--;
-				if (mon < 0 || mon > 11) {
-					throw new RrdException("Did you really mean month " + (mon + 1));
-				}
-				if (mday < 1 || mday > 31) {
-					throw new RrdException("I'm afraid that " + mday +
-							" is not a valid day of the month");
-				}
-				assignDate(mday, mon, year);
-				break;
-		}
-	}
-
-	/**
-	 * Parses the input string specified in the constructor.
-	 *
-	 * @return Object representing parsed date/time.
-	 * @throws RrdException Thrown if the date string cannot be parsed.
-	 */
-	public TimeSpec parse() throws RrdException {
-		long now = Util.getTime();
-		int hr = 0;
-		/* this MUST be initialized to zero for midnight/noon/teatime */
-		/* establish the default time reference */
-		spec.localtime(now);
-		token = scanner.nextToken();
-		switch (token.id) {
-			case TimeToken.PLUS:
-			case TimeToken.MINUS:
-				break; /* jump to OFFSET-SPEC part */
-			case TimeToken.START:
-				spec.type = TimeSpec.TYPE_START;
-				/* FALLTHRU */
-			case TimeToken.END:
-				if (spec.type != TimeSpec.TYPE_START) {
-					spec.type = TimeSpec.TYPE_END;
-				}
-				/* FALLTHRU */
-                        case TimeToken.EPOCH:
-                                /* FALLTHRU */
-			case TimeToken.NOW:
-				int time_reference = token.id;
-                                if (token.id != TimeToken.NOW) {
-                                    spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0;
-                                }
-				token = scanner.nextToken();
-				if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) {
-					break;
-				}
-				if (time_reference == TimeToken.START || time_reference == TimeToken.END) {
-					throw new RrdException("Words 'start' or 'end' MUST be followed by +|- offset");
-				}
-				else if (token.id != TimeToken.EOF) {
-					throw new RrdException("If 'now' or 'epoch' is followed by a token it must be +|- offset");
-				}
-				break;
-				/* Only absolute time specifications below */
-			case TimeToken.NUMBER:
-				timeOfDay();
-				//Keep going; there might be a date after the time of day, which day() will pick up
-				/* fix month parsing */
-			case TimeToken.JAN:
-			case TimeToken.FEB:
-			case TimeToken.MAR:
-			case TimeToken.APR:
-			case TimeToken.MAY:
-			case TimeToken.JUN:
-			case TimeToken.JUL:
-			case TimeToken.AUG:
-			case TimeToken.SEP:
-			case TimeToken.OCT:
-			case TimeToken.NOV:
-			case TimeToken.DEC:
-			case TimeToken.TODAY:
-			case TimeToken.YESTERDAY:
-			case TimeToken.TOMORROW:
-				day();
-				if (token.id != TimeToken.NUMBER) {
-					break;
-				}
-				//Allows (but does not require) the time to be specified after the day.  This extends the rrdfetch specifiation
-				timeOfDay();
-				break;
-
-				/* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized
-				 * hr to zero up above, then fall into this case in such a
-				 * way so we add +12 +4 hours to it for teatime, +12 hours
-				 * to it for noon, and nothing at all for midnight, then
-				 * set our rettime to that hour before leaping into the
-				 * month scanner
-				 */
-			case TimeToken.TEATIME:
-				hr += 4;
-				/* FALLTHRU */
-			case TimeToken.NOON:
-				hr += 12;
-				/* FALLTHRU */
-			case TimeToken.MIDNIGHT:
-				spec.hour = hr;
-				spec.min = 0;
-				spec.sec = 0;
-				token = scanner.nextToken();
-				day();
-				break;
-			default:
-				throw new RrdException("Unparsable time: " + token.value);
-		}
-
-		/*
-		 * the OFFSET-SPEC part
-		 *
-		 * (NOTE, the sc_tokid was prefetched for us by the previous code)
-		 */
-		if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) {
-			scanner.setContext(false);
-			while (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS ||
-					token.id == TimeToken.NUMBER) {
-				if (token.id == TimeToken.NUMBER) {
-					plusMinus(PREVIOUS_OP);
-				}
-				else {
-					plusMinus(token.id);
-				}
-				token = scanner.nextToken();
-				/* We will get EOF eventually but that's OK, since
-				token() will return us as many EOFs as needed */
-			}
-		}
-		/* now we should be at EOF */
-		if (token.id != TimeToken.EOF) {
-			throw new RrdException("Unparsable trailing text: " + token.value);
-		}
-		return spec;
-	}
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/core/timespec/TimeScanner.java b/apps/jrobin/java/src/org/jrobin/core/timespec/TimeScanner.java
deleted file mode 100644
index 2895ae55d60ebc140a89f42de049baecdc03b0c7..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/timespec/TimeScanner.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.core.timespec;
-
-class TimeScanner {
-	private String dateString;
-
-	private int pos, pos_save;
-	private TimeToken token, token_save;
-
-	static final TimeToken[] WORDS = {
-			new TimeToken("midnight", TimeToken.MIDNIGHT),	/* 00:00:00 of today or tomorrow */
-			new TimeToken("noon", TimeToken.NOON),		/* 12:00:00 of today or tomorrow */
-			new TimeToken("teatime", TimeToken.TEATIME),	/* 16:00:00 of today or tomorrow */
-			new TimeToken("am", TimeToken.AM),		/* morning times for 0-12 clock */
-			new TimeToken("pm", TimeToken.PM),		/* evening times for 0-12 clock */
-			new TimeToken("tomorrow", TimeToken.TOMORROW),
-			new TimeToken("yesterday", TimeToken.YESTERDAY),
-			new TimeToken("today", TimeToken.TODAY),
-			new TimeToken("now", TimeToken.NOW),
-			new TimeToken("n", TimeToken.NOW),
-			new TimeToken("start", TimeToken.START),
-			new TimeToken("s", TimeToken.START),
-			new TimeToken("end", TimeToken.END),
-			new TimeToken("e", TimeToken.END),
-			new TimeToken("jan", TimeToken.JAN),
-			new TimeToken("feb", TimeToken.FEB),
-			new TimeToken("mar", TimeToken.MAR),
-			new TimeToken("apr", TimeToken.APR),
-			new TimeToken("may", TimeToken.MAY),
-			new TimeToken("jun", TimeToken.JUN),
-			new TimeToken("jul", TimeToken.JUL),
-			new TimeToken("aug", TimeToken.AUG),
-			new TimeToken("sep", TimeToken.SEP),
-			new TimeToken("oct", TimeToken.OCT),
-			new TimeToken("nov", TimeToken.NOV),
-			new TimeToken("dec", TimeToken.DEC),
-			new TimeToken("january", TimeToken.JAN),
-			new TimeToken("february", TimeToken.FEB),
-			new TimeToken("march", TimeToken.MAR),
-			new TimeToken("april", TimeToken.APR),
-			new TimeToken("may", TimeToken.MAY),
-			new TimeToken("june", TimeToken.JUN),
-			new TimeToken("july", TimeToken.JUL),
-			new TimeToken("august", TimeToken.AUG),
-			new TimeToken("september", TimeToken.SEP),
-			new TimeToken("october", TimeToken.OCT),
-			new TimeToken("november", TimeToken.NOV),
-			new TimeToken("december", TimeToken.DEC),
-			new TimeToken("sunday", TimeToken.SUN),
-			new TimeToken("sun", TimeToken.SUN),
-			new TimeToken("monday", TimeToken.MON),
-			new TimeToken("mon", TimeToken.MON),
-			new TimeToken("tuesday", TimeToken.TUE),
-			new TimeToken("tue", TimeToken.TUE),
-			new TimeToken("wednesday", TimeToken.WED),
-			new TimeToken("wed", TimeToken.WED),
-			new TimeToken("thursday", TimeToken.THU),
-			new TimeToken("thu", TimeToken.THU),
-			new TimeToken("friday", TimeToken.FRI),
-			new TimeToken("fri", TimeToken.FRI),
-			new TimeToken("saturday", TimeToken.SAT),
-			new TimeToken("sat", TimeToken.SAT),
-                        new TimeToken("epoch", TimeToken.EPOCH),
-			new TimeToken(null, 0)			/*** SENTINEL ***/
-	};
-
-	static TimeToken[] MULTIPLIERS = {
-			new TimeToken("second", TimeToken.SECONDS),	/* seconds multiplier */
-			new TimeToken("seconds", TimeToken.SECONDS),	/* (pluralized) */
-			new TimeToken("sec", TimeToken.SECONDS),		/* (generic) */
-			new TimeToken("s", TimeToken.SECONDS),		/* (short generic) */
-			new TimeToken("minute", TimeToken.MINUTES),	/* minutes multiplier */
-			new TimeToken("minutes", TimeToken.MINUTES),	/* (pluralized) */
-			new TimeToken("min", TimeToken.MINUTES),		/* (generic) */
-			new TimeToken("m", TimeToken.MONTHS_MINUTES),	/* (short generic) */
-			new TimeToken("hour", TimeToken.HOURS),		/* hours ... */
-			new TimeToken("hours", TimeToken.HOURS),		/* (pluralized) */
-			new TimeToken("hr", TimeToken.HOURS),		/* (generic) */
-			new TimeToken("h", TimeToken.HOURS),		/* (short generic) */
-			new TimeToken("day", TimeToken.DAYS),		/* days ... */
-			new TimeToken("days", TimeToken.DAYS),		/* (pluralized) */
-			new TimeToken("d", TimeToken.DAYS),		/* (short generic) */
-			new TimeToken("week", TimeToken.WEEKS),		/* week ... */
-			new TimeToken("weeks", TimeToken.WEEKS),		/* (pluralized) */
-			new TimeToken("wk", TimeToken.WEEKS),		/* (generic) */
-			new TimeToken("w", TimeToken.WEEKS),		/* (short generic) */
-			new TimeToken("month", TimeToken.MONTHS),	/* week ... */
-			new TimeToken("months", TimeToken.MONTHS),	/* (pluralized) */
-			new TimeToken("mon", TimeToken.MONTHS),		/* (generic) */
-			new TimeToken("year", TimeToken.YEARS),		/* year ... */
-			new TimeToken("years", TimeToken.YEARS),		/* (pluralized) */
-			new TimeToken("yr", TimeToken.YEARS),		/* (generic) */
-			new TimeToken("y", TimeToken.YEARS),		/* (short generic) */
-			new TimeToken(null, 0)			/*** SENTINEL ***/
-	};
-
-	TimeToken[] specials = WORDS;
-
-	public TimeScanner(String dateString) {
-		this.dateString = dateString;
-	}
-
-	void setContext(boolean parsingWords) {
-		specials = parsingWords ? WORDS : MULTIPLIERS;
-	}
-
-	TimeToken nextToken() {
-		StringBuffer buffer = new StringBuffer("");
-		while (pos < dateString.length()) {
-			char c = dateString.charAt(pos++);
-			if (Character.isWhitespace(c) || c == '_' || c == ',') {
-				continue;
-			}
-			buffer.append(c);
-			if (Character.isDigit(c)) {
-				// pick as many digits as possible
-				while (pos < dateString.length()) {
-					char next = dateString.charAt(pos);
-					if (Character.isDigit(next)) {
-						buffer.append(next);
-						pos++;
-					}
-					else {
-						break;
-					}
-				}
-				String value = buffer.toString();
-				return token = new TimeToken(value, TimeToken.NUMBER);
-			}
-			if (Character.isLetter(c)) {
-				// pick as many letters as possible
-				while (pos < dateString.length()) {
-					char next = dateString.charAt(pos);
-					if (Character.isLetter(next)) {
-						buffer.append(next);
-						pos++;
-					}
-					else {
-						break;
-					}
-				}
-				String value = buffer.toString();
-				return token = new TimeToken(value, parseToken(value));
-			}
-			switch (c) {
-				case ':':
-					return token = new TimeToken(":", TimeToken.COLON);
-				case '.':
-					return token = new TimeToken(".", TimeToken.DOT);
-				case '+':
-					return token = new TimeToken("+", TimeToken.PLUS);
-				case '-':
-					return token = new TimeToken("-", TimeToken.MINUS);
-				case '/':
-					return token = new TimeToken("/", TimeToken.SLASH);
-				default:
-					pos--;
-					return token = new TimeToken(null, TimeToken.EOF);
-			}
-		}
-		return token = new TimeToken(null, TimeToken.EOF);
-	}
-
-	TimeToken resolveMonthsMinutes(int newId) {
-		assert token.id == TimeToken.MONTHS_MINUTES;
-		assert newId == TimeToken.MONTHS || newId == TimeToken.MINUTES;
-		return token = new TimeToken(token.value, newId);
-	}
-
-	void saveState() {
-		token_save = token;
-		pos_save = pos;
-	}
-
-	TimeToken restoreState() {
-		pos = pos_save;
-		return token = token_save;
-	}
-
-	private int parseToken(String arg) {
-		for (int i = 0; specials[i].value != null; i++) {
-			if (specials[i].value.equalsIgnoreCase(arg)) {
-				return specials[i].id;
-			}
-		}
-		return TimeToken.ID;
-	}
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/core/timespec/TimeSpec.java b/apps/jrobin/java/src/org/jrobin/core/timespec/TimeSpec.java
deleted file mode 100644
index 79b18878d96a88b3143cc4719c3e9301db748b68..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/timespec/TimeSpec.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core.timespec;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-
-/**
- * Simple class to represent time obtained by parsing at-style date specification (described
- * in detail on the rrdfetch man page. See javadoc for {@link org.jrobin.core.timespec.TimeParser}
- * for more information.
- */
-public class TimeSpec {
-	static final int TYPE_ABSOLUTE = 0;
-	static final int TYPE_START = 1;
-	static final int TYPE_END = 2;
-
-	int type = TYPE_ABSOLUTE;
-	int year, month, day, hour, min, sec;
-	int wday;
-	int dyear, dmonth, dday, dhour, dmin, dsec;
-
-	String dateString;
-
-	TimeSpec context;
-
-	TimeSpec(String dateString) {
-		this.dateString = dateString;
-	}
-
-	void localtime(long timestamp) {
-		GregorianCalendar date = new GregorianCalendar();
-		date.setTime(new Date(timestamp * 1000L));
-		year = date.get(Calendar.YEAR) - 1900;
-		month = date.get(Calendar.MONTH);
-		day = date.get(Calendar.DAY_OF_MONTH);
-		hour = date.get(Calendar.HOUR_OF_DAY);
-		min = date.get(Calendar.MINUTE);
-		sec = date.get(Calendar.SECOND);
-		wday = date.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY;
-	}
-
-	GregorianCalendar getTime() throws RrdException {
-		GregorianCalendar gc;
-		// absoulte time, this is easy
-		if (type == TYPE_ABSOLUTE) {
-			gc = new GregorianCalendar(year + 1900, month, day, hour, min, sec);
-		}
-		// relative time, we need a context to evaluate it
-		else if (context != null && context.type == TYPE_ABSOLUTE) {
-			gc = context.getTime();
-		}
-		// how would I guess what time it was?
-		else {
-			throw new RrdException("Relative times like '" +
-					dateString + "' require proper absolute context to be evaluated");
-		}
-		gc.add(Calendar.YEAR, dyear);
-		gc.add(Calendar.MONTH, dmonth);
-		gc.add(Calendar.DAY_OF_MONTH, dday);
-		gc.add(Calendar.HOUR_OF_DAY, dhour);
-		gc.add(Calendar.MINUTE, dmin);
-		gc.add(Calendar.SECOND, dsec);
-		return gc;
-	}
-
-	/**
-	 * Returns the corresponding timestamp (seconds since Epoch). Example:<p>
-	 * <pre>
-	 * TimeParser p = new TimeParser("now-1day");
-	 * TimeSpec ts = p.parse();
-	 * System.out.println("Timestamp was: " + ts.getTimestamp();
-	 * </pre>
-	 *
-	 * @return Timestamp (in seconds, no milliseconds)
-	 * @throws RrdException Thrown if this TimeSpec object does not represent absolute time.
-	 */
-	public long getTimestamp() throws RrdException {
-		return Util.getTimestamp(getTime());
-	}
-
-	String dump() {
-		return (type == TYPE_ABSOLUTE ? "ABSTIME" : type == TYPE_START ? "START" : "END") +
-				": " + year + "/" + month + "/" + day +
-				"/" + hour + "/" + min + "/" + sec + " (" +
-				dyear + "/" + dmonth + "/" + dday +
-				"/" + dhour + "/" + dmin + "/" + dsec + ")";
-	}
-
-	/**
-	 * Use this static method to resolve relative time references and obtain the corresponding
-	 * Calendar objects. Example:<p>
-	 * <pre>
-	 * TimeParser pStart = new TimeParser("now-1month"); // starting time
-	 * TimeParser pEnd = new TimeParser("start+1week");  // ending time
-	 * TimeSpec specStart = pStart.parse();
-	 * TimeSpec specEnd = pEnd.parse();
-	 * GregorianCalendar[] gc = TimeSpec.getTimes(specStart, specEnd);
-	 * </pre>
-	 *
-	 * @param spec1 Starting time specification
-	 * @param spec2 Ending time specification
-	 * @return Two element array containing Calendar objects
-	 * @throws RrdException Thrown if relative time references cannot be resolved
-	 */
-	public static Calendar[] getTimes(TimeSpec spec1, TimeSpec spec2) throws RrdException {
-		if (spec1.type == TYPE_START || spec2.type == TYPE_END) {
-			throw new RrdException("Recursive time specifications not allowed");
-		}
-		spec1.context = spec2;
-		spec2.context = spec1;
-		return new Calendar[] {
-				spec1.getTime(),
-				spec2.getTime()
-		};
-	}
-
-	/**
-	 * Use this static method to resolve relative time references and obtain the corresponding
-	 * timestamps (seconds since epoch). Example:<p>
-	 * <pre>
-	 * TimeParser pStart = new TimeParser("now-1month"); // starting time
-	 * TimeParser pEnd = new TimeParser("start+1week");  // ending time
-	 * TimeSpec specStart = pStart.parse();
-	 * TimeSpec specEnd = pEnd.parse();
-	 * long[] ts = TimeSpec.getTimestamps(specStart, specEnd);
-	 * </pre>
-	 *
-	 * @param spec1 Starting time specification
-	 * @param spec2 Ending time specification
-	 * @return array containing two timestamps (in seconds since epoch)
-	 * @throws RrdException Thrown if relative time references cannot be resolved
-	 */
-	public static long[] getTimestamps(TimeSpec spec1, TimeSpec spec2) throws RrdException {
-		Calendar[] gcs = getTimes(spec1, spec2);
-		return new long[] {
-				Util.getTimestamp(gcs[0]), Util.getTimestamp(gcs[1])
-		};
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/core/timespec/TimeToken.java b/apps/jrobin/java/src/org/jrobin/core/timespec/TimeToken.java
deleted file mode 100644
index 64b2057e710c4ef446c264124766561dae1ef6e9..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/core/timespec/TimeToken.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.core.timespec;
-
-class TimeToken {
-	public static final int MIDNIGHT = 1;
-	public static final int NOON = 2;
-	public static final int TEATIME = 3;
-	public static final int PM = 4;
-	public static final int AM = 5;
-	public static final int YESTERDAY = 6;
-	public static final int TODAY = 7;
-	public static final int TOMORROW = 8;
-	public static final int NOW = 9;
-	public static final int START = 10;
-	public static final int END = 11;
-	public static final int SECONDS = 12;
-	public static final int MINUTES = 13;
-	public static final int HOURS = 14;
-	public static final int DAYS = 15;
-	public static final int WEEKS = 16;
-	public static final int MONTHS = 17;
-	public static final int YEARS = 18;
-	public static final int MONTHS_MINUTES = 19;
-	public static final int NUMBER = 20;
-	public static final int PLUS = 21;
-	public static final int MINUS = 22;
-	public static final int DOT = 23;
-	public static final int COLON = 24;
-	public static final int SLASH = 25;
-	public static final int ID = 26;
-	public static final int JUNK = 27;
-	public static final int JAN = 28;
-	public static final int FEB = 29;
-	public static final int MAR = 30;
-	public static final int APR = 31;
-	public static final int MAY = 32;
-	public static final int JUN = 33;
-	public static final int JUL = 34;
-	public static final int AUG = 35;
-	public static final int SEP = 36;
-	public static final int OCT = 37;
-	public static final int NOV = 38;
-	public static final int DEC = 39;
-	public static final int SUN = 40;
-	public static final int MON = 41;
-	public static final int TUE = 42;
-	public static final int WED = 43;
-	public static final int THU = 44;
-	public static final int FRI = 45;
-	public static final int SAT = 46;
-        public static final int EPOCH = 46;
-	public static final int EOF = -1;
-
-	final String value; /* token name */
-	final int id;   /* token id */
-
-	public TimeToken(String value, int id) {
-		this.value = value;
-		this.id = id;
-	}
-
-	public String toString() {
-		return value + " [" + id + "]";
-	}
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/data/Aggregates.java b/apps/jrobin/java/src/org/jrobin/data/Aggregates.java
deleted file mode 100644
index f2194b593519e118634e9b8c7b9bee5bcfb0ef9d..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/Aggregates.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-import org.jrobin.core.ConsolFuns;
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-
-/**
- * Simple class which holds aggregated values (MIN, MAX, FIRST, LAST, AVERAGE and TOTAL). You
- * don't need to create objects of this class directly. Objects of this class are returned from
- * <code>getAggregates()</code> method in
- * {@link org.jrobin.core.FetchData#getAggregates(String) FetchData} and
- * {@link DataProcessor#getAggregates(String)} DataProcessor} classes.
- */
-public class Aggregates implements ConsolFuns {
-	double min = Double.NaN, max = Double.NaN;
-	double first = Double.NaN, last = Double.NaN;
-	double average = Double.NaN, total = Double.NaN;
-        double stdev = Double.NaN, lslslope = Double.NaN;
-        double lslint = Double.NaN, lslcorrel = Double.NaN;
-
-	Aggregates() {
-		// NOP;
-	}
-
-	/**
-	 * Returns the minimal value
-	 *
-	 * @return Minimal value
-	 */
-	public double getMin() {
-		return min;
-	}
-
-	/**
-	 * Returns the maximum value
-	 *
-	 * @return Maximum value
-	 */
-	public double getMax() {
-		return max;
-	}
-
-	/**
-	 * Returns the first falue
-	 *
-	 * @return First value
-	 */
-	public double getFirst() {
-		return first;
-	}
-
-	/**
-	 * Returns the last value
-	 *
-	 * @return Last value
-	 */
-	public double getLast() {
-		return last;
-	}
-
-	/**
-	 * Returns average
-	 *
-	 * @return Average value
-	 */
-	public double getAverage() {
-		return average;
-	}
-
-	/**
-	 * Returns total value
-	 *
-	 * @return Total value
-	 */
-	public double getTotal() {
-		return total;
-	}
-
-	/**
-	 * Returns stdev value
-	 *
-	 * @return Stdev value
-	 */
-	public double getStdev() {
-		return stdev;
-	}
-
-	/**
-	 * Returns Least Squares Line Slope value
-	 *
-	 * @return lslslope value
-	 */
-	public double getLSLSlope() {
-		return stdev;
-	}
-
-	/**
-	 * Returns Least Squares Line y-intercept value
-	 *
-	 * @return lslint value
-	 */
-	public double getLSLInt() {
-		return lslint;
-	}
-
-	/**
-	 * Returns Least Squares Line Correlation Coefficient
-	 *
-	 * @return lslcorrel value
-	 */
-	public double getLSLCorrel() {
-		return lslcorrel;
-	}
-
-	/**
-	 * Returns single aggregated value for the give consolidation function
-	 *
-	 * @param consolFun Consolidation function: MIN, MAX, FIRST, LAST, AVERAGE, TOTAL. These constants
-	 *                  are conveniently defined in the {@link org.jrobin.core.ConsolFuns ConsolFuns} interface.
-	 * @return Aggregated value
-	 * @throws RrdException Thrown if unsupported consolidation function is supplied
-	 */
-	public double getAggregate(String consolFun) throws RrdException {
-		if (consolFun.equals(CF_AVERAGE)) {
-			return average;
-		}
-		else if (consolFun.equals(CF_FIRST)) {
-			return first;
-		}
-		else if (consolFun.equals(CF_LAST)) {
-			return last;
-		}
-		else if (consolFun.equals(CF_MAX)) {
-			return max;
-		}
-		else if (consolFun.equals(CF_MIN)) {
-			return min;
-		}
-		else if (consolFun.equals(CF_TOTAL)) {
-			return total;
-		}
-		else if (consolFun.equals("STDEV")) {
-			return stdev;
-		}
-		else if (consolFun.equals("LSLSLOPE")) {
-			return lslslope;
-		}
-		else if (consolFun.equals("LSLINT")) {
-			return lslint;
-		}
-		else if (consolFun.equals("LSLCORREL")) {
-			return lslcorrel;
-		}
-		else {
-			throw new RrdException("Unknown consolidation function: " + consolFun);
-		}
-	}
-
-	/**
-	 * Returns String representing all aggregated values. Just for debugging purposes.
-	 *
-	 * @return String containing all aggregated values
-	 */
-	public String dump() {
-		return "MIN=" + Util.formatDouble(min) + ", MAX=" + Util.formatDouble(max) + "\n" +
-				"FIRST=" + Util.formatDouble(first) + ", LAST=" + Util.formatDouble(last) + "\n" +
-				"AVERAGE=" + Util.formatDouble(average) + ", TOTAL=" + Util.formatDouble(total);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/Aggregator.java b/apps/jrobin/java/src/org/jrobin/data/Aggregator.java
deleted file mode 100644
index 05ceccbbc9f34b8021a57c6cd7b929f3d47f1fe1..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/Aggregator.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-import org.jrobin.core.ConsolFuns;
-import org.jrobin.core.Util;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-class Aggregator implements ConsolFuns {
-	private long timestamps[], step;
-	private double[] values;
-
-	Aggregator(long[] timestamps, double[] values) {
-		assert timestamps.length == values.length: "Incompatible timestamps/values arrays (unequal lengths)";
-		assert timestamps.length >= 2: "At least two timestamps must be supplied";
-		this.timestamps = timestamps;
-		this.values = values;
-		this.step = timestamps[1] - timestamps[0];
-	}
-
-	Aggregates getAggregates(long tStart, long tEnd) {
-		Aggregates agg = new Aggregates();
-                int cnt = 0;
-                int lslstep = 0;
-		boolean firstFound = false;
-                double SUMx, SUMy, SUMxy, SUMxx, SUMyy;
-                SUMx = 0.0;
-                SUMy = 0.0;
-                SUMxy = 0.0;
-                SUMxx = 0.0;
-                SUMyy = 0.0;
-
-                for (int i = 0; i < timestamps.length; i++) {
-			long left = Math.max(timestamps[i] - step, tStart);
-			long right = Math.min(timestamps[i], tEnd);
-			long delta = right - left;
-
-			// delta is only >= 0 when the timestamp for a given buck is within the range of tStart and tEnd
-			if (delta >= 0) {
-				double value = values[i];
-				agg.min = Util.min(agg.min, value);
-				agg.max = Util.max(agg.max, value);
-				if (!firstFound) {
-					agg.first = value;
-					firstFound = true;
-					agg.last = value;
-				} else if (delta >= step) {  // an entire bucket is included in this range
-					agg.last = value;
-
-					/*
-					 * Algorithmically, we're only updating last if it's either the first
-					 * bucket encountered, or it's a "full" bucket.
-
-					if ( !isInRange(tEnd, left, right) ||
-							 (isInRange(tEnd, left, right) && !Double.isNaN(value))
-							 ) {
-							agg.last = value;
-						}
-					*/
-
-				}
-				if (!Double.isNaN(value)) {
-                                        cnt++;
-                                        SUMx += lslstep;
-                                        SUMxx += lslstep * lslstep;
-                                        SUMy  = Util.sum(SUMy, value);
-                                        SUMxy = Util.sum(SUMxy, lslstep * value);
-                                        SUMyy = Util.sum(SUMyy, value * value);
-				}
-                                lslstep ++;
-			}
-		}
-		agg.average = cnt > 0 ? (SUMy / cnt) : Double.NaN;
-
-                // Work on STDEV
-                if (cnt > 0) {
-                    double stdevSum = 0.0;
-                    for (int i = 0; i < timestamps.length; i++) {
-                        long left = Math.max(timestamps[i] - step, tStart);
-			long right = Math.min(timestamps[i], tEnd);
-			long delta = right - left;
-
-			// delta is only >= 0 when the timestamp for a given buck is within the range of tStart and tEnd
-			if (delta >= 0) {
-				double value = values[i];
-				if (!Double.isNaN(value)) {
-                                        stdevSum = Util.sum(stdevSum, Math.pow((value - agg.average), 2.0));
-				}
-			}
-                    }
-                    agg.stdev = Math.pow(stdevSum / cnt, 0.5);
-
-                    /* Bestfit line by linear least squares method */
-                    agg.lslslope = (SUMx * SUMy - cnt * SUMxy) / (SUMx * SUMx - cnt * SUMxx);
-                    agg.lslint = (SUMy - agg.lslslope * SUMx) / cnt;
-                    agg.lslcorrel =
-                       (SUMxy - (SUMx * SUMy) / cnt) /
-                       Math.sqrt((SUMxx - (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt));
-                }
-                agg.total = SUMy * step;
-
-                return agg;
-	}
-
-        double getPercentile(long tStart, long tEnd, double percentile) {
-            return getPercentile(tStart, tEnd, percentile, false);
-        }
-
-	double getPercentile(long tStart, long tEnd, double percentile, boolean includenan) {
-		List<Double> valueList = new ArrayList<Double>();
-		// create a list of included datasource values (different from NaN)
-		for (int i = 0; i < timestamps.length; i++) {
-			long left = Math.max(timestamps[i] - step, tStart);
-			long right = Math.min(timestamps[i], tEnd);
-			if (right > left && (!Double.isNaN(values[i]) || includenan)) {
-				valueList.add(values[i]);
-			}
-		}
-		// create an array to work with
-		int count = valueList.size();
-		if (count > 1) {
-			double[] valuesCopy = new double[count];
-			for (int i = 0; i < count; i++) {
-				valuesCopy[i] = valueList.get(i);
-			}
-			// sort array
-			Arrays.sort(valuesCopy);
-			// skip top (100% - percentile) values
-			double topPercentile = (100.0 - percentile) / 100.0;
-			count -= (int) Math.ceil(count * topPercentile);
-			// if we have anything left...
-			if (count > 0) {
-				return valuesCopy[count - 1];
-			}
-		}
-		// not enough data available
-		return Double.NaN;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/CDef.java b/apps/jrobin/java/src/org/jrobin/data/CDef.java
deleted file mode 100644
index 639c36d0c59410b1f89e31e1c6b4228f3eebf8bc..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/CDef.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-class CDef extends Source {
-	private String rpnExpression;
-
-	CDef(String name, String rpnExpression) {
-		super(name);
-		this.rpnExpression = rpnExpression;
-	}
-
-	String getRpnExpression() {
-		return rpnExpression;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/CubicSplineInterpolator.java b/apps/jrobin/java/src/org/jrobin/data/CubicSplineInterpolator.java
deleted file mode 100644
index d86666805398af5a4266faacd75e797c04edb19f..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/CubicSplineInterpolator.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.data;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-
-import java.util.Calendar;
-import java.util.Date;
-
-/**
- * Class used to interpolate datasource values from the collection of (timestamp, values)
- * points using natural cubic spline interpolation.
- * <p>
- * <b>WARNING</b>: So far, this class cannot handle NaN datasource values
- * (an exception will be thrown by the constructor). Future releases might change this.
- */
-public class CubicSplineInterpolator extends Plottable {
-	private double[] x;
-	private double[] y;
-
-	// second derivates come here
-	private double[] y2;
-
-	// internal spline variables
-	private int n, klo, khi;
-
-	/**
-	 * Creates cubic spline interpolator from arrays of timestamps and corresponding
-	 * datasource values.
-	 *
-	 * @param timestamps timestamps in seconds
-	 * @param values	 corresponding datasource values
-	 * @throws RrdException Thrown if supplied arrays do not contain at least 3 values, or if
-	 *                      timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
-	 */
-	public CubicSplineInterpolator(long[] timestamps, double[] values) throws RrdException {
-		this.x = new double[timestamps.length];
-		for (int i = 0; i < timestamps.length; i++) {
-			this.x[i] = timestamps[i];
-		}
-		this.y = values;
-		validate();
-		spline();
-	}
-
-	/**
-	 * Creates cubic spline interpolator from arrays of Date objects and corresponding
-	 * datasource values.
-	 *
-	 * @param dates  Array of Date objects
-	 * @param values corresponding datasource values
-	 * @throws RrdException Thrown if supplied arrays do not contain at least 3 values, or if
-	 *                      timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
-	 */
-	public CubicSplineInterpolator(Date[] dates, double[] values) throws RrdException {
-		this.x = new double[dates.length];
-		for (int i = 0; i < dates.length; i++) {
-			this.x[i] = Util.getTimestamp(dates[i]);
-		}
-		this.y = values;
-		validate();
-		spline();
-	}
-
-	/**
-	 * Creates cubic spline interpolator from arrays of GregorianCalendar objects and corresponding
-	 * datasource values.
-	 *
-	 * @param dates  Array of GregorianCalendar objects
-	 * @param values corresponding datasource values
-	 * @throws RrdException Thrown if supplied arrays do not contain at least 3 values, or if
-	 *                      timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
-	 */
-	public CubicSplineInterpolator(Calendar[] dates, double[] values) throws RrdException {
-		this.x = new double[dates.length];
-		for (int i = 0; i < dates.length; i++) {
-			this.x[i] = Util.getTimestamp(dates[i]);
-		}
-		this.y = values;
-		validate();
-		spline();
-	}
-
-	/**
-	 * Creates cubic spline interpolator for an array of 2D-points.
-	 *
-	 * @param x x-axis point coordinates
-	 * @param y y-axis point coordinates
-	 * @throws RrdException Thrown if supplied arrays do not contain at least 3 values, or if
-	 *                      timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
-	 */
-	public CubicSplineInterpolator(double[] x, double[] y) throws RrdException {
-		this.x = x;
-		this.y = y;
-		validate();
-		spline();
-	}
-
-	private void validate() throws RrdException {
-		boolean ok = true;
-		if (x.length != y.length || x.length < 3) {
-			ok = false;
-		}
-		for (int i = 0; i < x.length - 1 && ok; i++) {
-			if (x[i] >= x[i + 1] || Double.isNaN(y[i])) {
-				ok = false;
-			}
-		}
-		if (!ok) {
-			throw new RrdException("Invalid plottable data supplied");
-		}
-	}
-
-	private void spline() {
-		n = x.length;
-		y2 = new double[n];
-		double[] u = new double[n - 1];
-		y2[0] = y2[n - 1] = 0.0;
-		u[0] = 0.0; // natural spline
-		for (int i = 1; i <= n - 2; i++) {
-			double sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
-			double p = sig * y2[i - 1] + 2.0;
-			y2[i] = (sig - 1.0) / p;
-			u[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
-			u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
-		}
-		for (int k = n - 2; k >= 0; k--) {
-			y2[k] = y2[k] * y2[k + 1] + u[k];
-		}
-		// prepare everything for getValue()
-		klo = 0;
-		khi = n - 1;
-	}
-
-	/**
-	 * Calculates spline-interpolated y-value for the corresponding x-value. Call
-	 * this if you need spline-interpolated values in your code.
-	 *
-	 * @param xval x-value
-	 * @return inteprolated y-value
-	 */
-	public double getValue(double xval) {
-		if (xval < x[0] || xval > x[n - 1]) {
-			return Double.NaN;
-		}
-		if (xval < x[klo] || xval > x[khi]) {
-			// out of bounds
-			klo = 0;
-			khi = n - 1;
-		}
-		while (khi - klo > 1) {
-			// find bounding interval using bisection method
-			int k = (khi + klo) >>> 1;
-			if (x[k] > xval) {
-				khi = k;
-			}
-			else {
-				klo = k;
-			}
-		}
-		double h = x[khi] - x[klo];
-		double a = (x[khi] - xval) / h;
-		double b = (xval - x[klo]) / h;
-		return a * y[klo] + b * y[khi] +
-				((a * a * a - a) * y2[klo] + (b * b * b - b) * y2[khi]) * (h * h) / 6.0;
-	}
-
-	/**
-	 * Method overriden from the base class. This method will be called by the framework. Call
-	 * this method only if you need spline-interpolated values in your code.
-	 *
-	 * @param timestamp timestamp in seconds
-	 * @return inteprolated datasource value
-	 */
-	public double getValue(long timestamp) {
-		return getValue((double) timestamp);
-	}
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/DataProcessor.java b/apps/jrobin/java/src/org/jrobin/data/DataProcessor.java
deleted file mode 100644
index a258068342deaf12ea6f13250a794223b44f6ede..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/DataProcessor.java
+++ /dev/null
@@ -1,936 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-import org.jrobin.core.*;
-
-import java.io.IOException;
-import java.util.*;
-
-/**
- * Class which should be used for all calculations based on the data fetched from RRD files. This class
- * supports ordinary DEF datasources (defined in RRD files), CDEF datasources (RPN expressions evaluation),
- * SDEF (static datasources - extension of JRobin) and PDEF (plottables, see
- * {@link Plottable Plottable} for more information.
- * <p>
- * Typical class usage:
- * <p>
- * <pre>
- * final long t1 = ...
- * final long t2 = ...
- * DataProcessor dp = new DataProcessor(t1, t2);
- * // DEF datasource
- * dp.addDatasource("x", "demo.rrd", "some_source", "AVERAGE");
- * // DEF datasource
- * dp.addDatasource("y", "demo.rrd", "some_other_source", "AVERAGE");
- * // CDEF datasource, z = (x + y) / 2
- * dp.addDatasource("z", "x,y,+,2,/");
- * // ACTION!
- * dp.processData();
- * // Dump calculated values
- * System.out.println(dp.dump());
- * </pre>
- */
-public class DataProcessor implements ConsolFuns {
-	/**
-	 * Constant representing the default number of pixels on a JRobin graph (will be used if
-	 * no other value is specified with {@link #setStep(long) setStep()} method.
-	 */
-	public static final int DEFAULT_PIXEL_COUNT = 600;
-	private static final double DEFAULT_PERCENTILE = 95.0; // %
-
-	private int pixelCount = DEFAULT_PIXEL_COUNT;
-
-	/**
-	 * Constant that defines the default {@link RrdDbPool} usage policy. Defaults to <code>false</code>
-	 * (i.e. the pool will not be used to fetch data from RRD files)
-	 */
-	public static final boolean DEFAULT_POOL_USAGE_POLICY = false;
-	private boolean poolUsed = DEFAULT_POOL_USAGE_POLICY;
-
-	private final long tStart;
-	private long tEnd, timestamps[];
-	private long lastRrdArchiveUpdateTime = 0;
-	// this will be adjusted later
-	private long step = 0;
-	// resolution to be used for RRD fetch operation
-	private long fetchRequestResolution = 1;
-
-	// the order is important, ordinary HashMap is unordered
-	private Map<String, Source> sources = new LinkedHashMap<String, Source>();
-
-	private Def[] defSources;
-
-	/**
-	 * Creates new DataProcessor object for the given time span. Ending timestamp may be set to zero.
-	 * In that case, the class will try to find the optimal ending timestamp based on the last update time of
-	 * RRD files processed with the {@link #processData()} method.
-	 *
-	 * @param t1 Starting timestamp in seconds without milliseconds
-	 * @param t2 Ending timestamp in seconds without milliseconds
-	 * @throws RrdException Thrown if invalid timestamps are supplied
-	 */
-	public DataProcessor(long t1, long t2) throws RrdException {
-		if ((t1 < t2 && t1 > 0 && t2 > 0) || (t1 > 0 && t2 == 0)) {
-			this.tStart = t1;
-			this.tEnd = t2;
-		}
-		else {
-			throw new RrdException("Invalid timestamps specified: " + t1 + ", " + t2);
-		}
-	}
-
-	/**
-	 * Creates new DataProcessor object for the given time span. Ending date may be set to null.
-	 * In that case, the class will try to find optimal ending date based on the last update time of
-	 * RRD files processed with the {@link #processData()} method.
-	 *
-	 * @param d1 Starting date
-	 * @param d2 Ending date
-	 * @throws RrdException Thrown if invalid timestamps are supplied
-	 */
-	public DataProcessor(Date d1, Date d2) throws RrdException {
-		this(Util.getTimestamp(d1), d2 != null ? Util.getTimestamp(d2) : 0);
-	}
-
-	/**
-	 * Creates new DataProcessor object for the given time span. Ending date may be set to null.
-	 * In that case, the class will try to find optimal ending date based on the last update time of
-	 * RRD files processed with the {@link #processData()} method.
-	 *
-	 * @param gc1 Starting Calendar date
-	 * @param gc2 Ending Calendar date
-	 * @throws RrdException Thrown if invalid timestamps are supplied
-	 */
-	public DataProcessor(Calendar gc1, Calendar gc2) throws RrdException {
-		this(Util.getTimestamp(gc1), gc2 != null ? Util.getTimestamp(gc2) : 0);
-	}
-
-	/**
-	 * Returns boolean value representing {@link org.jrobin.core.RrdDbPool RrdDbPool} usage policy.
-	 *
-	 * @return true, if the pool will be used internally to fetch data from RRD files, false otherwise.
-	 */
-	public boolean isPoolUsed() {
-		return poolUsed;
-	}
-
-	/**
-	 * Sets the {@link org.jrobin.core.RrdDbPool RrdDbPool} usage policy.
-	 *
-	 * @param poolUsed true, if the pool should be used to fetch data from RRD files, false otherwise.
-	 */
-	public void setPoolUsed(boolean poolUsed) {
-		this.poolUsed = poolUsed;
-	}
-
-	/**
-	 * Sets the number of pixels (target graph width). This number is used only to calculate pixel coordinates
-	 * for JRobin graphs (methods {@link #getValuesPerPixel(String)} and {@link #getTimestampsPerPixel()}),
-	 * but has influence neither on datasource values calculated with the
-	 * {@link #processData()} method nor on aggregated values returned from {@link #getAggregates(String)}
-	 * and similar methods. In other words, aggregated values will not change once you decide to change
-	 * the dimension of your graph.
-	 * <p>
-	 * The default number of pixels is defined by constant {@link #DEFAULT_PIXEL_COUNT}
-	 * and can be changed with a {@link #setPixelCount(int)} method.
-	 *
-	 * @param pixelCount The number of pixels. If you process RRD data in order to display it on the graph,
-	 *                   this should be the width of your graph.
-	 */
-	public void setPixelCount(int pixelCount) {
-		this.pixelCount = pixelCount;
-	}
-
-	/**
-	 * Returns the number of pixels (target graph width). See {@link #setPixelCount(int)} for more information.
-	 *
-	 * @return Target graph width
-	 */
-	public int getPixelCount() {
-		return pixelCount;
-	}
-
-	/**
-	 * Roughly corresponds to the --step option in RRDTool's graph/xport commands. Here is an explanation borrowed
-	 * from RRDTool:
-	 * <p>
-	 * <i>"By default rrdgraph calculates the width of one pixel in the time
-	 * domain and tries to get data at that resolution from the RRD. With
-	 * this switch you can override this behavior. If you want rrdgraph to
-	 * get data at 1 hour resolution from the RRD, then you can set the
-	 * step to 3600 seconds. Note, that a step smaller than 1 pixel will
-	 * be silently ignored."</i>
-	 * <p>
-	 * I think this option is not that useful, but it's here just for compatibility.
-	 * <p>
-	 * @param step Time step at which data should be fetched from RRD files. If this method is not used,
-	 *             the step will be equal to the smallest RRD step of all processed RRD files. If no RRD file is processed,
-	 *             the step will be roughly equal to the with of one graph pixel (in seconds).
-	 */
-	public void setStep(long step) {
-		this.step = step;
-	}
-
-	/**
-	 * Returns the time step used for data processing. Initially, this method returns zero.
-	 * Once {@link #processData()} is finished, the method will return the real value used for
-	 * all internal computations. Roughly corresponds to the --step option in RRDTool's graph/xport commands.
-	 *
-	 * @return Step used for data processing.
-	 */
-	public long getStep() {
-		return step;
-	}
-
-	/**
-	 * Returns desired RRD archive step (reslution) in seconds to be used while fetching data
-	 * from RRD files. In other words, this value will used as the last parameter of
-	 * {@link RrdDb#createFetchRequest(String, long, long, long) RrdDb.createFetchRequest()} method
-	 * when this method is called internally by this DataProcessor.
-	 *
-	 * @return Desired archive step (fetch resolution) in seconds.
-	 */
-	public long getFetchRequestResolution() {
-		return fetchRequestResolution;
-	}
-
-	/**
-	 * Sets desired RRD archive step in seconds to be used internally while fetching data
-	 * from RRD files. In other words, this value will used as the last parameter of
-	 * {@link RrdDb#createFetchRequest(String, long, long, long) RrdDb.createFetchRequest()} method
-	 * when this method is called internally by this DataProcessor. If this method is never called, fetch
-	 * request resolution defaults to 1 (smallest possible archive step will be chosen automatically).
-	 *
-	 * @param fetchRequestResolution Desired archive step (fetch resoltuion) in seconds.
-	 */
-	public void setFetchRequestResolution(long fetchRequestResolution) {
-		this.fetchRequestResolution = fetchRequestResolution;
-	}
-
-	/**
-	 * Returns ending timestamp. Basically, this value is equal to the ending timestamp
-	 * specified in the constructor. However, if the ending timestamps was zero, it
-	 * will be replaced with the real timestamp when the {@link #processData()} method returns. The real
-	 * value will be calculated from the last update times of processed RRD files.
-	 *
-	 * @return Ending timestamp in seconds
-	 */
-	public long getEndingTimestamp() {
-		return tEnd;
-	}
-
-	/**
-	 * Returns consolidated timestamps created with the {@link #processData()} method.
-	 *
-	 * @return array of timestamps in seconds
-	 * @throws RrdException thrown if timestamps are not calculated yet
-	 */
-	public long[] getTimestamps() throws RrdException {
-		if (timestamps == null) {
-			throw new RrdException("Timestamps not calculated yet");
-		}
-		else {
-			return timestamps;
-		}
-	}
-
-	/**
-	 * Returns calculated values for a single datasource. Corresponding timestamps can be obtained from
-	 * the {@link #getTimestamps()} method.
-	 *
-	 * @param sourceName Datasource name
-	 * @return an array of datasource values
-	 * @throws RrdException Thrown if invalid datasource name is specified,
-	 *                      or if datasource values are not yet calculated (method {@link #processData()}
-	 *                      was not called)
-	 */
-	public double[] getValues(String sourceName) throws RrdException {
-		Source source = getSource(sourceName);
-		double[] values = source.getValues();
-		if (values == null) {
-			throw new RrdException("Values not available for source [" + sourceName + "]");
-		}
-		return values;
-	}
-
-	/**
-	 * Returns single aggregated value for a single datasource.
-	 *
-	 * @param sourceName Datasource name
-	 * @param consolFun  Consolidation function to be applied to fetched datasource values.
-	 *                   Valid consolidation functions are MIN, MAX, LAST, FIRST, AVERAGE and TOTAL
-	 *                   (these string constants are conveniently defined in the {@link ConsolFuns} class)
-	 * @return MIN, MAX, LAST, FIRST, AVERAGE or TOTAL value calculated from the data
-	 *         for the given datasource name
-	 * @throws RrdException Thrown if invalid datasource name is specified,
-	 *                      or if datasource values are not yet calculated (method {@link #processData()}
-	 *                      was not called)
-	 */
-	public double getAggregate(String sourceName, String consolFun) throws RrdException {
-		Source source = getSource(sourceName);
-		return source.getAggregates(tStart, tEnd).getAggregate(consolFun);
-	}
-
-	/**
-	 * Returns all (MIN, MAX, LAST, FIRST, AVERAGE and TOTAL) aggregated values for a single datasource.
-	 *
-	 * @param sourceName Datasource name
-	 * @return Object containing all aggregated values
-	 * @throws RrdException Thrown if invalid datasource name is specified,
-	 *                      or if datasource values are not yet calculated (method {@link #processData()}
-	 *                      was not called)
-	 */
-	public Aggregates getAggregates(String sourceName) throws RrdException {
-		Source source = getSource(sourceName);
-		return source.getAggregates(tStart, tEnd);
-	}
-
-	/**
-	 * This method is just an alias for {@link #getPercentile(String)} method.
-	 * <p>
-	 * Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.
-	 * <p>
-	 * The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
-	 * of source data is discarded. It is used as a measure of the peak value used when one discounts
-	 * a fair amount for transitory spikes. This makes it markedly different from the average.
-	 * <p>
-	 * Read more about this topic at
-	 * <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or
-	 * <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
-	 *
-	 * @param sourceName Datasource name
-	 * @return 95th percentile of fetched source values
-	 * @throws RrdException Thrown if invalid source name is supplied
-	 */
-	public double get95Percentile(String sourceName) throws RrdException {
-		return getPercentile(sourceName);
-	}
-
-	/**
-	 * Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.
-	 * <p>
-	 * The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
-	 * of source data is discarded. It is used as a measure of the peak value used when one discounts
-	 * a fair amount for transitory spikes. This makes it markedly different from the average.
-	 * <p>
-	 * Read more about this topic at
-	 * <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or
-	 * <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
-	 *
-	 * @param sourceName Datasource name
-	 * @return 95th percentile of fetched source values
-	 * @throws RrdException Thrown if invalid source name is supplied
-	 */
-	public double getPercentile(String sourceName) throws RrdException {
-		return getPercentile(sourceName, DEFAULT_PERCENTILE);
-	}
-
-	/**
-	 * The same as {@link #getPercentile(String)} but with a possibility to define custom percentile boundary
-	 * (different from 95).
-	 *
-	 * @param sourceName Datasource name.
-	 * @param percentile Boundary percentile. Value of 95 (%) is suitable in most cases, but you are free
-	 *                   to provide your own percentile boundary between zero and 100.
-	 * @return Requested percentile of fetched source values
-	 * @throws RrdException Thrown if invalid sourcename is supplied, or if the percentile value makes no sense.
-	 */
-	public double getPercentile(String sourceName, double percentile) throws RrdException {
-		if (percentile <= 0.0 || percentile > 100.0) {
-			throw new RrdException("Invalid percentile [" + percentile + "], should be between 0 and 100");
-		}
-		Source source = getSource(sourceName);
-		return source.getPercentile(tStart, tEnd, percentile);
-	}
-
-	/**
-	 * Returns array of datasource names defined in this DataProcessor.
-	 *
-	 * @return array of datasource names
-	 */
-	public String[] getSourceNames() {
-		return sources.keySet().toArray(new String[0]);
-	}
-
-	/**
-	 * Returns an array of all datasource values for all datasources. Each row in this two-dimensional
-	 * array represents an array of calculated values for a single datasource. The order of rows is the same
-	 * as the order in which datasources were added to this DataProcessor object.
-	 *
-	 * @return All datasource values for all datasources. The first index is the index of the datasource,
-	 *         the second index is the index of the datasource value. The number of datasource values is equal
-	 *         to the number of timestamps returned with {@link #getTimestamps()}  method.
-	 * @throws RrdException Thrown if invalid datasource name is specified,
-	 *                      or if datasource values are not yet calculated (method {@link #processData()}
-	 *                      was not called)
-	 */
-	public double[][] getValues() throws RrdException {
-		String[] names = getSourceNames();
-		double[][] values = new double[names.length][];
-		for (int i = 0; i < names.length; i++) {
-			values[i] = getValues(names[i]);
-		}
-		return values;
-	}
-
-	private Source getSource(String sourceName) throws RrdException {
-		Source source = sources.get(sourceName);
-		if (source != null) {
-			return source;
-		}
-		throw new RrdException("Unknown source: " + sourceName);
-	}
-
-	/////////////////////////////////////////////////////////////////
-	// DATASOURCE DEFINITIONS
-	/////////////////////////////////////////////////////////////////
-
-	/**
-	 * Adds a custom, {@link org.jrobin.data.Plottable plottable} datasource (<b>PDEF</b>).
-	 * The datapoints should be made available by a class extending
-	 * {@link org.jrobin.data.Plottable Plottable} class.
-	 * <p>
-	 *
-	 * @param name	  source name.
-	 * @param plottable class that extends Plottable class and is suited for graphing.
-	 */
-	public void addDatasource(String name, Plottable plottable) {
-		PDef pDef = new PDef(name, plottable);
-		sources.put(name, pDef);
-	}
-
-	/**
-	 * Adds complex source (<b>CDEF</b>).
-	 * Complex sources are evaluated using the supplied <code>RPN</code> expression.
-	 * <p>
-	 * Complex source <code>name</code> can be used:
-	 * <ul>
-	 * <li>To specify sources for line, area and stack plots.</li>
-	 * <li>To define other complex sources.</li>
-	 * </ul>
-	 * <p>
-	 * JRobin supports the following RPN functions, operators and constants: +, -, *, /,
-	 * %, SIN, COS, LOG, EXP, FLOOR, CEIL, ROUND, POW, ABS, SQRT, RANDOM, LT, LE, GT, GE, EQ,
-	 * IF, MIN, MAX, LIMIT, DUP, EXC, POP, UN, UNKN, NOW, TIME, PI, E,
-	 * AND, OR, XOR, PREV, PREV(sourceName), INF, NEGINF, STEP, YEAR, MONTH, DATE,
-	 * HOUR, MINUTE, SECOND, WEEK, SIGN and RND.
-	 * <p>
-	 * JRobin does not force you to specify at least one simple source name as RRDTool.
-	 * <p>
-	 * For more details on RPN see RRDTool's
-	 * <a href="http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/manual/rrdgraph.html" target="man">
-	 * rrdgraph man page</a>.
-	 *
-	 * @param name		  source name.
-	 * @param rpnExpression RPN expression containig comma (or space) delimited simple and complex
-	 *                      source names, RPN constants, functions and operators.
-	 */
-	public void addDatasource(String name, String rpnExpression) {
-		CDef cDef = new CDef(name, rpnExpression);
-		sources.put(name, cDef);
-	}
-
-	/**
-	 * Adds static source (<b>SDEF</b>). Static sources are the result of a consolidation function applied
-	 * to *any* other source that has been defined previously.
-	 *
-	 * @param name	  source name.
-	 * @param defName   Name of the datasource to calculate the value from.
-	 * @param consolFun Consolidation function to use for value calculation
-	 */
-	public void addDatasource(String name, String defName, String consolFun) {
-		SDef sDef = new SDef(name, defName, consolFun);
-		sources.put(name, sDef);
-	}
-
-	/**
-	 * <p>Adds simple datasource (<b>DEF</b>). Simple source <code>name</code>
-	 * can be used:</p>
-	 * <ul>
-	 * <li>To specify sources for line, area and stack plots.</li>
-	 * <li>To define complex sources
-	 * </ul>
-	 *
-	 * @param name	   source name.
-	 * @param file	   Path to RRD file.
-	 * @param dsName	 Datasource name defined in the RRD file.
-	 * @param consolFunc Consolidation function that will be used to extract data from the RRD
-	 *                   file ("AVERAGE", "MIN", "MAX" or "LAST" - these string constants are conveniently defined
-	 *                   in the {@link org.jrobin.core.ConsolFuns ConsolFuns} class).
-	 */
-	public void addDatasource(String name, String file, String dsName, String consolFunc) {
-		Def def = new Def(name, file, dsName, consolFunc);
-		sources.put(name, def);
-	}
-
-	/**
-	 * <p>Adds simple source (<b>DEF</b>). Source <code>name</code> can be used:</p>
-	 * <ul>
-	 * <li>To specify sources for line, area and stack plots.</li>
-	 * <li>To define complex sources
-	 * </ul>
-	 *
-	 * @param name	   Source name.
-	 * @param file	   Path to RRD file.
-	 * @param dsName	 Data source name defined in the RRD file.
-	 * @param consolFunc Consolidation function that will be used to extract data from the RRD
-	 *                   file ("AVERAGE", "MIN", "MAX" or "LAST" - these string constants are conveniently defined
-	 *                   in the {@link org.jrobin.core.ConsolFuns ConsolFuns} class).
-	 * @param backend	Name of the RrdBackendFactory that should be used for this RrdDb.
-	 */
-	public void addDatasource(String name, String file, String dsName, String consolFunc, String backend) {
-		Def def = new Def(name, file, dsName, consolFunc, backend);
-		sources.put(name, def);
-	}
-
-	/**
-	 * Adds DEF datasource with datasource values already available in the FetchData object. This method is
-	 * used internally by JRobin and probably has no purpose outside of it.
-	 *
-	 * @param name	  Source name.
-	 * @param fetchData Fetched data containing values for the given source name.
-	 */
-	public void addDatasource(String name, FetchData fetchData) {
-		Def def = new Def(name, fetchData);
-		sources.put(name, def);
-	}
-
-	/**
-         * Creates a new VDEF datasource that performs a percentile calculation on an
-         * another named datasource to yield a single value.
-         *
-         * Requires that the other datasource has already been defined; otherwise, it'll
-         * end up with no data
-         *
-         * @param name - the new virtual datasource name
-         * @param sourceName - the datasource from which to extract the percentile.  Must be a previously
-         *                     defined virtual datasource
-         * @param percentile - the percentile to extract from the source datasource
-         */
-	public void addDatasource(String name, String sourceName, double percentile) {
-	    Source source = sources.get(sourceName);
-	    sources.put(name, new PercentileDef(name, source, percentile));
-	}
-
-	/**
-         * Creates a new VDEF datasource that performs a percentile calculation on an
-         * another named datasource to yield a single value.
-         *
-         * Requires that the other datasource has already been defined; otherwise, it'll
-         * end up with no data
-         *
-         * @param name - the new virtual datasource name
-         * @param sourceName - the datasource from which to extract the percentile.  Must be a previously
-         *                     defined virtual datasource
-         * @param percentile - the percentile to extract from the source datasource
-         * @param ignorenan - true if we include Double.NaN
-         */
-	public void addDatasource(String name, String sourceName, double percentile, boolean ignorenan) {
-	    Source source = sources.get(sourceName);
-	    sources.put(name, new PercentileDef(name, source, percentile, ignorenan));
-	}
-
-	/////////////////////////////////////////////////////////////////
-	// CALCULATIONS
-	/////////////////////////////////////////////////////////////////
-
-	/**
-	 * Method that should be called once all datasources are defined. Data will be fetched from
-	 * RRD files, RPN expressions will be calculated, etc.
-	 *
-	 * @throws IOException  Thrown in case of I/O error (while fetching data from RRD files)
-	 * @throws RrdException Thrown in case of JRobin specific error
-	 */
-	public void processData() throws IOException, RrdException {
-		extractDefs();
-		fetchRrdData();
-		fixZeroEndingTimestamp();
-		chooseOptimalStep();
-		createTimestamps();
-		assignTimestampsToSources();
-		normalizeRrdValues();
-		calculateNonRrdSources();
-	}
-
-	/**
-	 * Method used to calculate datasource values which should be presented on the graph
-	 * based on the desired graph width. Each value returned represents a single pixel on the graph.
-	 * Corresponding timestamp can be found in the array returned from {@link #getTimestampsPerPixel()}
-	 * method.
-	 *
-	 * @param sourceName Datasource name
-	 * @param pixelCount Graph width
-	 * @return Per-pixel datasource values
-	 * @throws RrdException Thrown if datasource values are not yet calculated (method {@link #processData()}
-	 *                      was not called)
-	 */
-	public double[] getValuesPerPixel(String sourceName, int pixelCount) throws RrdException {
-		setPixelCount(pixelCount);
-		return getValuesPerPixel(sourceName);
-	}
-
-	/**
-	 * Method used to calculate datasource values which should be presented on the graph
-	 * based on the graph width set with a {@link #setPixelCount(int)} method call.
-	 * Each value returned represents a single pixel on the graph. Corresponding timestamp can be
-	 * found in the array returned from {@link #getTimestampsPerPixel()} method.
-	 *
-	 * @param sourceName Datasource name
-	 * @return Per-pixel datasource values
-	 * @throws RrdException Thrown if datasource values are not yet calculated (method {@link #processData()}
-	 *                      was not called)
-	 */
-	public double[] getValuesPerPixel(String sourceName) throws RrdException {
-		double[] values = getValues(sourceName);
-		double[] pixelValues = new double[pixelCount];
-		Arrays.fill(pixelValues, Double.NaN);
-		long span = tEnd - tStart;
-		// this is the ugliest nested loop I have ever made
-		for (int pix = 0, ref = 0; pix < pixelCount; pix++) {
-			double t = tStart + (double) (span * pix) / (double) (pixelCount - 1);
-			while (ref < timestamps.length) {
-				if (t <= timestamps[ref] - step) {
-					// too left, nothing to do, already NaN
-					break;
-				}
-				else if (t <= timestamps[ref]) {
-					// in brackets, get this value
-					pixelValues[pix] = values[ref];
-					break;
-				}
-				else {
-					// too right
-					ref++;
-				}
-			}
-		}
-		return pixelValues;
-	}
-
-	/**
-	 * Calculates timestamps which correspond to individual pixels on the graph.
-	 *
-	 * @param pixelCount Graph width
-	 * @return Array of timestamps
-	 */
-	public long[] getTimestampsPerPixel(int pixelCount) {
-		setPixelCount(pixelCount);
-		return getTimestampsPerPixel();
-	}
-
-	/**
-	 * Calculates timestamps which correspond to individual pixels on the graph
-	 * based on the graph width set with a {@link #setPixelCount(int)} method call.
-	 *
-	 * @return Array of timestamps
-	 */
-	public long[] getTimestampsPerPixel() {
-		long[] times = new long[pixelCount];
-		long span = tEnd - tStart;
-		for (int i = 0; i < pixelCount; i++) {
-			times[i] = Math.round(tStart + (double) (span * i) / (double) (pixelCount - 1));
-		}
-		return times;
-	}
-
-	/**
-	 * Dumps timestamps and values of all datasources in a tabelar form. Very useful for debugging.
-	 *
-	 * @return Dumped object content.
-	 * @throws RrdException Thrown if nothing is calculated so far (the method {@link #processData()}
-	 *                      was not called).
-	 */
-	public String dump() throws RrdException {
-		String[] names = getSourceNames();
-		double[][] values = getValues();
-		StringBuilder buffer = new StringBuilder();
-		buffer.append(format("timestamp", 12));
-		for (String name : names) {
-			buffer.append(format(name, 20));
-		}
-		buffer.append("\n");
-		for (int i = 0; i < timestamps.length; i++) {
-			buffer.append(format("" + timestamps[i], 12));
-			for (int j = 0; j < names.length; j++) {
-				buffer.append(format(Util.formatDouble(values[j][i]), 20));
-			}
-			buffer.append("\n");
-		}
-		return buffer.toString();
-	}
-
-	/**
-	 * Returns time when last RRD archive was updated (all RRD files are considered).
-	 *
-	 * @return Last archive update time for all RRD files in this DataProcessor
-	 */
-	public long getLastRrdArchiveUpdateTime() {
-		return lastRrdArchiveUpdateTime;
-	}
-
-	// PRIVATE METHODS
-
-	private void extractDefs() {
-		List<Def> defList = new ArrayList<Def>();
-		for (Source source : sources.values()) {
-			if (source instanceof Def) {
-				defList.add((Def) source);
-			}
-		}
-		defSources = defList.toArray(new Def[defList.size()]);
-	}
-
-	private void fetchRrdData() throws IOException, RrdException {
-		long tEndFixed = (tEnd == 0) ? Util.getTime() : tEnd;
-		for (int i = 0; i < defSources.length; i++) {
-			if (!defSources[i].isLoaded()) {
-				// not fetched yet
-				Set<String> dsNames = new HashSet<String>();
-				dsNames.add(defSources[i].getDsName());
-				// look for all other datasources with the same path and the same consolidation function
-				for (int j = i + 1; j < defSources.length; j++) {
-					if (defSources[i].isCompatibleWith(defSources[j])) {
-						dsNames.add(defSources[j].getDsName());
-					}
-				}
-				// now we have everything
-				RrdDb rrd = null;
-				try {
-					rrd = getRrd(defSources[i]);
-					lastRrdArchiveUpdateTime = Math.max(lastRrdArchiveUpdateTime, rrd.getLastArchiveUpdateTime());
-					FetchRequest req = rrd.createFetchRequest(defSources[i].getConsolFun(),
-							tStart, tEndFixed, fetchRequestResolution);
-					req.setFilter(dsNames);
-					FetchData data = req.fetchData();
-					defSources[i].setFetchData(data);
-					for (int j = i + 1; j < defSources.length; j++) {
-						if (defSources[i].isCompatibleWith(defSources[j])) {
-							defSources[j].setFetchData(data);
-						}
-					}
-				}
-				finally {
-					if (rrd != null) {
-						releaseRrd(rrd, defSources[i]);
-					}
-				}
-			}
-		}
-	}
-
-	private void fixZeroEndingTimestamp() throws RrdException {
-		if (tEnd == 0) {
-			if (defSources.length == 0) {
-				throw new RrdException("Could not adjust zero ending timestamp, no DEF source provided");
-			}
-			tEnd = defSources[0].getArchiveEndTime();
-			for (int i = 1; i < defSources.length; i++) {
-				tEnd = Math.min(tEnd, defSources[i].getArchiveEndTime());
-			}
-			if (tEnd <= tStart) {
-				throw new RrdException("Could not resolve zero ending timestamp.");
-			}
-		}
-	}
-
-	// Tricky and ugly. Should be redesigned some time in the future
-	private void chooseOptimalStep() {
-		long newStep = Long.MAX_VALUE;
-		for (Def defSource : defSources) {
-			long fetchStep = defSource.getFetchStep(), tryStep = fetchStep;
-			if (step > 0) {
-				tryStep = Math.min(newStep, (((step - 1) / fetchStep) + 1) * fetchStep);
-			}
-			newStep = Math.min(newStep, tryStep);
-		}
-		if (newStep != Long.MAX_VALUE) {
-			// step resolved from a RRD file
-			step = newStep;
-		}
-		else {
-			// choose step based on the number of pixels (useful for plottable datasources)
-			step = Math.max((tEnd - tStart) / pixelCount, 1);
-		}
-	}
-
-	private void createTimestamps() {
-		long t1 = Util.normalize(tStart, step);
-		long t2 = Util.normalize(tEnd, step);
-                if (t2 < tEnd) {
-                    t2 += step;
-                }
-		int count = (int) (((t2 - t1) / step) + 1);
-		timestamps = new long[count];
-		for (int i = 0; i < count; i++) {
-			timestamps[i] = t1;
-			t1 += step;
-		}
-	}
-
-	private void assignTimestampsToSources() {
-		for (Source src : sources.values()) {
-			src.setTimestamps(timestamps);
-		}
-	}
-
-	private void normalizeRrdValues() throws RrdException {
-		Normalizer normalizer = new Normalizer(timestamps);
-		for (Def def : defSources) {
-			long[] rrdTimestamps = def.getRrdTimestamps();
-			double[] rrdValues = def.getRrdValues();
-			double[] values = normalizer.normalize(rrdTimestamps, rrdValues);
-			def.setValues(values);
-		}
-	}
-
-	private void calculateNonRrdSources() throws RrdException {
-		for (Source source : sources.values()) {
-			if (source instanceof SDef) {
-				calculateSDef((SDef) source);
-			}
-			else if (source instanceof CDef) {
-				calculateCDef((CDef) source);
-			}
-			else if (source instanceof PDef) {
-				calculatePDef((PDef) source);
-			}
-			else if (source instanceof PercentileDef) {
-			        calculatePercentileDef((PercentileDef) source);
-			}
-		}
-	}
-
-	private void calculatePDef(PDef pdef) {
-		pdef.calculateValues();
-	}
-
-	private void calculateCDef(CDef cDef) throws RrdException {
-		RpnCalculator calc = new RpnCalculator(cDef.getRpnExpression(), cDef.getName(), this);
-		cDef.setValues(calc.calculateValues());
-	}
-
-	private void calculateSDef(SDef sDef) throws RrdException {
-		String defName = sDef.getDefName();
-		String consolFun = sDef.getConsolFun();
-		Source source = getSource(defName);
-                if (consolFun.equals("MAXIMUM")) { consolFun = "MAX"; }
-                else if (consolFun.equals("MINIMUM")) { consolFun = "MIN"; }
-		double value = source.getAggregates(tStart, tEnd).getAggregate(consolFun);
-		sDef.setValue(value);
-	}
-
-	//Yeah, this is different from the other calculation methods
-	// Frankly, this is how it *should* be done, and the other methods will
-	// be refactored to this design (and the instanceof's removed) at some point
-        private void calculatePercentileDef(PercentileDef def) throws RrdException {
-                def.calculate(tStart, tEnd);
-        }
-
-
-	private RrdDb getRrd(Def def) throws IOException, RrdException {
-		String path = def.getPath(), backend = def.getBackend();
-		if (poolUsed && backend == null) {
-			return RrdDbPool.getInstance().requestRrdDb(path);
-		}
-		else if (backend != null) {
-			return new RrdDb(path, true, RrdBackendFactory.getFactory(backend));
-		}
-		else {
-			return new RrdDb(path, true);
-		}
-	}
-
-	private void releaseRrd(RrdDb rrd, Def def) throws IOException, RrdException {
-		String backend = def.getBackend();
-		if (poolUsed && backend == null) {
-			RrdDbPool.getInstance().release(rrd);
-		}
-		else {
-			rrd.close();
-		}
-	}
-
-	private static String format(String s, int length) {
-		StringBuilder b = new StringBuilder(s);
-		for (int i = 0; i < length - s.length(); i++) {
-			b.append(' ');
-		}
-		return b.toString();
-	}
-
-	/**
-	 * Cute little demo. Uses demo.rrd file previously created by basic JRobin demo.
-	 *
-	 * @param args Not used
-	 * @throws IOException if an I/O error occurs.
-	 * @throws RrdException Thrown if internal jrobin error occurs.
-	 */
-	public static void main(String[] args) throws IOException, RrdException {
-		// time span
-		long t1 = Util.getTimestamp(2003, 4, 1);
-		long t2 = Util.getTimestamp(2003, 5, 1);
-		System.out.println("t1 = " + t1);
-		System.out.println("t2 = " + t2);
-
-		// RRD file to use
-		String rrdPath = Util.getJRobinDemoPath("demo.rrd");
-
-		// constructor
-		DataProcessor dp = new DataProcessor(t1, t2);
-
-		// uncomment and run again
-		//dp.setFetchRequestResolution(86400);
-
-		// uncomment and run again
-		//dp.setStep(86500);
-
-		// datasource definitions
-		dp.addDatasource("X", rrdPath, "sun", "AVERAGE");
-		dp.addDatasource("Y", rrdPath, "shade", "AVERAGE");
-		dp.addDatasource("Z", "X,Y,+,2,/");
-		dp.addDatasource("DERIVE[Z]", "Z,PREV(Z),-,STEP,/");
-		dp.addDatasource("TREND[Z]", "DERIVE[Z],SIGN");
-		dp.addDatasource("AVG[Z]", "Z", "AVERAGE");
-		dp.addDatasource("DELTA", "Z,AVG[Z],-");
-
-		// action
-		long laptime = System.currentTimeMillis();
-		//dp.setStep(86400);
-		dp.processData();
-		System.out.println("Data processed in " + (System.currentTimeMillis() - laptime) + " milliseconds\n---");
-		System.out.println(dp.dump());
-
-		// aggregates
-		System.out.println("\nAggregates for X");
-		Aggregates agg = dp.getAggregates("X");
-		System.out.println(agg.dump());
-		System.out.println("\nAggregates for Y");
-		agg = dp.getAggregates("Y");
-		System.out.println(agg.dump());
-
-		// 95-percentile
-		System.out.println("\n95-percentile for X: " + Util.formatDouble(dp.get95Percentile("X")));
-		System.out.println("95-percentile for Y: " + Util.formatDouble(dp.get95Percentile("Y")));
-
-		// lastArchiveUpdateTime
-		System.out.println("\nLast archive update time was: " + dp.getLastRrdArchiveUpdateTime());
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/Def.java b/apps/jrobin/java/src/org/jrobin/data/Def.java
deleted file mode 100644
index defd84fce05b7a60bbaf42085571604a60371ffc..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/Def.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-import org.jrobin.core.FetchData;
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-
-import java.io.IOException;
-
-class Def extends Source {
-	private String path, dsName, consolFun, backend;
-	private FetchData fetchData;
-
-	Def(String name, FetchData fetchData) {
-		this(name, null, name, null, null);
-		setFetchData(fetchData);
-	}
-
-	Def(String name, String path, String dsName, String consolFunc) {
-		this(name, path, dsName, consolFunc, null);
-	}
-
-	Def(String name, String path, String dsName, String consolFunc, String backend) {
-		super(name);
-		this.path = path;
-		this.dsName = dsName;
-		this.consolFun = consolFunc;
-		this.backend = backend;
-	}
-
-	String getPath() {
-		return path;
-	}
-
-	String getCanonicalPath() throws IOException {
-		return Util.getCanonicalPath(path);
-	}
-
-	String getDsName() {
-		return dsName;
-	}
-
-	String getConsolFun() {
-		return consolFun;
-	}
-
-	String getBackend() {
-		return backend;
-	}
-
-	boolean isCompatibleWith(Def def) throws IOException {
-		return getCanonicalPath().equals(def.getCanonicalPath()) &&
-				getConsolFun().equals(def.consolFun) &&
-				((backend == null && def.backend == null) ||
-						(backend != null && def.backend != null && backend.equals(def.backend)));
-	}
-
-	void setFetchData(FetchData fetchData) {
-		this.fetchData = fetchData;
-	}
-
-	long[] getRrdTimestamps() {
-		return fetchData.getTimestamps();
-	}
-
-	double[] getRrdValues() throws RrdException {
-		return fetchData.getValues(dsName);
-	}
-
-	long getArchiveEndTime() {
-		return fetchData.getArcEndTime();
-	}
-
-	long getFetchStep() {
-		return fetchData.getStep();
-	}
-
-	Aggregates getAggregates(long tStart, long tEnd) throws RrdException {
-		long[] t = getRrdTimestamps();
-		double[] v = getRrdValues();
-		Aggregator agg = new Aggregator(t, v);
-		return agg.getAggregates(tStart, tEnd);
-	}
-
-	double getPercentile(long tStart, long tEnd, double percentile) throws RrdException {
-		long[] t = getRrdTimestamps();
-		double[] v = getRrdValues();
-		Aggregator agg = new Aggregator(t, v);
-		return agg.getPercentile(tStart, tEnd, percentile);
-	}
-
-	boolean isLoaded() {
-		return fetchData != null;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/LinearInterpolator.java b/apps/jrobin/java/src/org/jrobin/data/LinearInterpolator.java
deleted file mode 100644
index b64434517515a01cd2a898a4cb77b806195e3de3..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/LinearInterpolator.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.data;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-
-import java.util.Calendar;
-import java.util.Date;
-
-/**
- * Class used to interpolate datasource values from the collection of (timestamp, values)
- * points. This class is suitable for linear interpolation only.
- * <p>
- * Interpolation algorithm returns different values based on the value passed to
- * {@link #setInterpolationMethod(int) setInterpolationMethod()}. If not set, interpolation
- * method defaults to standard linear interpolation ({@link #INTERPOLATE_LINEAR}).
- * Interpolation method handles NaN datasource
- * values gracefully.
- */
-public class LinearInterpolator extends Plottable {
-	/**
-	 * constant used to specify LEFT interpolation.
-	 * See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
-	 */
-	public static final int INTERPOLATE_LEFT = 0;
-	/**
-	 * constant used to specify RIGHT interpolation.
-	 * See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
-	 */
-	public static final int INTERPOLATE_RIGHT = 1;
-	/**
-	 * constant used to specify LINEAR interpolation (default interpolation method).
-	 * See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
-	 */
-	public static final int INTERPOLATE_LINEAR = 2;
-	/**
-	 * constant used to specify LINEAR REGRESSION as interpolation method.
-	 * See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
-	 */
-	public static final int INTERPOLATE_REGRESSION = 3;
-
-	private int lastIndexUsed = 0;
-	private int interpolationMethod = INTERPOLATE_LINEAR;
-
-	private long[] timestamps;
-	private double[] values;
-
-	// used only if INTERPOLATE_BESTFIT is specified
-	double b0 = Double.NaN, b1 = Double.NaN;
-
-	/**
-	 * Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
-	 *
-	 * @param timestamps timestamps in seconds
-	 * @param values	 corresponding datasource values
-	 * @throws RrdException Thrown if supplied arrays do not contain at least two values, or if
-	 *                      timestamps are not ordered, or array lengths are not equal.
-	 */
-	public LinearInterpolator(long[] timestamps, double[] values) throws RrdException {
-		this.timestamps = timestamps;
-		this.values = values;
-		validate();
-	}
-
-	/**
-	 * Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
-	 *
-	 * @param dates  Array of Date objects
-	 * @param values corresponding datasource values
-	 * @throws RrdException Thrown if supplied arrays do not contain at least two values, or if
-	 *                      timestamps are not ordered, or array lengths are not equal.
-	 */
-	public LinearInterpolator(Date[] dates, double[] values) throws RrdException {
-		this.values = values;
-		timestamps = new long[dates.length];
-		for (int i = 0; i < dates.length; i++) {
-			timestamps[i] = Util.getTimestamp(dates[i]);
-		}
-		validate();
-	}
-
-	/**
-	 * Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
-	 *
-	 * @param dates  array of GregorianCalendar objects
-	 * @param values corresponding datasource values
-	 * @throws RrdException Thrown if supplied arrays do not contain at least two values, or if
-	 *                      timestamps are not ordered, or array lengths are not equal.
-	 */
-	public LinearInterpolator(Calendar[] dates, double[] values) throws RrdException {
-		this.values = values;
-		timestamps = new long[dates.length];
-		for (int i = 0; i < dates.length; i++) {
-			timestamps[i] = Util.getTimestamp(dates[i]);
-		}
-		validate();
-	}
-
-	private void validate() throws RrdException {
-		boolean ok = true;
-		if (timestamps.length != values.length || timestamps.length < 2) {
-			ok = false;
-		}
-		for (int i = 0; i < timestamps.length - 1 && ok; i++) {
-			if (timestamps[i] >= timestamps[i + 1]) {
-				ok = false;
-			}
-		}
-		if (!ok) {
-			throw new RrdException("Invalid plottable data supplied");
-		}
-	}
-
-	/**
-	 * Sets interpolation method to be used. Suppose that we have two timestamp/value pairs:<br>
-	 * <code>(t, 100)</code> and <code>(t + 100, 300)</code>. Here are the results interpolator
-	 * returns for t + 50 seconds, for various <code>interpolationMethods</code>:
-	 * <p>
-	 * <ul>
-	 * <li><code>INTERPOLATE_LEFT:   100</code>
-	 * <li><code>INTERPOLATE_RIGHT:  300</code>
-	 * <li><code>INTERPOLATE_LINEAR: 200</code>
-	 * </ul>
-	 * If not set, interpolation method defaults to <code>INTERPOLATE_LINEAR</code>.
-	 * <p>
-	 * The fourth available interpolation method is INTERPOLATE_REGRESSION. This method uses
-	 * simple linear regression to interpolate supplied data with a simple straight line which does not
-	 * necessarily pass through all data points. The slope of the best-fit line will be chosen so that the
-	 * total square distance of real data points from from the best-fit line is at minimum.
-	 * <p>
-	 * The full explanation of this inteprolation method can be found
-	 * <a href="http://www.tufts.edu/~gdallal/slr.htm">here</a>.
-	 *
-	 * @param interpolationMethod Should be <code>INTERPOLATE_LEFT</code>,
-	 *                            <code>INTERPOLATE_RIGHT</code>, <code>INTERPOLATE_LINEAR</code> or
-	 *                            <code>INTERPOLATE_REGRESSION</code>. Any other value will be interpreted as
-	 *                            INTERPOLATE_LINEAR (default).
-	 */
-	public void setInterpolationMethod(int interpolationMethod) {
-		switch (interpolationMethod) {
-			case INTERPOLATE_REGRESSION:
-				calculateBestFitLine();
-				this.interpolationMethod = interpolationMethod;
-				break;
-			case INTERPOLATE_LEFT:
-			case INTERPOLATE_RIGHT:
-			case INTERPOLATE_LINEAR:
-				this.interpolationMethod = interpolationMethod;
-				break;
-			default:
-				this.interpolationMethod = INTERPOLATE_LINEAR;
-		}
-	}
-
-	private void calculateBestFitLine() {
-		int count = timestamps.length, validCount = 0;
-		double ts = 0.0, vs = 0.0;
-		for (int i = 0; i < count; i++) {
-			if (!Double.isNaN(values[i])) {
-				ts += timestamps[i];
-				vs += values[i];
-				validCount++;
-			}
-		}
-		if (validCount <= 1) {
-			// just one not-NaN point
-			b0 = b1 = Double.NaN;
-			return;
-		}
-		ts /= validCount;
-		vs /= validCount;
-		double s1 = 0, s2 = 0;
-		for (int i = 0; i < count; i++) {
-			if (!Double.isNaN(values[i])) {
-				double dt = timestamps[i] - ts;
-				double dv = values[i] - vs;
-				s1 += dt * dv;
-				s2 += dt * dt;
-			}
-		}
-		b1 = s1 / s2;
-		b0 = vs - b1 * ts;
-	}
-
-	/**
-	 * Method overriden from the base class. This method will be called by the framework. Call
-	 * this method only if you need interpolated values in your code.
-	 *
-	 * @param timestamp timestamp in seconds
-	 * @return inteprolated datasource value
-	 */
-	public double getValue(long timestamp) {
-		if (interpolationMethod == INTERPOLATE_REGRESSION) {
-			return b0 + b1 * timestamp;
-		}
-		int count = timestamps.length;
-		// check if out of range
-		if (timestamp < timestamps[0] || timestamp > timestamps[count - 1]) {
-			return Double.NaN;
-		}
-		// find matching segment
-		int startIndex = lastIndexUsed;
-		if (timestamp < timestamps[lastIndexUsed]) {
-			// backward reading, shift to the first timestamp
-			startIndex = 0;
-		}
-		for (int i = startIndex; i < count; i++) {
-			if (timestamps[i] == timestamp) {
-				return values[i];
-			}
-			if (i < count - 1 && timestamps[i] < timestamp && timestamp < timestamps[i + 1]) {
-				// matching segment found
-				lastIndexUsed = i;
-				switch (interpolationMethod) {
-					case INTERPOLATE_LEFT:
-						return values[i];
-					case INTERPOLATE_RIGHT:
-						return values[i + 1];
-					case INTERPOLATE_LINEAR:
-						double slope = (values[i + 1] - values[i]) /
-								(timestamps[i + 1] - timestamps[i]);
-						return values[i] + slope * (timestamp - timestamps[i]);
-					default:
-						return Double.NaN;
-				}
-			}
-		}
-		// should not be here ever, but let's satisfy the compiler
-		return Double.NaN;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/Normalizer.java b/apps/jrobin/java/src/org/jrobin/data/Normalizer.java
deleted file mode 100644
index 34c177e741e6cb96a3b0aac0b71fd1dafc9069ae..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/Normalizer.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-import org.jrobin.core.Util;
-
-import java.util.Arrays;
-
-class Normalizer {
-	final private long[] timestamps;
-	final int count;
-	final long step;
-
-	Normalizer(long[] timestamps) {
-		this.timestamps = timestamps;
-		this.step = timestamps[1] - timestamps[0];
-		this.count = timestamps.length;
-	}
-
-	double[] normalize(long[] rawTimestamps, double[] rawValues) {
-		int rawCount = rawTimestamps.length;
-		long rawStep = rawTimestamps[1] - rawTimestamps[0];
-		// check if we have a simple match
-		if (rawCount == count && rawStep == step && rawTimestamps[0] == timestamps[0]) {
-			return getCopyOf(rawValues);
-		}
-		// reset all normalized values to NaN
-		double[] values = new double[count];
-		Arrays.fill(values, Double.NaN);
-		for (int rawSeg = 0, seg = 0; rawSeg < rawCount && seg < count; rawSeg++) {
-			double rawValue = rawValues[rawSeg];
-			if (!Double.isNaN(rawValue)) {
-				long rawLeft = rawTimestamps[rawSeg] - rawStep;
-				while (seg < count && rawLeft >= timestamps[seg]) {
-					seg++;
-				}
-				boolean overlap = true;
-				for (int fillSeg = seg; overlap && fillSeg < count; fillSeg++) {
-					long left = timestamps[fillSeg] - step;
-					long t1 = Math.max(rawLeft, left);
-					long t2 = Math.min(rawTimestamps[rawSeg], timestamps[fillSeg]);
-					if (t1 < t2) {
-						values[fillSeg] = Util.sum(values[fillSeg], (t2 - t1) * rawValues[rawSeg]);
-					}
-					else {
-						overlap = false;
-					}
-				}
-			}
-		}
-		for (int seg = 0; seg < count; seg++) {
-			values[seg] /= step;
-		}
-		return values;
-	}
-
-	private static double[] getCopyOf(double[] rawValues) {
-		int n = rawValues.length;
-		double[] values = new double[n];
-		System.arraycopy(rawValues, 0, values, 0, n);
-		return values;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/PDef.java b/apps/jrobin/java/src/org/jrobin/data/PDef.java
deleted file mode 100644
index b6bb1f315cced45760058efcf34dfac6a6856777..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/PDef.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-class PDef extends Source {
-	private final Plottable plottable;
-
-	PDef(String name, Plottable plottable) {
-		super(name);
-		this.plottable = plottable;
-	}
-
-	void calculateValues() {
-		long[] times = getTimestamps();
-		double[] vals = new double[times.length];
-		for (int i = 0; i < times.length; i++) {
-			vals[i] = plottable.getValue(times[i]);
-		}
-		setValues(vals);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/PercentileDef.java b/apps/jrobin/java/src/org/jrobin/data/PercentileDef.java
deleted file mode 100644
index 853f4d9501ba1b73df59b85b14c15bafbc155ada..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/PercentileDef.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2011 Craig Miskell
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.data;
-
-import org.jrobin.core.RrdException;
-
-public class PercentileDef extends Source {
-
-    private Source m_source;
-
-    private double m_value;
-
-    private double m_percentile;
-
-    @SuppressWarnings("unused")
-	private boolean m_ignorenan;
-
-    PercentileDef(String name, Source source, double percentile) {
-        this(name, source, percentile, false);
-    }
-
-    PercentileDef(String name, Source source, double percentile, boolean ignorenan) {
-        super(name);
-
-        m_percentile = percentile;
-        m_ignorenan = ignorenan;
-        m_source = source;
-
-        //The best we can do at this point; until this object has it's value realized over a
-        // particular time period (with calculate()), there's not much else to do
-        this.setValue(Double.NaN);
-    }
-
-    /**
-     * Realize the calculation of this definition, over the given time period
-     *
-     * @param tStart the time period start
-     * @param tEnd the time period end
-     * @throws RrdException Thrown if we cannot get a percentile value for the time period.
-     */
-    public void calculate(long tStart, long tEnd) throws RrdException {
-        if(m_source != null) {
-            this.setValue(m_source.getPercentile(tStart, tEnd, m_percentile));
-        }
-    }
-
-    /**
-     * Takes the given value and puts it in each position in the 'values' array.
-     * @param value
-     */
-    private void setValue(double value) {
-        this.m_value = value;
-        long[] times = getTimestamps();
-        if( times != null ) {
-            int count = times.length;
-            double[] values = new double[count];
-            for (int i = 0; i < count; i++) {
-                    values[i] = m_value;
-            }
-            setValues(values);
-        }
-    }
-
-    @Override
-    void setTimestamps(long[] timestamps) {
-        super.setTimestamps(timestamps);
-        //And now also call setValue with the current value, to sort out "values"
-        setValue(m_value);
-    }
-
-    /**
-     * Same as SDef; the aggregates of a static value are all just the
-     * same static value.
-     *
-     * Assumes this def has been realized by calling calculate(), otherwise
-     * the aggregated values will be NaN
-     */
-    @Override
-    Aggregates getAggregates(long tStart, long tEnd) throws RrdException {
-        Aggregates agg = new Aggregates();
-        agg.first = agg.last = agg.min = agg.max = agg.average = m_value;
-        agg.total = m_value * (tEnd - tStart);
-        return agg;
-    }
-
-    /**
-     * Returns just the calculated percentile; the "Xth" percentile of a static value is
-     * the static value itself.
-     *
-     * Assumes this def has been realized by calling calculate(), otherwise
-     * the aggregated values will be NaN
-     */
-    @Override
-    double getPercentile(long tStart, long tEnd, double percentile)
-            throws RrdException {
-        return m_value;
-    }
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/Plottable.java b/apps/jrobin/java/src/org/jrobin/data/Plottable.java
deleted file mode 100644
index b8c879608eb559865055ea7075ffc49462bdd858..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/Plottable.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.data;
-
-/**
- * <p>Interface to be used for custom datasources.
- * If you wish to use a custom datasource in a graph, you should create a class implementing this interface
- * that represents that datasource, and then pass this class on to the RrdGraphDef.</p>
- */
-public abstract class Plottable {
-	/**
-	 * Retrieves datapoint value based on a given timestamp.
-	 * Use this method if you only have one series of data in this class.
-	 *
-	 * @param timestamp Timestamp in seconds for the datapoint.
-	 * @return Double value of the datapoint.
-	 */
-	public double getValue(long timestamp) {
-		return Double.NaN;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/RpnCalculator.java b/apps/jrobin/java/src/org/jrobin/data/RpnCalculator.java
deleted file mode 100644
index 6e0dfbb93cd695bd99e16cf73cfc2604e45e5bf0..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/RpnCalculator.java
+++ /dev/null
@@ -1,832 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-import java.util.Arrays;
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-
-import java.util.Calendar;
-import java.util.StringTokenizer;
-import java.util.TimeZone;
-
-class RpnCalculator {
-	private static final byte TKN_VAR = 0;
-	private static final byte TKN_NUM = 1;
-	private static final byte TKN_PLUS = 2;
-	private static final byte TKN_MINUS = 3;
-	private static final byte TKN_MULT = 4;
-	private static final byte TKN_DIV = 5;
-	private static final byte TKN_MOD = 6;
-	private static final byte TKN_SIN = 7;
-	private static final byte TKN_COS = 8;
-	private static final byte TKN_LOG = 9;
-	private static final byte TKN_EXP = 10;
-	private static final byte TKN_FLOOR = 11;
-	private static final byte TKN_CEIL = 12;
-	private static final byte TKN_ROUND = 13;
-	private static final byte TKN_POW = 14;
-	private static final byte TKN_ABS = 15;
-	private static final byte TKN_SQRT = 16;
-	private static final byte TKN_RANDOM = 17;
-	private static final byte TKN_LT = 18;
-	private static final byte TKN_LE = 19;
-	private static final byte TKN_GT = 20;
-	private static final byte TKN_GE = 21;
-	private static final byte TKN_EQ = 22;
-	private static final byte TKN_IF = 23;
-	private static final byte TKN_MIN = 24;
-	private static final byte TKN_MAX = 25;
-	private static final byte TKN_LIMIT = 26;
-	private static final byte TKN_DUP = 27;
-	private static final byte TKN_EXC = 28;
-	private static final byte TKN_POP = 29;
-	private static final byte TKN_UN = 30;
-	private static final byte TKN_UNKN = 31;
-	private static final byte TKN_NOW = 32;
-	private static final byte TKN_TIME = 33;
-	private static final byte TKN_PI = 34;
-	private static final byte TKN_E = 35;
-	private static final byte TKN_AND = 36;
-	private static final byte TKN_OR = 37;
-	private static final byte TKN_XOR = 38;
-	private static final byte TKN_PREV = 39;
-	private static final byte TKN_INF = 40;
-	private static final byte TKN_NEGINF = 41;
-	private static final byte TKN_STEP = 42;
-	private static final byte TKN_YEAR = 43;
-	private static final byte TKN_MONTH = 44;
-	private static final byte TKN_DATE = 45;
-	private static final byte TKN_HOUR = 46;
-	private static final byte TKN_MINUTE = 47;
-	private static final byte TKN_SECOND = 48;
-	private static final byte TKN_WEEK = 49;
-	private static final byte TKN_SIGN = 50;
-	private static final byte TKN_RND = 51;
-        private static final byte TKN_ADDNAN = 52;
-        private static final byte TKN_NE = 53;
-        private static final byte TKN_ISINF = 54;
-        private static final byte TKN_ATAN = 55;
-        private static final byte TKN_ATAN2 = 56;
-        private static final byte TKN_DEG2RAD = 57;
-        private static final byte TKN_RAD2DEG = 58;
-        private static final byte TKN_COUNT = 59;
-        private static final byte TKN_SORT = 60;
-        private static final byte TKN_REV = 61;
-        private static final byte TKN_AVG = 62;
-        private static final byte TKN_LTIME = 63;
-        private static final byte TKN_TREND = 64;
-        private static final byte TKN_TRENDNAN = 65;
-        private static final byte TKN_PREDICT = 66;
-        private static final byte TKN_PREDICTSIGMA = 67;
-
-	private String rpnExpression;
-	private String sourceName;
-	private DataProcessor dataProcessor;
-
-	private Token[] tokens;
-	private RpnStack stack = new RpnStack();
-	private double[] calculatedValues;
-	private long[] timestamps;
-	private double timeStep;
-
-	RpnCalculator(String rpnExpression, String sourceName, DataProcessor dataProcessor) throws RrdException {
-		this.rpnExpression = rpnExpression;
-		this.sourceName = sourceName;
-		this.dataProcessor = dataProcessor;
-		this.timestamps = dataProcessor.getTimestamps();
-		this.timeStep = this.timestamps[1] - this.timestamps[0];
-		this.calculatedValues = new double[this.timestamps.length];
-		StringTokenizer st = new StringTokenizer(rpnExpression, ", ");
-		tokens = new Token[st.countTokens()];
-		for (int i = 0; st.hasMoreTokens(); i++) {
-			tokens[i] = createToken(st.nextToken());
-		}
-	}
-
-	private Token createToken(String parsedText) throws RrdException {
-		Token token = new Token();
-		if (Util.isDouble(parsedText)) {
-			token.id = TKN_NUM;
-			token.number = Util.parseDouble(parsedText);
-		}
-		else if (parsedText.equals("+")) {
-			token.id = TKN_PLUS;
-		}
-		else if (parsedText.equals("-")) {
-			token.id = TKN_MINUS;
-		}
-		else if (parsedText.equals("*")) {
-			token.id = TKN_MULT;
-		}
-		else if (parsedText.equals("/")) {
-			token.id = TKN_DIV;
-		}
-		else if (parsedText.equals("%")) {
-			token.id = TKN_MOD;
-		}
-		else if (parsedText.equals("SIN")) {
-			token.id = TKN_SIN;
-		}
-		else if (parsedText.equals("COS")) {
-			token.id = TKN_COS;
-		}
-		else if (parsedText.equals("LOG")) {
-			token.id = TKN_LOG;
-		}
-		else if (parsedText.equals("EXP")) {
-			token.id = TKN_EXP;
-		}
-		else if (parsedText.equals("FLOOR")) {
-			token.id = TKN_FLOOR;
-		}
-		else if (parsedText.equals("CEIL")) {
-			token.id = TKN_CEIL;
-		}
-		else if (parsedText.equals("ROUND")) {
-			token.id = TKN_ROUND;
-		}
-		else if (parsedText.equals("POW")) {
-			token.id = TKN_POW;
-		}
-		else if (parsedText.equals("ABS")) {
-			token.id = TKN_ABS;
-		}
-		else if (parsedText.equals("SQRT")) {
-			token.id = TKN_SQRT;
-		}
-		else if (parsedText.equals("RANDOM")) {
-			token.id = TKN_RANDOM;
-		}
-		else if (parsedText.equals("LT")) {
-			token.id = TKN_LT;
-		}
-		else if (parsedText.equals("LE")) {
-			token.id = TKN_LE;
-		}
-		else if (parsedText.equals("GT")) {
-			token.id = TKN_GT;
-		}
-		else if (parsedText.equals("GE")) {
-			token.id = TKN_GE;
-		}
-		else if (parsedText.equals("EQ")) {
-			token.id = TKN_EQ;
-		}
-		else if (parsedText.equals("IF")) {
-			token.id = TKN_IF;
-		}
-		else if (parsedText.equals("MIN")) {
-			token.id = TKN_MIN;
-		}
-		else if (parsedText.equals("MAX")) {
-			token.id = TKN_MAX;
-		}
-		else if (parsedText.equals("LIMIT")) {
-			token.id = TKN_LIMIT;
-		}
-		else if (parsedText.equals("DUP")) {
-			token.id = TKN_DUP;
-		}
-		else if (parsedText.equals("EXC")) {
-			token.id = TKN_EXC;
-		}
-		else if (parsedText.equals("POP")) {
-			token.id = TKN_POP;
-		}
-		else if (parsedText.equals("UN")) {
-			token.id = TKN_UN;
-		}
-		else if (parsedText.equals("UNKN")) {
-			token.id = TKN_UNKN;
-		}
-		else if (parsedText.equals("NOW")) {
-			token.id = TKN_NOW;
-		}
-		else if (parsedText.equals("TIME")) {
-			token.id = TKN_TIME;
-		}
-		else if (parsedText.equals("LTIME")) {
-			token.id = TKN_LTIME;
-		}
-		else if (parsedText.equals("PI")) {
-			token.id = TKN_PI;
-		}
-		else if (parsedText.equals("E")) {
-			token.id = TKN_E;
-		}
-		else if (parsedText.equals("AND")) {
-			token.id = TKN_AND;
-		}
-		else if (parsedText.equals("OR")) {
-			token.id = TKN_OR;
-		}
-		else if (parsedText.equals("XOR")) {
-			token.id = TKN_XOR;
-		}
-		else if (parsedText.equals("PREV")) {
-			token.id = TKN_PREV;
-			token.variable = sourceName;
-			token.values = calculatedValues;
-		}
-		else if (parsedText.startsWith("PREV(") && parsedText.endsWith(")")) {
-			token.id = TKN_PREV;
-			token.variable = parsedText.substring(5, parsedText.length() - 1);
-			token.values = dataProcessor.getValues(token.variable);
-		}
-		else if (parsedText.equals("INF")) {
-			token.id = TKN_INF;
-		}
-		else if (parsedText.equals("NEGINF")) {
-			token.id = TKN_NEGINF;
-		}
-		else if (parsedText.equals("STEP")) {
-			token.id = TKN_STEP;
-		}
-		else if (parsedText.equals("YEAR")) {
-			token.id = TKN_YEAR;
-		}
-		else if (parsedText.equals("MONTH")) {
-			token.id = TKN_MONTH;
-		}
-		else if (parsedText.equals("DATE")) {
-			token.id = TKN_DATE;
-		}
-		else if (parsedText.equals("HOUR")) {
-			token.id = TKN_HOUR;
-		}
-		else if (parsedText.equals("MINUTE")) {
-			token.id = TKN_MINUTE;
-		}
-		else if (parsedText.equals("SECOND")) {
-			token.id = TKN_SECOND;
-		}
-		else if (parsedText.equals("WEEK")) {
-			token.id = TKN_WEEK;
-		}
-		else if (parsedText.equals("SIGN")) {
-			token.id = TKN_SIGN;
-		}
-		else if (parsedText.equals("RND")) {
-			token.id = TKN_RND;
-		}
-		else if (parsedText.equals("ADDNAN")) {
-			token.id = TKN_ADDNAN;
-		}
-		else if (parsedText.equals("NE")) {
-			token.id = TKN_NE;
-		}
-		else if (parsedText.equals("ISINF")) {
-			token.id = TKN_ISINF;
-		}
-		else if (parsedText.equals("ATAN")) {
-			token.id = TKN_ATAN;
-		}
-		else if (parsedText.equals("ATAN2")) {
-			token.id = TKN_ATAN2;
-		}
-		else if (parsedText.equals("DEG2RAD")) {
-			token.id = TKN_DEG2RAD;
-		}
-		else if (parsedText.equals("RAD2DEG")) {
-			token.id = TKN_RAD2DEG;
-		}
-		else if (parsedText.equals("COUNT")) {
-			token.id = TKN_COUNT;
-		}
-		else if (parsedText.equals("SORT")) {
-			token.id = TKN_SORT;
-		}
-		else if (parsedText.equals("REV")) {
-			token.id = TKN_REV;
-		}
-		else if (parsedText.equals("AVG")) {
-			token.id = TKN_AVG;
-		}
-		else if (parsedText.equals("TREND")) {
-			token.id = TKN_TREND;
-		}
-		else if (parsedText.equals("TRENDNAN")) {
-			token.id = TKN_TRENDNAN;
-		}
-		else if (parsedText.equals("PREDICT")) {
-			token.id = TKN_PREDICT;
-		}
-		else if (parsedText.equals("PREDICTSIGMA")) {
-			token.id = TKN_PREDICTSIGMA;
-		}
-		else {
-			token.id = TKN_VAR;
-			token.variable = parsedText;
-			token.values = dataProcessor.getValues(token.variable);
-		}
-		return token;
-	}
-
-	double[] calculateValues() throws RrdException {
-                TimeZone tz = TimeZone.getDefault();
-		for (int slot = 0; slot < timestamps.length; slot++) {
-			resetStack();
-                        int token_rpi = -1;
-                        for (int rpi = 0; rpi < tokens.length; rpi++) {
-                                Token token = tokens[rpi];
-				double x1, x2, x3;
-				switch (token.id) {
-					case TKN_NUM:
-						push(token.number);
-						break;
-					case TKN_VAR:
-						push(token.values[slot]);
-                                                token_rpi = rpi;
-						break;
-					case TKN_COUNT:
-						push(slot+1);
-						break;
-					case TKN_PLUS:
-						push(pop() + pop());
-						break;
-					case TKN_MINUS:
-						x2 = pop();
-						x1 = pop();
-						push(x1 - x2);
-						break;
-					case TKN_MULT:
-						push(pop() * pop());
-						break;
-					case TKN_DIV:
-						x2 = pop();
-						x1 = pop();
-						push(x1 / x2);
-						break;
-					case TKN_MOD:
-						x2 = pop();
-						x1 = pop();
-						push(x1 % x2);
-						break;
-					case TKN_SIN:
-						push(Math.sin(pop()));
-						break;
-					case TKN_COS:
-						push(Math.cos(pop()));
-						break;
-					case TKN_ATAN:
-						push(Math.atan(pop()));
-						break;
-					case TKN_ATAN2:
-						x2 = pop();
-						x1 = pop();
-						push(Math.atan2(x1, x2));
-						break;
-					case TKN_LOG:
-						push(Math.log(pop()));
-						break;
-					case TKN_EXP:
-						push(Math.exp(pop()));
-						break;
-					case TKN_FLOOR:
-						push(Math.floor(pop()));
-						break;
-					case TKN_CEIL:
-						push(Math.ceil(pop()));
-						break;
-					case TKN_ROUND:
-						push(Math.round(pop()));
-						break;
-					case TKN_POW:
-						x2 = pop();
-						x1 = pop();
-						push(Math.pow(x1, x2));
-						break;
-					case TKN_ABS:
-						push(Math.abs(pop()));
-						break;
-					case TKN_SQRT:
-						push(Math.sqrt(pop()));
-						break;
-					case TKN_RANDOM:
-						push(Math.random());
-						break;
-					case TKN_LT:
-						x2 = pop();
-						x1 = pop();
-						push(x1 < x2 ? 1 : 0);
-						break;
-					case TKN_LE:
-						x2 = pop();
-						x1 = pop();
-						push(x1 <= x2 ? 1 : 0);
-						break;
-					case TKN_GT:
-						x2 = pop();
-						x1 = pop();
-						push(x1 > x2 ? 1 : 0);
-						break;
-					case TKN_GE:
-						x2 = pop();
-						x1 = pop();
-						push(x1 >= x2 ? 1 : 0);
-						break;
-					case TKN_EQ:
-						x2 = pop();
-						x1 = pop();
-						push(x1 == x2 ? 1 : 0);
-						break;
-					case TKN_NE:
-						x2 = pop();
-						x1 = pop();
-						push(x1 != x2 ? 1 : 0);
-						break;
-					case TKN_IF:
-						x3 = pop();
-						x2 = pop();
-						x1 = pop();
-						push(x1 != 0 ? x2 : x3);
-						break;
-					case TKN_MIN:
-						push(Math.min(pop(), pop()));
-						break;
-					case TKN_MAX:
-						push(Math.max(pop(), pop()));
-						break;
-					case TKN_LIMIT:
-						x3 = pop();
-						x2 = pop();
-						x1 = pop();
-						push(x1 < x2 || x1 > x3 ? Double.NaN : x1);
-						break;
-					case TKN_DUP:
-						push(peek());
-						break;
-					case TKN_EXC:
-						x2 = pop();
-						x1 = pop();
-						push(x2);
-						push(x1);
-						break;
-					case TKN_POP:
-						pop();
-						break;
-					case TKN_UN:
-						push(Double.isNaN(pop()) ? 1 : 0);
-						break;
-					case TKN_ISINF:
-						push(Double.isInfinite(pop()) ? 1 : 0);
-						break;
-					case TKN_UNKN:
-						push(Double.NaN);
-						break;
-					case TKN_NOW:
-						push(Util.getTime());
-						break;
-					case TKN_TIME:
-						push(timestamps[slot]);
-						break;
-					case TKN_LTIME:
-						push(timestamps[slot] + (tz.getOffset(timestamps[slot]) / 1000L));
-						break;
-					case TKN_PI:
-						push(Math.PI);
-						break;
-					case TKN_E:
-						push(Math.E);
-						break;
-					case TKN_AND:
-						x2 = pop();
-						x1 = pop();
-						push((x1 != 0 && x2 != 0) ? 1 : 0);
-						break;
-					case TKN_OR:
-						x2 = pop();
-						x1 = pop();
-						push((x1 != 0 || x2 != 0) ? 1 : 0);
-						break;
-					case TKN_XOR:
-						x2 = pop();
-						x1 = pop();
-						push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0)) ? 1 : 0);
-						break;
-					case TKN_PREV:
-						push((slot == 0) ? Double.NaN : token.values[slot - 1]);
-						break;
-					case TKN_INF:
-						push(Double.POSITIVE_INFINITY);
-						break;
-					case TKN_NEGINF:
-						push(Double.NEGATIVE_INFINITY);
-						break;
-					case TKN_STEP:
-						push(timeStep);
-						break;
-					case TKN_YEAR:
-						push(getCalendarField(pop(), Calendar.YEAR));
-						break;
-					case TKN_MONTH:
-						push(getCalendarField(pop(), Calendar.MONTH));
-						break;
-					case TKN_DATE:
-						push(getCalendarField(pop(), Calendar.DAY_OF_MONTH));
-						break;
-					case TKN_HOUR:
-						push(getCalendarField(pop(), Calendar.HOUR_OF_DAY));
-						break;
-					case TKN_MINUTE:
-						push(getCalendarField(pop(), Calendar.MINUTE));
-						break;
-					case TKN_SECOND:
-						push(getCalendarField(pop(), Calendar.SECOND));
-						break;
-					case TKN_WEEK:
-						push(getCalendarField(pop(), Calendar.WEEK_OF_YEAR));
-						break;
-					case TKN_SIGN:
-						x1 = pop();
-						push(Double.isNaN(x1) ? Double.NaN : x1 > 0 ? +1 : x1 < 0 ? -1 : 0);
-						break;
-					case TKN_RND:
-						push(Math.floor(pop() * Math.random()));
-						break;
-					case TKN_ADDNAN:
-						x2 = pop();
-						x1 = pop();
-                                                if (Double.isNaN(x1)) {
-                                                    push(x2);
-                                                } else if (Double.isNaN(x2)) {
-                                                    push(x1);
-                                                } else {
-                                                    push(x1+x2);
-                                                }
-						break;
-					case TKN_DEG2RAD:
-						push(Math.toRadians(pop()));
-						break;
-					case TKN_RAD2DEG:
-						push(Math.toDegrees(pop()));
-						break;
-					case TKN_SORT:
-                                        {
-                                                int n = (int) pop();
-                                                double[] array = new double[n];
-                                                for(int i = 0; i < n; i++) {
-                                                    array[i] = pop();
-                                                }
-                                                Arrays.sort(array);
-                                                for (int i = 0; i < n; i++) {
-                                                    push(array[i]);
-                                                }
-                                        }
-						break;
-					case TKN_REV:
-                                        {
-                                                int n = (int) pop();
-                                                double[] array = new double[n];
-                                                for(int i = 0; i < n; i++) {
-                                                    array[i] = pop();
-                                                }
-                                                for (int i = 0; i < n; i++) {
-                                                    push(array[i]);
-                                                }
-                                        }
-						break;
-					case TKN_AVG:
-                                        {
-                                                int count = 0;
-                                                int n = (int) pop();
-                                                double sum = 0.0;
-                                                while (n > 0) {
-                                                    x1 = pop();
-                                                    n--;
-
-                                                    if (Double.isNaN(x1)) {
-                                                        continue;
-                                                    }
-
-                                                    sum += x1;
-                                                    count++;
-                                                }
-                                                if (count > 0) {
-                                                    push(sum / count);
-                                                } else {
-                                                    push(Double.NaN);
-                                                }
-                                        }
-						break;
-                                        case TKN_TREND:
-                                        case TKN_TRENDNAN:
-                                        {
-                                                int dur = (int) pop();
-                                                pop();
-                                                /*
-                                                 * OK, so to match the output from rrdtool, we have to go *forward* 2 timeperiods.
-                                                 * So at t[59] we use the average of t[1]..t[61]
-                                                 *
-                                                 */
-
-                                                if ((slot+1) < Math.ceil(dur / timeStep)) {
-                                                    push(Double.NaN);
-                                                } else {
-                                                    double[] vals = dataProcessor.getValues(tokens[token_rpi].variable);
-                                                    boolean ignorenan = token.id == TKN_TRENDNAN;
-                                                    double accum = 0.0;
-                                                    int count = 0;
-
-                                                    int start = (int) (Math.ceil(dur / timeStep));
-                                                    int row = 2;
-                                                    while ((slot + row) > vals.length) {
-                                                        row --;
-                                                    }
-
-                                                    for(; start > 0; start--) {
-                                                        double val = vals[slot + row - start];
-                                                        if (ignorenan || !Double.isNaN(val)) {
-                                                            accum = Util.sum(accum, val);
-                                                            ++count;
-                                                        }
-                                                    }
-                                                    //System.err.printf("t[%d]: %1.10e / %d\n", slot, (count == 0) ? Double.NaN : (accum / count), count);
-                                                    push((count == 0) ? Double.NaN : (accum / count));
-                                                }
-                                        }
-                                                break;
-                                        case TKN_PREDICT:
-                                        case TKN_PREDICTSIGMA:
-                                        {
-                                            pop(); // Clear the value of our variable
-
-                                            /* the local averaging window (similar to trend, but better here, as we get better statistics thru numbers)*/
-                                            int locstepsize = (int) pop();
-                                            /* the number of shifts and range-checking*/
-                                            int num_shifts = (int) pop();
-                                            double[] multipliers;
-
-                                            // handle negative shifts special
-                                            if (num_shifts < 0) {
-                                                multipliers = new double[1];
-                                                multipliers[0] = pop();
-                                            } else {
-                                                multipliers = new double[num_shifts];
-                                                for(int i = 0; i < num_shifts; i++) {
-                                                    multipliers[i] = pop();
-                                                }
-                                            }
-
-                                            /* the real calculation */
-                                            double val = Double.NaN;
-
-                                            /* the info on the datasource */
-                                            double[] vals = dataProcessor.getValues(tokens[rpi-1].variable);
-
-                                            int locstep = (int) Math.ceil((float) locstepsize / (float) timeStep);
-
-                                            /* the sums */
-                                            double sum = 0;
-                                            double sum2 = 0;
-                                            int count = 0;
-
-                                            /* now loop for each position */
-                                            int doshifts = Math.abs(num_shifts);
-                                            for (int loop = 0; loop < doshifts; loop++) {
-                                                /* calculate shift step */
-                                                int shiftstep = 1;
-                                                if (num_shifts < 0) {
-                                                    shiftstep = loop * (int) multipliers[0];
-                                                } else {
-                                                    shiftstep = (int) multipliers[loop];
-                                                }
-                                                if (shiftstep < 0) {
-                                                    throw new RrdException("negative shift step not allowed: " + shiftstep);
-                                                }
-                                                shiftstep = (int) Math.ceil((float) shiftstep / (float) timeStep);
-                                                /* loop all local shifts */
-                                                for (int i = 0; i <= locstep; i++) {
-
-                                                    int offset = shiftstep + i;
-                                                    if ((offset >= 0) && (offset < slot)) {
-                                                        /* get the value */
-                                                        val = vals[slot - offset];
-
-                                                        /* and handle the non NAN case only*/
-                                                        if (!Double.isNaN(val)) {
-                                                            sum = Util.sum(sum, val);
-                                                            sum2 = Util.sum(sum2, val * val);
-                                                            count++;
-                                                        }
-                                                    }
-                                                }
-                                            }
-                                            /* do the final calculations */
-                                            val = Double.NaN;
-                                            if (token.id == TKN_PREDICT) {  /* the average */
-                                                if (count > 0) {
-                                                    val = sum / (double) count;
-                                                }
-                                            } else {
-                                                if (count > 1) { /* the sigma case */
-                                                    val = count * sum2 - sum * sum;
-                                                    if (val < 0) {
-                                                        val = Double.NaN;
-                                                    } else {
-                                                        val = Math.sqrt(val / ((float) count * ((float) count - 1.0)));
-                                                    }
-                                                }
-                                            }
-                                            push(val);
-                                        }
-                                                break;
-					default:
-						throw new RrdException("Unexpected RPN token encountered, token.id=" + token.id);
-				}
-			}
-			calculatedValues[slot] = pop();
-			// check if stack is empty only on the first try
-			if (slot == 0 && !isStackEmpty()) {
-				throw new RrdException("Stack not empty at the end of calculation. " +
-						"Probably bad RPN expression [" + rpnExpression + "]");
-			}
-		}
-		return calculatedValues;
-	}
-
-	private double getCalendarField(double timestamp, int field) {
-		Calendar calendar = Util.getCalendar((long) timestamp);
-		return calendar.get(field);
-	}
-
-	private void push(double x) throws RrdException {
-		stack.push(x);
-	}
-
-	private double pop() throws RrdException {
-		return stack.pop();
-	}
-
-	private double peek() throws RrdException {
-		return stack.peek();
-	}
-
-	private void resetStack() {
-		stack.reset();
-	}
-
-	private boolean isStackEmpty() {
-		return stack.isEmpty();
-	}
-
-	private static final class RpnStack {
-		private static final int MAX_STACK_SIZE = 1000;
-		private double[] stack = new double[MAX_STACK_SIZE];
-		private int pos = 0;
-
-		void push(final double x) throws RrdException {
-			if (pos >= MAX_STACK_SIZE) {
-				throw new RrdException("PUSH failed, RPN stack full [" + MAX_STACK_SIZE + "]");
-			}
-			stack[pos++] = x;
-		}
-
-		double pop() throws RrdException {
-			if (pos <= 0) {
-				throw new RrdException("POP failed, RPN stack is empty ");
-			}
-			return stack[--pos];
-		}
-
-		double peek() throws RrdException {
-			if (pos <= 0) {
-				throw new RrdException("PEEK failed, RPN stack is empty ");
-			}
-			return stack[pos - 1];
-		}
-
-		void reset() {
-			pos = 0;
-		}
-
-		boolean isEmpty() {
-			return pos <= 0;
-		}
-	}
-
-	private static final class Token {
-		byte id = -1;
-		double number = Double.NaN;
-		String variable = null;
-		double[] values = null;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/SDef.java b/apps/jrobin/java/src/org/jrobin/data/SDef.java
deleted file mode 100644
index 7d92be14333e3a242bc3af6f7eaf32d3d5d257cd..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/SDef.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-import org.jrobin.core.RrdException;
-
-class SDef extends Source {
-	private String defName;
-	private String consolFun;
-	private double value;
-
-	SDef(String name, String defName, String consolFun) {
-		super(name);
-		this.defName = defName;
-		this.consolFun = consolFun;
-	}
-
-	String getDefName() {
-		return defName;
-	}
-
-	String getConsolFun() {
-		return consolFun;
-	}
-
-	void setValue(double value) {
-		this.value = value;
-		int count = getTimestamps().length;
-		double[] values = new double[count];
-		for (int i = 0; i < count; i++) {
-			values[i] = value;
-		}
-		setValues(values);
-	}
-
-	Aggregates getAggregates(long tStart, long tEnd) throws RrdException {
-		Aggregates agg = new Aggregates();
-		agg.first = agg.last = agg.min = agg.max = agg.average = value;
-		agg.total = value * (tEnd - tStart);
-		return agg;
-	}
-
-	double getPercentile(long tStart, long tEnd, double percentile) throws RrdException {
-		return value;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/Source.java b/apps/jrobin/java/src/org/jrobin/data/Source.java
deleted file mode 100644
index 302b70670ebc62304e9d262cd674fcebc88e4b29..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/Source.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.data;
-
-import org.jrobin.core.ConsolFuns;
-import org.jrobin.core.RrdException;
-
-abstract class Source implements ConsolFuns {
-	final private String name;
-	protected double[] values;
-	protected long[] timestamps;
-
-	Source(String name) {
-		this.name = name;
-	}
-
-	String getName() {
-		return name;
-	}
-
-	void setValues(double[] values) {
-		this.values = values;
-	}
-
-	void setTimestamps(long[] timestamps) {
-		this.timestamps = timestamps;
-	}
-
-	double[] getValues() {
-		return values;
-	}
-
-	long[] getTimestamps() {
-		return timestamps;
-	}
-
-	Aggregates getAggregates(long tStart, long tEnd) throws RrdException {
-		Aggregator agg = new Aggregator(timestamps, values);
-		return agg.getAggregates(tStart, tEnd);
-	}
-
-	double getPercentile(long tStart, long tEnd, double percentile) throws RrdException {
-		Aggregator agg = new Aggregator(timestamps, values);
-		return agg.getPercentile(tStart, tEnd, percentile, false);
-	}
-
-        double getPercentile(long tStart, long tEnd, double percentile, boolean includenan) throws RrdException {
-		Aggregator agg = new Aggregator(timestamps, values);
-		return agg.getPercentile(tStart, tEnd, percentile, includenan);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/data/package.html b/apps/jrobin/java/src/org/jrobin/data/package.html
deleted file mode 100644
index 809736855d30efdd12b701fbc551cc5cc0537a08..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/data/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-  <body>
-  JRobin data management.
-  </body>
-</html>
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/graph/Area.java b/apps/jrobin/java/src/org/jrobin/graph/Area.java
deleted file mode 100644
index 73845e3a0b83e2a18249ec03a6ae26b85f8dd123..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/Area.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-
-class Area extends SourcedPlotElement {
-	Area(String srcName, Paint color) {
-		super(srcName, color);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/CDef.java b/apps/jrobin/java/src/org/jrobin/graph/CDef.java
deleted file mode 100644
index 39bb6148194201cfab65f3cffb082c16575e0818..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/CDef.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.data.DataProcessor;
-
-class CDef extends Source {
-	private final String rpnExpression;
-
-	CDef(String name, String rpnExpression) {
-		super(name);
-		this.rpnExpression = rpnExpression;
-	}
-
-	void requestData(DataProcessor dproc) {
-		dproc.addDatasource(name, rpnExpression);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/CommentText.java b/apps/jrobin/java/src/org/jrobin/graph/CommentText.java
deleted file mode 100644
index 5031b88d9474b1b18264c663c355dda19ec7b386..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/CommentText.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.data.DataProcessor;
-
-class CommentText implements RrdGraphConstants {
-    private final String text; // original text
-
-    String resolvedText; // resolved text
-    String marker; // end-of-text marker
-    boolean enabled; // hrule and vrule comments can be disabled at runtime
-    int x, y; // coordinates, evaluated later
-
-    CommentText(String text) {
-        this.text = text;
-    }
-
-    void resolveText(DataProcessor dproc, ValueScaler valueScaler) throws RrdException {
-        resolvedText = text;
-        marker = "";
-        if (resolvedText != null) {
-            for (String mark : MARKERS) {
-                if (resolvedText.endsWith(mark)) {
-                    marker = mark;
-                    resolvedText = resolvedText.substring(0, resolvedText.length() - marker.length());
-                    trimIfGlue();
-                    break;
-                }
-            }
-        }
-        enabled = resolvedText != null;
-    }
-
-    void trimIfGlue() {
-        if (marker.equals(GLUE_MARKER)) {
-            resolvedText = resolvedText.replaceFirst("\\s+$", "");
-        }
-    }
-
-    boolean isPrint() {
-        return false;
-    }
-
-    boolean isValidGraphElement() {
-        return !isPrint() && enabled;
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/Def.java b/apps/jrobin/java/src/org/jrobin/graph/Def.java
deleted file mode 100644
index 06072fb5db84efe96686ee2599f51112f2b0e34a..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/Def.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.graph;
-
-import org.jrobin.data.DataProcessor;
-
-class Def extends Source {
-	private final String rrdPath, dsName, consolFun, backend;
-
-	Def(String name, String rrdPath, String dsName, String consolFun) {
-		this(name, rrdPath, dsName, consolFun, null);
-	}
-
-	Def(String name, String rrdPath, String dsName, String consolFun, String backend) {
-		super(name);
-		this.rrdPath = rrdPath;
-		this.dsName = dsName;
-		this.consolFun = consolFun;
-		this.backend = backend;
-	}
-
-	void requestData(DataProcessor dproc) {
-		if (backend == null) {
-			dproc.addDatasource(name, rrdPath, dsName, consolFun);
-		}
-		else {
-			dproc.addDatasource(name, rrdPath, dsName, consolFun, backend);
-		}
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/GifEncoder.java b/apps/jrobin/java/src/org/jrobin/graph/GifEncoder.java
deleted file mode 100644
index a90d171925e1901c205705a0b2d5d45fb183f412..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/GifEncoder.java
+++ /dev/null
@@ -1,728 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-///////////////////////////////////////////////////////////////////
-// GifEncoder from J.M.G. Elliott
-// http://jmge.net/java/gifenc/
-///////////////////////////////////////////////////////////////////
-
-package org.jrobin.graph;
-
-import java.awt.*;
-import java.awt.image.PixelGrabber;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Vector;
-
-class GifEncoder {
-	private Dimension dispDim = new Dimension(0, 0);
-	private GifColorTable colorTable;
-	private int bgIndex = 0;
-	private int loopCount = 1;
-	private String theComments;
-	private Vector<Gif89Frame> vFrames = new Vector<Gif89Frame>();
-
-	GifEncoder() {
-		colorTable = new GifColorTable();
-	}
-
-	GifEncoder(Image static_image) throws IOException {
-		this();
-		addFrame(static_image);
-	}
-
-	GifEncoder(Color[] colors) {
-		colorTable = new GifColorTable(colors);
-	}
-
-	GifEncoder(Color[] colors, int width, int height, byte ci_pixels[])
-			throws IOException {
-		this(colors);
-		addFrame(width, height, ci_pixels);
-	}
-
-	int getFrameCount() {
-		return vFrames.size();
-	}
-
-	Gif89Frame getFrameAt(int index) {
-		return isOk(index) ? vFrames.elementAt(index) : null;
-	}
-
-	void addFrame(Gif89Frame gf) throws IOException {
-		accommodateFrame(gf);
-		vFrames.addElement(gf);
-	}
-
-	void addFrame(Image image) throws IOException {
-		addFrame(new DirectGif89Frame(image));
-	}
-
-	void addFrame(int width, int height, byte ci_pixels[])
-			throws IOException {
-		addFrame(new IndexGif89Frame(width, height, ci_pixels));
-	}
-
-	void insertFrame(int index, Gif89Frame gf) throws IOException {
-		accommodateFrame(gf);
-		vFrames.insertElementAt(gf, index);
-	}
-
-	void setTransparentIndex(int index) {
-		colorTable.setTransparent(index);
-	}
-
-	void setLogicalDisplay(Dimension dim, int background) {
-		dispDim = new Dimension(dim);
-		bgIndex = background;
-	}
-
-	void setLoopCount(int count) {
-		loopCount = count;
-	}
-
-	void setComments(String comments) {
-		theComments = comments;
-	}
-
-	void setUniformDelay(int interval) {
-		for (int i = 0; i < vFrames.size(); ++i) {
-			vFrames.elementAt(i).setDelay(interval);
-		}
-	}
-
-	void encode(OutputStream out) throws IOException {
-		int nframes = getFrameCount();
-		boolean is_sequence = nframes > 1;
-		colorTable.closePixelProcessing();
-		Put.ascii("GIF89a", out);
-		writeLogicalScreenDescriptor(out);
-		colorTable.encode(out);
-		if (is_sequence && loopCount != 1) {
-			writeNetscapeExtension(out);
-		}
-		if (theComments != null && theComments.length() > 0) {
-			writeCommentExtension(out);
-		}
-		for (int i = 0; i < nframes; ++i) {
-			vFrames.elementAt(i).encode(
-					out, is_sequence, colorTable.getDepth(), colorTable.getTransparent()
-			);
-		}
-		out.write((int) ';');
-		out.flush();
-	}
-
-	private void accommodateFrame(Gif89Frame gf) throws IOException {
-		dispDim.width = Math.max(dispDim.width, gf.getWidth());
-		dispDim.height = Math.max(dispDim.height, gf.getHeight());
-		colorTable.processPixels(gf);
-	}
-
-	private void writeLogicalScreenDescriptor(OutputStream os) throws IOException {
-		Put.leShort(dispDim.width, os);
-		Put.leShort(dispDim.height, os);
-		os.write(0xf0 | colorTable.getDepth() - 1);
-		os.write(bgIndex);
-		os.write(0);
-	}
-
-
-	private void writeNetscapeExtension(OutputStream os) throws IOException {
-		os.write((int) '!');
-		os.write(0xff);
-		os.write(11);
-		Put.ascii("NETSCAPE2.0", os);
-		os.write(3);
-		os.write(1);
-		Put.leShort(loopCount > 1 ? loopCount - 1 : 0, os);
-		os.write(0);
-	}
-
-
-	private void writeCommentExtension(OutputStream os) throws IOException {
-		os.write((int) '!');
-		os.write(0xfe);
-		int remainder = theComments.length() % 255;
-		int nsubblocks_full = theComments.length() / 255;
-		int nsubblocks = nsubblocks_full + (remainder > 0 ? 1 : 0);
-		int ibyte = 0;
-		for (int isb = 0; isb < nsubblocks; ++isb) {
-			int size = isb < nsubblocks_full ? 255 : remainder;
-			os.write(size);
-			Put.ascii(theComments.substring(ibyte, ibyte + size), os);
-			ibyte += size;
-		}
-		os.write(0);
-	}
-
-
-	private boolean isOk(int frame_index) {
-		return frame_index >= 0 && frame_index < vFrames.size();
-	}
-}
-
-class DirectGif89Frame extends Gif89Frame {
-	private int[] argbPixels;
-
-	DirectGif89Frame(Image img) throws IOException {
-		PixelGrabber pg = new PixelGrabber(img, 0, 0, -1, -1, true);
-		String errmsg = null;
-		try {
-			if (!pg.grabPixels()) {
-				errmsg = "can't grab pixels from image";
-			}
-		}
-		catch (InterruptedException e) {
-			errmsg = "interrupted grabbing pixels from image";
-		}
-		if (errmsg != null) {
-			throw new IOException(errmsg + " (" + getClass().getName() + ")");
-		}
-		theWidth = pg.getWidth();
-		theHeight = pg.getHeight();
-		argbPixels = (int[]) pg.getPixels();
-		ciPixels = new byte[argbPixels.length];
-	}
-
-	DirectGif89Frame(int width, int height, int argb_pixels[]) {
-		theWidth = width;
-		theHeight = height;
-		argbPixels = new int[theWidth * theHeight];
-		System.arraycopy(argb_pixels, 0, argbPixels, 0, argbPixels.length);
-		ciPixels = new byte[argbPixels.length];
-	}
-
-	Object getPixelSource() {
-		return argbPixels;
-	}
-}
-
-
-class GifColorTable {
-	private int[] theColors = new int[256];
-	private int colorDepth;
-	private int transparentIndex = -1;
-	private int ciCount = 0;
-	private ReverseColorMap ciLookup;
-
-	GifColorTable() {
-		ciLookup = new ReverseColorMap();
-	}
-
-	GifColorTable(Color[] colors) {
-		int n2copy = Math.min(theColors.length, colors.length);
-		for (int i = 0; i < n2copy; ++i) {
-			theColors[i] = colors[i].getRGB();
-		}
-	}
-
-	int getDepth() {
-		return colorDepth;
-	}
-
-	int getTransparent() {
-		return transparentIndex;
-	}
-
-	void setTransparent(int color_index) {
-		transparentIndex = color_index;
-	}
-
-	void processPixels(Gif89Frame gf) throws IOException {
-		if (gf instanceof DirectGif89Frame) {
-			filterPixels((DirectGif89Frame) gf);
-		}
-		else {
-			trackPixelUsage((IndexGif89Frame) gf);
-		}
-	}
-
-	void closePixelProcessing() {
-		colorDepth = computeColorDepth(ciCount);
-	}
-
-	void encode(OutputStream os) throws IOException {
-		int palette_size = 1 << colorDepth;
-		for (int i = 0; i < palette_size; ++i) {
-			os.write(theColors[i] >> 16 & 0xff);
-			os.write(theColors[i] >> 8 & 0xff);
-			os.write(theColors[i] & 0xff);
-		}
-	}
-
-	private void filterPixels(DirectGif89Frame dgf) throws IOException {
-		if (ciLookup == null) {
-			throw new IOException("RGB frames require palette autodetection");
-		}
-		int[] argb_pixels = (int[]) dgf.getPixelSource();
-		byte[] ci_pixels = dgf.getPixelSink();
-		int npixels = argb_pixels.length;
-		for (int i = 0; i < npixels; ++i) {
-			int argb = argb_pixels[i];
-			if ((argb >>> 24) < 0x80) {
-				if (transparentIndex == -1) {
-					transparentIndex = ciCount;
-				}
-				else if (argb != theColors[transparentIndex]) {
-					ci_pixels[i] = (byte) transparentIndex;
-					continue;
-				}
-			}
-			int color_index = ciLookup.getPaletteIndex(argb & 0xffffff);
-			if (color_index == -1) {
-				if (ciCount == 256) {
-					throw new IOException("can't encode as GIF (> 256 colors)");
-				}
-				theColors[ciCount] = argb;
-				ciLookup.put(argb & 0xffffff, ciCount);
-				ci_pixels[i] = (byte) ciCount;
-				++ciCount;
-			}
-			else {
-				ci_pixels[i] = (byte) color_index;
-			}
-		}
-	}
-
-	private void trackPixelUsage(IndexGif89Frame igf) {
-		byte[] ci_pixels = (byte[]) igf.getPixelSource();
-		int npixels = ci_pixels.length;
-		for (int i = 0; i < npixels; ++i) {
-			if (ci_pixels[i] >= ciCount) {
-				ciCount = ci_pixels[i] + 1;
-			}
-		}
-	}
-
-	private int computeColorDepth(int colorcount) {
-		if (colorcount <= 2) {
-			return 1;
-		}
-		if (colorcount <= 4) {
-			return 2;
-		}
-		if (colorcount <= 16) {
-			return 4;
-		}
-		return 8;
-	}
-}
-
-class ReverseColorMap {
-	private static class ColorRecord {
-		int rgb;
-		int ipalette;
-
-		ColorRecord(int rgb, int ipalette) {
-			this.rgb = rgb;
-			this.ipalette = ipalette;
-		}
-	}
-
-	private static final int HCAPACITY = 2053;
-	private ColorRecord[] hTable = new ColorRecord[HCAPACITY];
-
-	int getPaletteIndex(int rgb) {
-		ColorRecord rec;
-		for (int itable = rgb % hTable.length;
-			 (rec = hTable[itable]) != null && rec.rgb != rgb;
-			 itable = ++itable % hTable.length
-				) {
-			;
-		}
-		if (rec != null) {
-			return rec.ipalette;
-		}
-		return -1;
-	}
-
-
-	void put(int rgb, int ipalette) {
-		int itable;
-		for (itable = rgb % hTable.length;
-			 hTable[itable] != null;
-			 itable = ++itable % hTable.length
-				) {
-			;
-		}
-		hTable[itable] = new ColorRecord(rgb, ipalette);
-	}
-}
-
-abstract class Gif89Frame {
-	static final int DM_UNDEFINED = 0;
-	static final int DM_LEAVE = 1;
-	static final int DM_BGCOLOR = 2;
-	static final int DM_REVERT = 3;
-	int theWidth = -1;
-	int theHeight = -1;
-	byte[] ciPixels;
-
-	private Point thePosition = new Point(0, 0);
-	private boolean isInterlaced;
-	private int csecsDelay;
-	private int disposalCode = DM_LEAVE;
-
-	void setPosition(Point p) {
-		thePosition = new Point(p);
-	}
-
-	void setInterlaced(boolean b) {
-		isInterlaced = b;
-	}
-
-	void setDelay(int interval) {
-		csecsDelay = interval;
-	}
-
-	void setDisposalMode(int code) {
-		disposalCode = code;
-	}
-
-	Gif89Frame() {
-	}
-
-	abstract Object getPixelSource();
-
-	int getWidth() {
-		return theWidth;
-	}
-
-	int getHeight() {
-		return theHeight;
-	}
-
-	byte[] getPixelSink() {
-		return ciPixels;
-	}
-
-	void encode(OutputStream os, boolean epluribus, int color_depth,
-				int transparent_index) throws IOException {
-		writeGraphicControlExtension(os, epluribus, transparent_index);
-		writeImageDescriptor(os);
-		new GifPixelsEncoder(
-				theWidth, theHeight, ciPixels, isInterlaced, color_depth
-		).encode(os);
-	}
-
-	private void writeGraphicControlExtension(OutputStream os, boolean epluribus,
-											  int itransparent) throws IOException {
-		int transflag = itransparent == -1 ? 0 : 1;
-		if (transflag == 1 || epluribus) {
-			os.write((int) '!');
-			os.write(0xf9);
-			os.write(4);
-			os.write((disposalCode << 2) | transflag);
-			Put.leShort(csecsDelay, os);
-			os.write(itransparent);
-			os.write(0);
-		}
-	}
-
-	private void writeImageDescriptor(OutputStream os) throws IOException {
-		os.write((int) ',');
-		Put.leShort(thePosition.x, os);
-		Put.leShort(thePosition.y, os);
-		Put.leShort(theWidth, os);
-		Put.leShort(theHeight, os);
-		os.write(isInterlaced ? 0x40 : 0);
-	}
-}
-
-class GifPixelsEncoder {
-	private static final int EOF = -1;
-	private int imgW, imgH;
-	private byte[] pixAry;
-	private boolean wantInterlaced;
-	private int initCodeSize;
-	private int countDown;
-	private int xCur, yCur;
-	private int curPass;
-
-	GifPixelsEncoder(int width, int height, byte[] pixels, boolean interlaced,
-					 int color_depth) {
-		imgW = width;
-		imgH = height;
-		pixAry = pixels;
-		wantInterlaced = interlaced;
-		initCodeSize = Math.max(2, color_depth);
-	}
-
-	void encode(OutputStream os) throws IOException {
-		os.write(initCodeSize);
-
-		countDown = imgW * imgH;
-		xCur = yCur = curPass = 0;
-
-		compress(initCodeSize + 1, os);
-
-		os.write(0);
-	}
-
-	private void bumpPosition() {
-		++xCur;
-		if (xCur == imgW) {
-			xCur = 0;
-			if (!wantInterlaced) {
-				++yCur;
-			}
-			else {
-				switch (curPass) {
-					case 0:
-						yCur += 8;
-						if (yCur >= imgH) {
-							++curPass;
-							yCur = 4;
-						}
-						break;
-					case 1:
-						yCur += 8;
-						if (yCur >= imgH) {
-							++curPass;
-							yCur = 2;
-						}
-						break;
-					case 2:
-						yCur += 4;
-						if (yCur >= imgH) {
-							++curPass;
-							yCur = 1;
-						}
-						break;
-					case 3:
-						yCur += 2;
-						break;
-				}
-			}
-		}
-	}
-
-	private int nextPixel() {
-		if (countDown == 0) {
-			return EOF;
-		}
-		--countDown;
-		byte pix = pixAry[yCur * imgW + xCur];
-		bumpPosition();
-		return pix & 0xff;
-	}
-
-	static final int BITS = 12;
-	static final int HSIZE = 5003;
-	int n_bits;
-	int maxbits = BITS;
-	int maxcode;
-	int maxmaxcode = 1 << BITS;
-
-	final int MAXCODE(int n_bits) {
-		return (1 << n_bits) - 1;
-	}
-
-	int[] htab = new int[HSIZE];
-	int[] codetab = new int[HSIZE];
-	int hsize = HSIZE;
-	int free_ent = 0;
-	boolean clear_flg = false;
-	int g_init_bits;
-	int ClearCode;
-	int EOFCode;
-
-	void compress(int init_bits, OutputStream outs) throws IOException {
-		int fcode;
-		int i /* = 0 */;
-		int c;
-		int ent;
-		int disp;
-		int hsize_reg;
-		int hshift;
-		g_init_bits = init_bits;
-		clear_flg = false;
-		n_bits = g_init_bits;
-		maxcode = MAXCODE(n_bits);
-		ClearCode = 1 << (init_bits - 1);
-		EOFCode = ClearCode + 1;
-		free_ent = ClearCode + 2;
-
-		char_init();
-		ent = nextPixel();
-		hshift = 0;
-		for (fcode = hsize; fcode < 65536; fcode *= 2) {
-			++hshift;
-		}
-		hshift = 8 - hshift;
-		hsize_reg = hsize;
-		cl_hash(hsize_reg);
-		output(ClearCode, outs);
-		outer_loop:
-		while ((c = nextPixel()) != EOF) {
-			fcode = (c << maxbits) + ent;
-			i = (c << hshift) ^ ent;
-			if (htab[i] == fcode) {
-				ent = codetab[i];
-				continue;
-			}
-			else if (htab[i] >= 0) {
-				disp = hsize_reg - i;
-				if (i == 0) {
-					disp = 1;
-				}
-				do {
-					if ((i -= disp) < 0) {
-						i += hsize_reg;
-					}
-
-					if (htab[i] == fcode) {
-						ent = codetab[i];
-						continue outer_loop;
-					}
-				} while (htab[i] >= 0);
-			}
-			output(ent, outs);
-			ent = c;
-			if (free_ent < maxmaxcode) {
-				codetab[i] = free_ent++;
-				htab[i] = fcode;
-			}
-			else {
-				cl_block(outs);
-			}
-		}
-		output(ent, outs);
-		output(EOFCode, outs);
-	}
-
-	int cur_accum = 0;
-	int cur_bits = 0;
-	int masks[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
-			0x001F, 0x003F, 0x007F, 0x00FF,
-			0x01FF, 0x03FF, 0x07FF, 0x0FFF,
-			0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF};
-
-	void output(int code, OutputStream outs) throws IOException {
-		cur_accum &= masks[cur_bits];
-		if (cur_bits > 0) {
-			cur_accum |= (code << cur_bits);
-		}
-		else {
-			cur_accum = code;
-		}
-
-		cur_bits += n_bits;
-
-		while (cur_bits >= 8) {
-			char_out((byte) (cur_accum & 0xff), outs);
-			cur_accum >>= 8;
-			cur_bits -= 8;
-		}
-		if (free_ent > maxcode || clear_flg) {
-			if (clear_flg) {
-				maxcode = MAXCODE(n_bits = g_init_bits);
-				clear_flg = false;
-			}
-			else {
-				++n_bits;
-				if (n_bits == maxbits) {
-					maxcode = maxmaxcode;
-				}
-				else {
-					maxcode = MAXCODE(n_bits);
-				}
-			}
-		}
-		if (code == EOFCode) {
-
-			while (cur_bits > 0) {
-				char_out((byte) (cur_accum & 0xff), outs);
-				cur_accum >>= 8;
-				cur_bits -= 8;
-			}
-			flush_char(outs);
-		}
-	}
-
-
-	void cl_block(OutputStream outs) throws IOException {
-		cl_hash(hsize);
-		free_ent = ClearCode + 2;
-		clear_flg = true;
-
-		output(ClearCode, outs);
-	}
-
-
-	void cl_hash(int hsize) {
-		for (int i = 0; i < hsize; ++i) {
-			htab[i] = -1;
-		}
-	}
-
-	int a_count;
-
-	void char_init() {
-		a_count = 0;
-	}
-
-	byte[] accum = new byte[256];
-
-	void char_out(byte c, OutputStream outs) throws IOException {
-		accum[a_count++] = c;
-		if (a_count >= 254) {
-			flush_char(outs);
-		}
-	}
-
-	void flush_char(OutputStream outs) throws IOException {
-		if (a_count > 0) {
-			outs.write(a_count);
-			outs.write(accum, 0, a_count);
-			a_count = 0;
-		}
-	}
-}
-
-class IndexGif89Frame extends Gif89Frame {
-
-	IndexGif89Frame(int width, int height, byte ci_pixels[]) {
-		theWidth = width;
-		theHeight = height;
-		ciPixels = new byte[theWidth * theHeight];
-		System.arraycopy(ci_pixels, 0, ciPixels, 0, ciPixels.length);
-	}
-
-	Object getPixelSource() {
-		return ciPixels;
-	}
-}
-
-
-final class Put {
-	static void ascii(String s, OutputStream os) throws IOException {
-		byte[] bytes = new byte[s.length()];
-		for (int i = 0; i < bytes.length; ++i) {
-			bytes[i] = (byte) s.charAt(i);
-		}
-		os.write(bytes);
-	}
-
-	static void leShort(int i16, OutputStream os) throws IOException {
-		os.write(i16 & 0xff);
-		os.write(i16 >> 8 & 0xff);
-	}
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/graph/HRule.java b/apps/jrobin/java/src/org/jrobin/graph/HRule.java
deleted file mode 100644
index 163d59a21748fe1605b43557c44e3bce07847e80..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/HRule.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.graph;
-
-import java.awt.*;
-
-class HRule extends Rule {
-	final double value;
-
-	HRule(double value, Paint color, LegendText legend, float width) {
-		super(color, legend, width);
-		this.value = value;
-	}
-
-	void setLegendVisibility(double minval, double maxval, boolean forceLegend) {
-		legend.enabled &= (forceLegend || (value >= minval && value <= maxval));
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/ImageParameters.java b/apps/jrobin/java/src/org/jrobin/graph/ImageParameters.java
deleted file mode 100644
index ddc55b181d3a0da44222f6fad81be2ac58e37ff0..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/ImageParameters.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-class ImageParameters {
-	long start, end;
-	double minval, maxval;
-	int unitsexponent;
-	double base;
-	double magfact;
-	char symbol;
-	double ygridstep;
-	int ylabfact;
-	double decimals;
-	int quadrant;
-	double scaledstep;
-	int xsize;
-	int ysize;
-	int xorigin;
-	int yorigin;
-	int unitslength;
-	int xgif, ygif;
-	String unit;
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/ImageWorker.java b/apps/jrobin/java/src/org/jrobin/graph/ImageWorker.java
deleted file mode 100644
index 7a5fd53c337e51f2b70e84b3b1e48b3d6dc416d1..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/ImageWorker.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-import java.awt.font.LineMetrics;
-import java.awt.geom.AffineTransform;
-import java.awt.image.BufferedImage;
-import java.io.*;
-import java.util.Iterator;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageWriteParam;
-import javax.imageio.ImageWriter;
-
-class ImageWorker {
-    private static final String DUMMY_TEXT = "Dummy";
-
-    private BufferedImage img;
-    private Graphics2D gd;
-    private int imgWidth, imgHeight;
-    private AffineTransform aftInitial;
-
-    ImageWorker(int width, int height) {
-        resize(width, height);
-    }
-
-    void resize(int width, int height) {
-        if (gd != null) {
-            gd.dispose();
-        }
-        this.imgWidth = width;
-        this.imgHeight = height;
-        this.img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-        this.gd = img.createGraphics();
-        this.aftInitial = gd.getTransform();
-        this.setAntiAliasing(false);
-    }
-
-    void clip(int x, int y, int width, int height) {
-        gd.setClip(x, y, width, height);
-    }
-
-    void transform(int x, int y, double angle) {
-        gd.translate(x, y);
-        gd.rotate(angle);
-    }
-
-    void reset() {
-        gd.setTransform(aftInitial);
-        gd.setClip(0, 0, imgWidth, imgHeight);
-    }
-
-    void fillRect(int x, int y, int width, int height, Paint paint) {
-        gd.setPaint(paint);
-        gd.fillRect(x, y, width, height);
-    }
-
-    void fillPolygon(int[] x, int[] y, Paint paint) {
-        gd.setPaint(paint);
-        gd.fillPolygon(x, y, x.length);
-    }
-
-    void fillPolygon(double[] x, double yBottom, double[] yTop, Paint paint) {
-        gd.setPaint(paint);
-        PathIterator path = new PathIterator(yTop);
-        for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
-            int start = pos[0], end = pos[1], n = end - start;
-            int[] xDev = new int[n + 2], yDev = new int[n + 2];
-            for (int i = start; i < end; i++) {
-                xDev[i - start] = (int) x[i];
-                yDev[i - start] = (int) yTop[i];
-            }
-            xDev[n] = xDev[n - 1];
-            xDev[n + 1] = xDev[0];
-            yDev[n] = yDev[n + 1] = (int) yBottom;
-            gd.fillPolygon(xDev, yDev, xDev.length);
-            gd.drawPolygon(xDev, yDev, xDev.length);
-        }
-    }
-
-    void fillPolygon(double[] x, double[] yBottom, double[] yTop, Paint paint) {
-        gd.setPaint(paint);
-        PathIterator path = new PathIterator(yTop);
-        for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
-            int start = pos[0], end = pos[1], n = end - start;
-            int[] xDev = new int[n * 2], yDev = new int[n * 2];
-            for (int i = start; i < end; i++) {
-                int ix1 = i - start, ix2 = n * 2 - 1 - i + start;
-                xDev[ix1] = xDev[ix2] = (int) x[i];
-                yDev[ix1] = (int) yTop[i];
-                yDev[ix2] = (int) yBottom[i];
-            }
-            gd.fillPolygon(xDev, yDev, xDev.length);
-            gd.drawPolygon(xDev, yDev, xDev.length);
-        }
-    }
-
-    void drawLine(int x1, int y1, int x2, int y2, Paint paint, Stroke stroke) {
-        gd.setStroke(stroke);
-        gd.setPaint(paint);
-        gd.drawLine(x1, y1, x2, y2);
-    }
-
-    void drawPolyline(int[] x, int[] y, Paint paint, Stroke stroke) {
-        gd.setStroke(stroke);
-        gd.setPaint(paint);
-        gd.drawPolyline(x, y, x.length);
-    }
-
-    void drawPolyline(double[] x, double[] y, Paint paint, Stroke stroke) {
-        gd.setPaint(paint);
-        gd.setStroke(stroke);
-        PathIterator path = new PathIterator(y);
-        for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
-            int start = pos[0], end = pos[1];
-            int[] xDev = new int[end - start], yDev = new int[end - start];
-            for (int i = start; i < end; i++) {
-                xDev[i - start] = (int) x[i];
-                yDev[i - start] = (int) y[i];
-            }
-            gd.drawPolyline(xDev, yDev, xDev.length);
-        }
-    }
-
-    void drawString(String text, int x, int y, Font font, Paint paint) {
-        gd.setFont(font);
-        gd.setPaint(paint);
-        gd.drawString(text, x, y);
-    }
-
-    double getFontAscent(Font font) {
-        LineMetrics lm = font.getLineMetrics(DUMMY_TEXT, gd.getFontRenderContext());
-        return lm.getAscent();
-    }
-
-    double getFontHeight(Font font) {
-        LineMetrics lm = font.getLineMetrics(DUMMY_TEXT, gd.getFontRenderContext());
-        return lm.getAscent() + lm.getDescent();
-    }
-
-    double getStringWidth(String text, Font font) {
-        return font.getStringBounds(text, 0, text.length(), gd.getFontRenderContext()).getBounds().getWidth();
-    }
-
-    void setAntiAliasing(boolean enable) {
-        gd.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-                enable ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
-        gd.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
-        gd.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
-    }
-
-    void dispose() {
-        gd.dispose();
-    }
-
-    void saveImage(OutputStream stream, String type, float quality) throws IOException {
-        if (type.equalsIgnoreCase("png")) {
-            ImageIO.write(img, "png", stream);
-        }
-        else if (type.equalsIgnoreCase("gif")) {
-            GifEncoder gifEncoder = new GifEncoder(img);
-            gifEncoder.encode(stream);
-        }
-        else if (type.equalsIgnoreCase("jpg") || type.equalsIgnoreCase("jpeg")) {
-            Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpg");
-            ImageWriter writer = iter.next();
-            ImageWriteParam iwp = writer.getDefaultWriteParam();
-
-            iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
-            iwp.setCompressionQuality(quality);
-            writer.setOutput(stream);
-            writer.write(img);
-            writer.dispose();
-        }
-        else {
-            throw new IOException("Unsupported image format: " + type);
-        }
-        stream.flush();
-    }
-
-    byte[] saveImage(String path, String type, float quality) throws IOException {
-        byte[] bytes = getImageBytes(type, quality);
-        RandomAccessFile f = new RandomAccessFile(path, "rw");
-        try {
-            f.write(bytes);
-            return bytes;
-        } finally {
-            f.close();
-        }
-    }
-
-    byte[] getImageBytes(String type, float quality) throws IOException {
-        ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        try {
-            saveImage(stream, type, quality);
-            return stream.toByteArray();
-        } finally {
-            stream.close();
-        }
-    }
-
-    public void loadImage(String imageFile) throws IOException {
-        BufferedImage wpImage = ImageIO.read(new File(imageFile));
-        TexturePaint paint = new TexturePaint(wpImage, new Rectangle(0, 0, wpImage.getWidth(), wpImage.getHeight()));
-        gd.setPaint(paint);
-        gd.fillRect(0, 0, wpImage.getWidth(), wpImage.getHeight());
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/LegendComposer.java b/apps/jrobin/java/src/org/jrobin/graph/LegendComposer.java
deleted file mode 100644
index 85a9b0e18ede4827a3693b509f0ebecb733d6f28..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/LegendComposer.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class LegendComposer implements RrdGraphConstants {
-	private RrdGraphDef gdef;
-	private ImageWorker worker;
-	private int legX, legY, legWidth;
-	private double interlegendSpace;
-	private double leading;
-	private double smallLeading;
-	private double boxSpace;
-
-	LegendComposer(RrdGraph rrdGraph, int legX, int legY, int legWidth) {
-		this.gdef = rrdGraph.gdef;
-		this.worker = rrdGraph.worker;
-		this.legX = legX;
-		this.legY = legY;
-		this.legWidth = legWidth;
-		interlegendSpace = rrdGraph.getInterlegendSpace();
-		leading = rrdGraph.getLeading();
-		smallLeading = rrdGraph.getSmallLeading();
-		boxSpace = rrdGraph.getBoxSpace();
-	}
-
-	int placeComments() {
-		Line line = new Line();
-		for (CommentText comment : gdef.comments) {
-			if (comment.isValidGraphElement()) {
-				if (!line.canAccomodate(comment)) {
-					line.layoutAndAdvance(false);
-					line.clear();
-				}
-				line.add(comment);
-			}
-		}
-		line.layoutAndAdvance(true);
-		worker.dispose();
-		return legY;
-	}
-
-	class Line {
-		private String lastMarker;
-		private double width;
-		private int spaceCount;
-		private boolean noJustification;
-		private List<CommentText> comments = new ArrayList<CommentText>();
-
-		Line() {
-			clear();
-		}
-
-		void clear() {
-			lastMarker = "";
-			width = 0;
-			spaceCount = 0;
-			noJustification = false;
-			comments.clear();
-		}
-
-		boolean canAccomodate(CommentText comment) {
-			// always accommodate if empty
-			if (comments.size() == 0) {
-				return true;
-			}
-			// cannot accommodate if the last marker was \j, \l, \r, \c, \s
-			if (lastMarker.equals(ALIGN_LEFT_MARKER) || lastMarker.equals(ALIGN_CENTER_MARKER) ||
-					lastMarker.equals(ALIGN_RIGHT_MARKER) || lastMarker.equals(ALIGN_JUSTIFIED_MARKER) ||
-					lastMarker.equals(VERTICAL_SPACING_MARKER)) {
-				return false;
-			}
-			// cannot accommodate if line would be too long
-			double commentWidth = getCommentWidth(comment);
-			if (!lastMarker.equals(GLUE_MARKER)) {
-				commentWidth += interlegendSpace;
-			}
-			return width + commentWidth <= legWidth;
-		}
-
-		void add(CommentText comment) {
-			double commentWidth = getCommentWidth(comment);
-			if (comments.size() > 0 && !lastMarker.equals(GLUE_MARKER)) {
-				commentWidth += interlegendSpace;
-				spaceCount++;
-			}
-			width += commentWidth;
-			lastMarker = comment.marker;
-			noJustification |= lastMarker.equals(NO_JUSTIFICATION_MARKER);
-			comments.add(comment);
-		}
-
-		void layoutAndAdvance(boolean isLastLine) {
-			if (comments.size() > 0) {
-				if (lastMarker.equals(ALIGN_LEFT_MARKER)) {
-					placeComments(legX, interlegendSpace);
-				}
-				else if (lastMarker.equals(ALIGN_RIGHT_MARKER)) {
-					placeComments(legX + legWidth - width, interlegendSpace);
-				}
-				else if (lastMarker.equals(ALIGN_CENTER_MARKER)) {
-					placeComments(legX + (legWidth - width) / 2.0, interlegendSpace);
-				}
-				else if (lastMarker.equals(ALIGN_JUSTIFIED_MARKER)) {
-					// anything to justify?
-					if (spaceCount > 0) {
-						placeComments(legX, (legWidth - width) / spaceCount + interlegendSpace);
-					}
-					else {
-						placeComments(legX, interlegendSpace);
-					}
-				}
-				else if (lastMarker.equals(VERTICAL_SPACING_MARKER)) {
-					placeComments(legX, interlegendSpace);
-				}
-				else {
-					// nothing specified, align with respect to '\J'
-					if (noJustification || isLastLine) {
-						placeComments(legX, interlegendSpace);
-					}
-					else {
-						placeComments(legX, (legWidth - width) / spaceCount + interlegendSpace);
-					}
-				}
-				if (lastMarker.equals(VERTICAL_SPACING_MARKER)) {
-					legY += smallLeading;
-				}
-				else {
-					legY += leading;
-				}
-			}
-		}
-
-		private double getCommentWidth(CommentText comment) {
-			double commentWidth = worker.getStringWidth(comment.resolvedText, gdef.getFont(FONTTAG_LEGEND));
-			if (comment instanceof LegendText) {
-				commentWidth += boxSpace;
-			}
-			return commentWidth;
-		}
-
-		private void placeComments(double xStart, double space) {
-			double x = xStart;
-			for (CommentText comment : comments) {
-				comment.x = (int) x;
-				comment.y = legY;
-				x += getCommentWidth(comment);
-				if (!comment.marker.equals(GLUE_MARKER)) {
-					x += space;
-				}
-			}
-		}
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/LegendText.java b/apps/jrobin/java/src/org/jrobin/graph/LegendText.java
deleted file mode 100644
index da2efb79ccc431a223b71a207ff20920a5e13bab..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/LegendText.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-
-class LegendText extends CommentText {
-	final Paint legendColor;
-
-	LegendText(Paint legendColor, String text) {
-		super(text);
-		this.legendColor = legendColor;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/Line.java b/apps/jrobin/java/src/org/jrobin/graph/Line.java
deleted file mode 100644
index 76ce08bc41c631beec8c2915caec0af4a14d3b01..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/Line.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-
-class Line extends SourcedPlotElement {
-	final float width;
-
-	Line(String srcName, Paint color, float width) {
-		super(srcName, color);
-		this.width = width;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/Mapper.java b/apps/jrobin/java/src/org/jrobin/graph/Mapper.java
deleted file mode 100644
index e0b2f8f9c65865f6569bb74d5d81a147dc47beca..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/Mapper.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-class Mapper {
-	private RrdGraphDef gdef;
-	private ImageParameters im;
-	private double pixieX, pixieY;
-
-	Mapper(RrdGraph rrdGraph) {
-		this(rrdGraph.gdef, rrdGraph.im);
-	}
-
-	Mapper(RrdGraphDef gdef, ImageParameters im) {
-		this.gdef = gdef;
-		this.im = im;
-		pixieX = (double) im.xsize / (double) (im.end - im.start);
-		if (!gdef.logarithmic) {
-			pixieY = (double) im.ysize / (im.maxval - im.minval);
-		}
-		else {
-			pixieY = (double) im.ysize / (Math.log10(im.maxval) - Math.log10(im.minval));
-		}
-	}
-
-	int xtr(double mytime) {
-		return (int) ((double) im.xorigin + pixieX * (mytime - im.start));
-	}
-
-	int ytr(double value) {
-		double yval;
-		if (!gdef.logarithmic) {
-			yval = im.yorigin - pixieY * (value - im.minval) + 0.5;
-		}
-		else {
-			if (value < im.minval) {
-				yval = im.yorigin;
-			}
-			else {
-				yval = im.yorigin - pixieY * (Math.log10(value) - Math.log10(im.minval)) + 0.5;
-			}
-		}
-		if (!gdef.rigid) {
-			return (int) yval;
-		}
-		else if ((int) yval > im.yorigin) {
-			return im.yorigin + 2;
-		}
-		else if ((int) yval < im.yorigin - im.ysize) {
-			return im.yorigin - im.ysize - 2;
-		}
-		else {
-			return (int) yval;
-		}
-	}
-}
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/jrobin/graph/Normalizer.java b/apps/jrobin/java/src/org/jrobin/graph/Normalizer.java
deleted file mode 100644
index 6a3ce265dcf624bf69464ca5bcdae2902cb4d540..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/Normalizer.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-
-package org.jrobin.graph;
-
-import org.jrobin.core.Util;
-
-import java.util.Arrays;
-
-class Normalizer {
-	final private double[] timestamps;
-	final int count;
-	final double step;
-
-	Normalizer(long tStart, long tEnd, int count) {
-		this.count = count;
-		this.step = (tEnd - tStart) / Double.valueOf(count - 1);
-		this.timestamps = new double[count];
-		for (int i = 0; i < count; i++) {
-			this.timestamps[i] = tStart + ((double) i / (double) (count - 1)) * (tEnd - tStart);
-		}
-	}
-
-	double[] getTimestamps() {
-		return timestamps;
-	}
-
-	double[] normalize(long[] rawTimestamps, double[] rawValues) {
-		int rawCount = rawTimestamps.length;
-		long rawStep = rawTimestamps[1] - rawTimestamps[0];
-		// check if we have a simple match
-		if (rawCount == count && rawStep == step && rawTimestamps[0] == timestamps[0]) {
-			return getCopyOf(rawValues);
-		}
-		// reset all normalized values to NaN
-		double[] values = new double[count];
-		Arrays.fill(values, Double.NaN);
-		for (int rawSeg = 0, seg = 0; rawSeg < rawCount && seg < count; rawSeg++) {
-			double rawValue = rawValues[rawSeg];
-			if (!Double.isNaN(rawValue)) {
-				long rawLeft = rawTimestamps[rawSeg] - rawStep;
-				while (seg < count && rawLeft >= timestamps[seg]) {
-					seg++;
-				}
-				boolean overlap = true;
-				for (int fillSeg = seg; overlap && fillSeg < count; fillSeg++) {
-					double left = timestamps[fillSeg] - step;
-					double t1 = Math.max(rawLeft, left);
-					double t2 = Math.min(rawTimestamps[rawSeg], timestamps[fillSeg]);
-					if (t1 < t2) {
-						values[fillSeg] = Util.sum(values[fillSeg], (t2 - t1) * rawValues[rawSeg]);
-					}
-					else {
-						overlap = false;
-					}
-				}
-			}
-		}
-		for (int seg = 0; seg < count; seg++) {
-			values[seg] /= step;
-		}
-		return values;
-	}
-
-	private static double[] getCopyOf(double[] rawValues) {
-		int n = rawValues.length;
-		double[] values = new double[n];
-		System.arraycopy(rawValues, 0, values, 0, n);
-		return values;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/PDef.java b/apps/jrobin/java/src/org/jrobin/graph/PDef.java
deleted file mode 100644
index 875e88a476b771ccaa12ccad981e8157ed04dec1..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/PDef.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.data.DataProcessor;
-import org.jrobin.data.Plottable;
-
-class PDef extends Source {
-	private Plottable plottable;
-
-	PDef(String name, Plottable plottable) {
-		super(name);
-		this.plottable = plottable;
-	}
-
-	void requestData(DataProcessor dproc) {
-		dproc.addDatasource(name, plottable);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/PathIterator.java b/apps/jrobin/java/src/org/jrobin/graph/PathIterator.java
deleted file mode 100644
index 3e7055f16cea2309893594c0b8d357d95a30a7f5..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/PathIterator.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-class PathIterator {
-	private double[] y;
-	private int pos = 0;
-
-	PathIterator(double[] y) {
-		this.y = y;
-	}
-
-	int[] getNextPath() {
-		while (pos < y.length) {
-			if (Double.isNaN(y[pos])) {
-				pos++;
-			}
-			else {
-				int endPos = pos + 1;
-				while (endPos < y.length && !Double.isNaN(y[endPos])) {
-					endPos++;
-				}
-				int[] result = {pos, endPos};
-				pos = endPos;
-				if (result[1] - result[0] >= 2) {
-					return result;
-				}
-			}
-		}
-		return null;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/PercentileDef.java b/apps/jrobin/java/src/org/jrobin/graph/PercentileDef.java
deleted file mode 100644
index 43e38053f25801010a625a9563f8a4298d041b7c..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/PercentileDef.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2011 Craig Miskell
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.data.DataProcessor;
-import org.jrobin.graph.Source;
-
-public class PercentileDef extends Source {
-    private String m_sourceName;
-
-    private double m_percentile;
-
-    private boolean m_includenan;
-
-    PercentileDef(String name, String sourceName, double percentile) {
-        this(name, sourceName, percentile, false);
-    }
-
-    PercentileDef(String name, String sourceName, double percentile, boolean includenan) {
-        super(name);
-        m_sourceName = sourceName;
-        m_percentile = percentile;
-        m_includenan = includenan;
-    }
-
-    @Override
-    void requestData(DataProcessor dproc) {
-        dproc.addDatasource(name, m_sourceName, m_percentile, m_includenan);
-    }
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/PlotElement.java b/apps/jrobin/java/src/org/jrobin/graph/PlotElement.java
deleted file mode 100644
index 5217434e86a73fd23ec16e6cd526056a4bb25d61..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/PlotElement.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-
-class PlotElement {
-	final Paint color;
-
-	PlotElement(Paint color) {
-		this.color = color;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/PrintText.java b/apps/jrobin/java/src/org/jrobin/graph/PrintText.java
deleted file mode 100644
index ee6875a79bb02f6eafff14089bfa86eeca5f8bfb..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/PrintText.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-import org.jrobin.data.DataProcessor;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-class PrintText extends CommentText {
-	static final String UNIT_MARKER = "([^%]?)%(s|S)";
-	static final Pattern UNIT_PATTERN = Pattern.compile(UNIT_MARKER);
-
-	private final String srcName, consolFun;
-	private final boolean includedInGraph;
-
-	PrintText(String srcName, String consolFun, String text, boolean includedInGraph) {
-		super(text);
-		this.srcName = srcName;
-		this.consolFun = consolFun;
-		this.includedInGraph = includedInGraph;
-	}
-
-	boolean isPrint() {
-		return !includedInGraph;
-	}
-
-	void resolveText(DataProcessor dproc, ValueScaler valueScaler) throws RrdException {
-		super.resolveText(dproc, valueScaler);
-		if (resolvedText != null) {
-			double value = dproc.getAggregate(srcName, consolFun);
-			Matcher matcher = UNIT_PATTERN.matcher(resolvedText);
-			if (matcher.find()) {
-				// unit specified
-				ValueScaler.Scaled scaled = valueScaler.scale(value, matcher.group(2).equals("s"));
-				resolvedText = resolvedText.substring(0, matcher.start()) +
-						matcher.group(1) + scaled.unit + resolvedText.substring(matcher.end());
-				value = scaled.value;
-			}
-			resolvedText = Util.sprintf(resolvedText, value);
-			trimIfGlue();
-		}
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/RrdGraph.java b/apps/jrobin/java/src/org/jrobin/graph/RrdGraph.java
deleted file mode 100644
index 6e1d2f886869337bdc44cf841238d4f59200c831..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/RrdGraph.java
+++ /dev/null
@@ -1,677 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-import org.jrobin.data.DataProcessor;
-
-import javax.swing.*;
-import java.awt.*;
-import java.io.IOException;
-
-/**
- * Class which actually creates JRobin graphs (does the hard work).
- */
-public class RrdGraph implements RrdGraphConstants {
-	RrdGraphDef gdef;
-	ImageParameters im = new ImageParameters();
-	DataProcessor dproc;
-	ImageWorker worker;
-	Mapper mapper;
-	RrdGraphInfo info = new RrdGraphInfo();
-	private String signature;
-
-	/**
-	 * Creates graph from the corresponding {@link RrdGraphDef} object.
-	 *
-	 * @param gdef Graph definition
-	 * @throws IOException  Thrown in case of I/O error
-	 * @throws RrdException Thrown in case of JRobin related error
-	 */
-	public RrdGraph(RrdGraphDef gdef) throws IOException, RrdException {
-		this.gdef = gdef;
-		signature = gdef.getSignature();
-		worker = new ImageWorker(100, 100); // Dummy worker, just to start with something
-		try {
-			createGraph();
-		}
-		finally {
-			worker.dispose();
-			worker = null;
-			dproc = null;
-		}
-	}
-
-	/**
-	 * Returns complete graph information in a single object.
-	 *
-	 * @return Graph information (width, height, filename, image bytes, etc...)
-	 */
-	public RrdGraphInfo getRrdGraphInfo() {
-		return info;
-	}
-
-	private void createGraph() throws RrdException, IOException {
-		boolean lazy = lazyCheck();
-		if (!lazy || gdef.printStatementCount() != 0) {
-			fetchData();
-			resolveTextElements();
-			if (gdef.shouldPlot() && !lazy) {
-				calculatePlotValues();
-				findMinMaxValues();
-				identifySiUnit();
-				expandValueRange();
-				removeOutOfRangeRules();
-				initializeLimits();
-				placeLegends();
-				createImageWorker();
-				drawBackground();
-				drawData();
-				drawGrid();
-				drawAxis();
-				drawText();
-				drawLegend();
-				drawRules();
-				gator();
-				drawOverlay();
-				saveImage();
-			}
-		}
-		collectInfo();
-	}
-
-	private void collectInfo() {
-		info.filename = gdef.filename;
-		info.width = im.xgif;
-		info.height = im.ygif;
-		for (CommentText comment : gdef.comments) {
-			if (comment instanceof PrintText) {
-				PrintText pt = (PrintText) comment;
-				if (pt.isPrint()) {
-					info.addPrintLine(pt.resolvedText);
-				}
-			}
-		}
-		if (gdef.imageInfo != null) {
-			info.imgInfo = Util.sprintf(gdef.imageInfo, gdef.filename, im.xgif, im.ygif);
-		}
-	}
-
-	private void saveImage() throws IOException {
-		if (!gdef.filename.equals("-")) {
-			info.bytes = worker.saveImage(gdef.filename, gdef.imageFormat, gdef.imageQuality);
-		}
-		else {
-			info.bytes = worker.getImageBytes(gdef.imageFormat, gdef.imageQuality);
-		}
-	}
-
-	private void drawOverlay() throws IOException {
-		if (gdef.overlayImage != null) {
-			worker.loadImage(gdef.overlayImage);
-		}
-	}
-
-	private void gator() {
-		if (!gdef.onlyGraph && gdef.showSignature) {
-			Font font = gdef.getFont(FONTTAG_WATERMARK);
-			int x = (int) (im.xgif - 2 - worker.getFontAscent(font));
-			int y = 4;
-			worker.transform(x, y, Math.PI / 2);
-			worker.drawString(signature, 0, 0, font, Color.LIGHT_GRAY);
-			worker.reset();
-		}
-	}
-
-	private void drawRules() {
-		worker.clip(im.xorigin + 1, im.yorigin - gdef.height - 1, gdef.width - 1, gdef.height + 2);
-		for (PlotElement pe : gdef.plotElements) {
-			if (pe instanceof HRule) {
-				HRule hr = (HRule) pe;
-				if (hr.value >= im.minval && hr.value <= im.maxval) {
-					int y = mapper.ytr(hr.value);
-					worker.drawLine(im.xorigin, y, im.xorigin + im.xsize, y, hr.color, new BasicStroke(hr.width));
-				}
-			}
-			else if (pe instanceof VRule) {
-				VRule vr = (VRule) pe;
-				if (vr.timestamp >= im.start && vr.timestamp <= im.end) {
-					int x = mapper.xtr(vr.timestamp);
-					worker.drawLine(x, im.yorigin, x, im.yorigin - im.ysize, vr.color, new BasicStroke(vr.width));
-				}
-			}
-		}
-		worker.reset();
-	}
-
-	private void drawText() {
-		if (!gdef.onlyGraph) {
-			if (gdef.title != null) {
-				int x = im.xgif / 2 - (int) (worker.getStringWidth(gdef.title, gdef.getFont(FONTTAG_TITLE)) / 2);
-				int y = PADDING_TOP + (int) worker.getFontAscent(gdef.getFont(FONTTAG_TITLE));
-				worker.drawString(gdef.title, x, y, gdef.getFont(FONTTAG_TITLE), gdef.colors[COLOR_FONT]);
-			}
-			if (gdef.verticalLabel != null) {
-				int x = PADDING_LEFT;
-				int y = im.yorigin - im.ysize / 2 + (int) worker.getStringWidth(gdef.verticalLabel, gdef.getFont(FONTTAG_UNIT)) / 2;
-				int ascent = (int) worker.getFontAscent(gdef.getFont(FONTTAG_UNIT));
-				worker.transform(x, y, -Math.PI / 2);
-				worker.drawString(gdef.verticalLabel, 0, ascent, gdef.getFont(FONTTAG_UNIT), gdef.colors[COLOR_FONT]);
-				worker.reset();
-			}
-		}
-	}
-
-	private void drawGrid() {
-		if (!gdef.onlyGraph) {
-			Paint shade1 = gdef.colors[COLOR_SHADEA], shade2 = gdef.colors[COLOR_SHADEB];
-			Stroke borderStroke = new BasicStroke(1);
-			worker.drawLine(0, 0, im.xgif - 1, 0, shade1, borderStroke);
-			worker.drawLine(1, 1, im.xgif - 2, 1, shade1, borderStroke);
-			worker.drawLine(0, 0, 0, im.ygif - 1, shade1, borderStroke);
-			worker.drawLine(1, 1, 1, im.ygif - 2, shade1, borderStroke);
-			worker.drawLine(im.xgif - 1, 0, im.xgif - 1, im.ygif - 1, shade2, borderStroke);
-			worker.drawLine(0, im.ygif - 1, im.xgif - 1, im.ygif - 1, shade2, borderStroke);
-			worker.drawLine(im.xgif - 2, 1, im.xgif - 2, im.ygif - 2, shade2, borderStroke);
-			worker.drawLine(1, im.ygif - 2, im.xgif - 2, im.ygif - 2, shade2, borderStroke);
-			if (gdef.drawXGrid) {
-				new TimeAxis(this).draw();
-			}
-			if (gdef.drawYGrid) {
-				boolean ok;
-				if (gdef.altYMrtg) {
-					ok = new ValueAxisMrtg(this).draw();
-				}
-				else if (gdef.logarithmic) {
-					ok = new ValueAxisLogarithmic(this).draw();
-				}
-				else {
-					ok = new ValueAxis(this).draw();
-				}
-				if (!ok) {
-					String msg = "No Data Found";
-					worker.drawString(msg,
-							im.xgif / 2 - (int) worker.getStringWidth(msg, gdef.getFont(FONTTAG_TITLE)) / 2,
-							(2 * im.yorigin - im.ysize) / 2,
-							gdef.getFont(FONTTAG_TITLE), gdef.colors[COLOR_FONT]);
-				}
-			}
-		}
-	}
-
-	private void drawData() throws RrdException {
-		worker.setAntiAliasing(gdef.antiAliasing);
-		worker.clip(im.xorigin + 1, im.yorigin - gdef.height - 1, gdef.width - 1, gdef.height + 2);
-		double areazero = mapper.ytr((im.minval > 0.0) ? im.minval : (im.maxval < 0.0) ? im.maxval : 0.0);
-		double[] x = xtr(dproc.getTimestamps()), lastY = null;
-		// draw line, area and stack
-		for (PlotElement plotElement : gdef.plotElements) {
-			if (plotElement instanceof SourcedPlotElement) {
-				SourcedPlotElement source = (SourcedPlotElement) plotElement;
-				double[] y = ytr(source.getValues());
-				if (source instanceof Line) {
-					worker.drawPolyline(x, y, source.color, new BasicStroke(((Line) source).width));
-				}
-				else if (source instanceof Area) {
-					worker.fillPolygon(x, areazero, y, source.color);
-				}
-				else if (source instanceof Stack) {
-					Stack stack = (Stack) source;
-					float width = stack.getParentLineWidth();
-					if (width >= 0F) {
-						// line
-						worker.drawPolyline(x, y, stack.color, new BasicStroke(width));
-					}
-					else {
-						// area
-						worker.fillPolygon(x, lastY, y, stack.color);
-						worker.drawPolyline(x, lastY, stack.getParentColor(), new BasicStroke(0));
-					}
-				}
-				else {
-					// should not be here
-					throw new RrdException("Unknown plot source: " + source.getClass().getName());
-				}
-				lastY = y;
-			}
-		}
-		worker.reset();
-		worker.setAntiAliasing(false);
-	}
-
-	private void drawAxis() {
-		if (!gdef.onlyGraph) {
-			Paint gridColor = gdef.colors[COLOR_GRID];
-			Paint fontColor = gdef.colors[COLOR_FONT];
-			Paint arrowColor = gdef.colors[COLOR_ARROW];
-			Stroke stroke = new BasicStroke(1);
-			worker.drawLine(im.xorigin + im.xsize, im.yorigin, im.xorigin + im.xsize, im.yorigin - im.ysize,
-					gridColor, stroke);
-			worker.drawLine(im.xorigin, im.yorigin - im.ysize, im.xorigin + im.xsize, im.yorigin - im.ysize,
-					gridColor, stroke);
-			worker.drawLine(im.xorigin - 4, im.yorigin, im.xorigin + im.xsize + 4, im.yorigin,
-					fontColor, stroke);
-			worker.drawLine(im.xorigin, im.yorigin, im.xorigin, im.yorigin - im.ysize,
-					gridColor, stroke);
-			worker.drawLine(im.xorigin + im.xsize + 4, im.yorigin - 3, im.xorigin + im.xsize + 4, im.yorigin + 3,
-					arrowColor, stroke);
-			worker.drawLine(im.xorigin + im.xsize + 4, im.yorigin - 3, im.xorigin + im.xsize + 9, im.yorigin,
-					arrowColor, stroke);
-			worker.drawLine(im.xorigin + im.xsize + 4, im.yorigin + 3, im.xorigin + im.xsize + 9, im.yorigin,
-					arrowColor, stroke);
-		}
-	}
-
-	private void drawBackground() throws IOException {
-		worker.fillRect(0, 0, im.xgif, im.ygif, gdef.colors[COLOR_BACK]);
-		if (gdef.backgroundImage != null) {
-			worker.loadImage(gdef.backgroundImage);
-		}
-		worker.fillRect(im.xorigin, im.yorigin - im.ysize, im.xsize, im.ysize, gdef.colors[COLOR_CANVAS]);
-	}
-
-	private void createImageWorker() {
-		worker.resize(im.xgif, im.ygif);
-	}
-
-	private void placeLegends() {
-		if (!gdef.noLegend && !gdef.onlyGraph) {
-			int border = (int) (getSmallFontCharWidth() * PADDING_LEGEND);
-			LegendComposer lc = new LegendComposer(this, border, im.ygif, im.xgif - 2 * border);
-			im.ygif = lc.placeComments() + PADDING_BOTTOM;
-		}
-	}
-
-	private void initializeLimits() throws RrdException {
-		im.xsize = gdef.width;
-		im.ysize = gdef.height;
-		im.unitslength = gdef.unitsLength;
-		if (gdef.onlyGraph) {
-			if (im.ysize > 64) {
-				throw new RrdException("Cannot create graph only, height too big");
-			}
-			im.xorigin = 0;
-		}
-		else {
-			im.xorigin = (int) (PADDING_LEFT + im.unitslength * getSmallFontCharWidth());
-		}
-		if (gdef.verticalLabel != null) {
-			im.xorigin += getFontHeight(FONTTAG_UNIT);
-		}
-		if (gdef.onlyGraph) {
-			im.yorigin = im.ysize;
-		}
-		else {
-			im.yorigin = PADDING_TOP + im.ysize;
-		}
-		mapper = new Mapper(this);
-		if (gdef.title != null) {
-			im.yorigin += getFontHeight(FONTTAG_TITLE) + PADDING_TITLE;
-		}
-		if (gdef.onlyGraph) {
-			im.xgif = im.xsize;
-			im.ygif = im.yorigin;
-		}
-		else {
-			im.xgif = PADDING_RIGHT + im.xsize + im.xorigin;
-			im.ygif = im.yorigin + (int) (PADDING_PLOT * getFontHeight(FONTTAG_DEFAULT));
-		}
-	}
-
-	private void removeOutOfRangeRules() {
-		for (PlotElement plotElement : gdef.plotElements) {
-			if (plotElement instanceof HRule) {
-				((HRule) plotElement).setLegendVisibility(im.minval, im.maxval, gdef.forceRulesLegend);
-			}
-			else if (plotElement instanceof VRule) {
-				((VRule) plotElement).setLegendVisibility(im.start, im.end, gdef.forceRulesLegend);
-			}
-		}
-	}
-
-	private void expandValueRange() {
-		im.ygridstep = (gdef.valueAxisSetting != null) ? gdef.valueAxisSetting.gridStep : Double.NaN;
-		im.ylabfact = (gdef.valueAxisSetting != null) ? gdef.valueAxisSetting.labelFactor : 0;
-		if (!gdef.rigid && !gdef.logarithmic) {
-			double sensiblevalues[] = {
-					1000.0, 900.0, 800.0, 750.0, 700.0, 600.0, 500.0, 400.0, 300.0, 250.0, 200.0, 125.0, 100.0,
-					90.0, 80.0, 75.0, 70.0, 60.0, 50.0, 40.0, 30.0, 25.0, 20.0, 10.0,
-					9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.5, 3.0, 2.5, 2.0, 1.8, 1.5, 1.2, 1.0,
-					0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, -1
-			};
-			double scaled_min, scaled_max, adj;
-			if (Double.isNaN(im.ygridstep)) {
-				if (gdef.altYMrtg) { /* mrtg */
-					im.decimals = Math.ceil(Math.log10(Math.max(Math.abs(im.maxval), Math.abs(im.minval))));
-					im.quadrant = 0;
-					if (im.minval < 0) {
-						im.quadrant = 2;
-						if (im.maxval <= 0) {
-							im.quadrant = 4;
-						}
-					}
-					switch (im.quadrant) {
-						case 2:
-							im.scaledstep = Math.ceil(50 * Math.pow(10, -(im.decimals)) * Math.max(Math.abs(im.maxval),
-									Math.abs(im.minval))) * Math.pow(10, im.decimals - 2);
-							scaled_min = -2 * im.scaledstep;
-							scaled_max = 2 * im.scaledstep;
-							break;
-						case 4:
-							im.scaledstep = Math.ceil(25 * Math.pow(10,
-									-(im.decimals)) * Math.abs(im.minval)) * Math.pow(10, im.decimals - 2);
-							scaled_min = -4 * im.scaledstep;
-							scaled_max = 0;
-							break;
-						default: /* quadrant 0 */
-							im.scaledstep = Math.ceil(25 * Math.pow(10, -(im.decimals)) * im.maxval) *
-									Math.pow(10, im.decimals - 2);
-							scaled_min = 0;
-							scaled_max = 4 * im.scaledstep;
-							break;
-					}
-					im.minval = scaled_min;
-					im.maxval = scaled_max;
-				}
-				else if (gdef.altAutoscale) {
-					/* measure the amplitude of the function. Make sure that
-					   graph boundaries are slightly higher then max/min vals
-					   so we can see amplitude on the graph */
-					double delt, fact;
-
-					delt = im.maxval - im.minval;
-					adj = delt * 0.1;
-					fact = 2.0 * Math.pow(10.0,
-							Math.floor(Math.log10(Math.max(Math.abs(im.minval), Math.abs(im.maxval)))) - 2);
-					if (delt < fact) {
-						adj = (fact - delt) * 0.55;
-					}
-					im.minval -= adj;
-					im.maxval += adj;
-				}
-				else if (gdef.altAutoscaleMax) {
-					/* measure the amplitude of the function. Make sure that
-					   graph boundaries are slightly higher than max vals
-					   so we can see amplitude on the graph */
-					adj = (im.maxval - im.minval) * 0.1;
-					im.maxval += adj;
-				}
-				else {
-					scaled_min = im.minval / im.magfact;
-					scaled_max = im.maxval / im.magfact;
-					for (int i = 1; sensiblevalues[i] > 0; i++) {
-						if (sensiblevalues[i - 1] >= scaled_min && sensiblevalues[i] <= scaled_min) {
-							im.minval = sensiblevalues[i] * im.magfact;
-						}
-						if (-sensiblevalues[i - 1] <= scaled_min && -sensiblevalues[i] >= scaled_min) {
-							im.minval = -sensiblevalues[i - 1] * im.magfact;
-						}
-						if (sensiblevalues[i - 1] >= scaled_max && sensiblevalues[i] <= scaled_max) {
-							im.maxval = sensiblevalues[i - 1] * im.magfact;
-						}
-						if (-sensiblevalues[i - 1] <= scaled_max && -sensiblevalues[i] >= scaled_max) {
-							im.maxval = -sensiblevalues[i] * im.magfact;
-						}
-					}
-				}
-			}
-			else {
-				im.minval = (double) im.ylabfact * im.ygridstep *
-						Math.floor(im.minval / ((double) im.ylabfact * im.ygridstep));
-				im.maxval = (double) im.ylabfact * im.ygridstep *
-						Math.ceil(im.maxval / ((double) im.ylabfact * im.ygridstep));
-			}
-
-		}
-	}
-
-	private void identifySiUnit() {
-		im.unitsexponent = gdef.unitsExponent;
-		im.base = gdef.base;
-		if (!gdef.logarithmic) {
-			final char symbol[] = {'a', 'f', 'p', 'n', 'u', 'm', ' ', 'k', 'M', 'G', 'T', 'P', 'E'};
-			int symbcenter = 6;
-			double digits;
-			if (im.unitsexponent != Integer.MAX_VALUE) {
-				digits = Math.floor(im.unitsexponent / 3.0);
-			}
-			else {
-				digits = Math.floor(Math.log(Math.max(Math.abs(im.minval), Math.abs(im.maxval))) / Math.log(im.base));
-			}
-			im.magfact = Math.pow(im.base, digits);
-			if (((digits + symbcenter) < symbol.length) && ((digits + symbcenter) >= 0)) {
-				im.symbol = symbol[(int) digits + symbcenter];
-			}
-			else {
-				im.symbol = '?';
-			}
-		}
-	}
-
-	private void findMinMaxValues() {
-		double minval = Double.NaN, maxval = Double.NaN;
-		for (PlotElement pe : gdef.plotElements) {
-			if (pe instanceof SourcedPlotElement) {
-				minval = Util.min(((SourcedPlotElement) pe).getMinValue(), minval);
-				maxval = Util.max(((SourcedPlotElement) pe).getMaxValue(), maxval);
-			}
-		}
-		if (Double.isNaN(minval)) {
-			minval = 0D;
-		}
-		if (Double.isNaN(maxval)) {
-			maxval = 1D;
-		}
-		im.minval = gdef.minValue;
-		im.maxval = gdef.maxValue;
-		/* adjust min and max values */
-		if (Double.isNaN(im.minval) || ((!gdef.logarithmic && !gdef.rigid) && im.minval > minval)) {
-			im.minval = minval;
-		}
-		if (Double.isNaN(im.maxval) || (!gdef.rigid && im.maxval < maxval)) {
-			if (gdef.logarithmic) {
-				im.maxval = maxval * 1.1;
-			}
-			else {
-				im.maxval = maxval;
-			}
-		}
-		/* make sure min is smaller than max */
-		if (im.minval > im.maxval) {
-			im.minval = 0.99 * im.maxval;
-		}
-		/* make sure min and max are not equal */
-		if (Math.abs(im.minval - im.maxval) < .0000001) {
-			im.maxval *= 1.01;
-			if (!gdef.logarithmic) {
-				im.minval *= 0.99;
-			}
-			/* make sure min and max are not both zero */
-			if (im.maxval == 0.0) {
-				im.maxval = 1.0;
-			}
-		}
-	}
-
-	private void calculatePlotValues() throws RrdException {
-		for (PlotElement pe : gdef.plotElements) {
-			if (pe instanceof SourcedPlotElement) {
-				((SourcedPlotElement) pe).assignValues(dproc);
-			}
-		}
-	}
-
-	private void resolveTextElements() throws RrdException {
-		ValueScaler valueScaler = new ValueScaler(gdef.base);
-		for (CommentText comment : gdef.comments) {
-			comment.resolveText(dproc, valueScaler);
-		}
-	}
-
-	private void fetchData() throws RrdException, IOException {
-		dproc = new DataProcessor(gdef.startTime, gdef.endTime);
-		dproc.setPoolUsed(gdef.poolUsed);
-		if (gdef.step > 0) {
-			dproc.setStep(gdef.step);
-		}
-		for (Source src : gdef.sources) {
-			src.requestData(dproc);
-		}
-		dproc.processData();
-		//long[] t = dproc.getTimestamps();
-		//im.start = t[0];
-		//im.end = t[t.length - 1];
-		im.start = gdef.startTime;
-		im.end = gdef.endTime;
-	}
-
-	private boolean lazyCheck() {
-		// redraw if lazy option is not set or file does not exist
-		if (!gdef.lazy || !Util.fileExists(gdef.filename)) {
-			return false; // 'false' means 'redraw'
-		}
-		// redraw if not enough time has passed
-		long secPerPixel = (gdef.endTime - gdef.startTime) / gdef.width;
-		long elapsed = Util.getTimestamp() - Util.getLastModified(gdef.filename);
-		return elapsed <= secPerPixel;
-	}
-
-	private void drawLegend() {
-		if (!gdef.onlyGraph && !gdef.noLegend) {
-			int ascent = (int) worker.getFontAscent(gdef.getFont(FONTTAG_LEGEND));
-			int box = (int) getBox(), boxSpace = (int) (getBoxSpace());
-			for (CommentText c : gdef.comments) {
-				if (c.isValidGraphElement()) {
-					int x = c.x, y = c.y + ascent;
-					if (c instanceof LegendText) {
-						// draw with BOX
-						worker.fillRect(x, y - box, box, box, gdef.colors[COLOR_FRAME]);
-						worker.fillRect(x + 1, y - box + 1, box - 2, box - 2, gdef.colors[COLOR_CANVAS]);
-						worker.fillRect(x + 1, y - box + 1, box - 2, box - 2, ((LegendText) c).legendColor);
-						worker.drawString(c.resolvedText, x + boxSpace, y, gdef.getFont(FONTTAG_LEGEND), gdef.colors[COLOR_FONT]);
-					}
-					else {
-						worker.drawString(c.resolvedText, x, y, gdef.getFont(FONTTAG_LEGEND), gdef.colors[COLOR_FONT]);
-					}
-				}
-			}
-		}
-	}
-
-	// helper methods
-
-	double getFontHeight(int fonttag) {
-		return worker.getFontHeight(gdef.getFont(fonttag));
-	}
-
-	double getFontCharWidth(int fonttag) {
-		return worker.getStringWidth("a", gdef.getFont(fonttag));
-	}
-
-	double getSmallFontHeight() {
-		return getFontHeight(FONTTAG_LEGEND);
-	}
-
-	@SuppressWarnings("unused")
-	private double getTitleFontHeight() {
-		return getFontHeight(FONTTAG_TITLE);
-	}
-
-	private double getSmallFontCharWidth() {
-		return getFontCharWidth(FONTTAG_LEGEND);
-	}
-
-	double getInterlegendSpace() {
-		return getFontCharWidth(FONTTAG_LEGEND) * LEGEND_INTERSPACING;
-	}
-
-	double getLeading() {
-		return getFontHeight(FONTTAG_LEGEND) * LEGEND_LEADING;
-	}
-
-	double getSmallLeading() {
-		return getFontHeight(FONTTAG_LEGEND) * LEGEND_LEADING_SMALL;
-	}
-
-	double getBoxSpace() {
-		return Math.ceil(getFontHeight(FONTTAG_LEGEND) * LEGEND_BOX_SPACE);
-	}
-
-	private double getBox() {
-		return getFontHeight(FONTTAG_LEGEND) * LEGEND_BOX;
-	}
-
-	double[] xtr(long[] timestamps) {
-		/*
-		double[] timestampsDev = new double[timestamps.length];
-		for (int i = 0; i < timestamps.length; i++) {
-			timestampsDev[i] = mapper.xtr(timestamps[i]);
-		}
-		return timestampsDev;
-		*/
-		double[] timestampsDev = new double[2 * timestamps.length - 1];
-		for (int i = 0, j = 0; i < timestamps.length; i += 1, j += 2) {
-			timestampsDev[j] = mapper.xtr(timestamps[i]);
-			if (i < timestamps.length - 1) {
-				timestampsDev[j + 1] = timestampsDev[j];
-			}
-		}
-		return timestampsDev;
-	}
-
-	double[] ytr(double[] values) {
-		/*
-		double[] valuesDev = new double[values.length];
-		for (int i = 0; i < values.length; i++) {
-			if (Double.isNaN(values[i])) {
-				valuesDev[i] = Double.NaN;
-			}
-			else {
-				valuesDev[i] = mapper.ytr(values[i]);
-			}
-		}
-		return valuesDev;
-		*/
-		double[] valuesDev = new double[2 * values.length - 1];
-		for (int i = 0, j = 0; i < values.length; i += 1, j += 2) {
-			if (Double.isNaN(values[i])) {
-				valuesDev[j] = Double.NaN;
-			}
-			else {
-				valuesDev[j] = mapper.ytr(values[i]);
-			}
-			if (j > 0) {
-				valuesDev[j - 1] = valuesDev[j];
-			}
-		}
-		return valuesDev;
-	}
-
-	/**
-	 * Renders this graph onto graphing device
-	 *
-	 * @param g Graphics handle
-	 */
-	public void render(Graphics g) {
-		byte[] imageData = getRrdGraphInfo().getBytes();
-		ImageIcon image = new ImageIcon(imageData);
-		image.paintIcon(null, g, 0, 0);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/RrdGraphConstants.java b/apps/jrobin/java/src/org/jrobin/graph/RrdGraphConstants.java
deleted file mode 100644
index 87e6fafed3a13ad0b5382664f377ea40c399df29..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/RrdGraphConstants.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-import java.util.Calendar;
-import java.util.Locale;
-
-/**
- * Class to represent various constants used for graphing. No methods are specified.
- */
-public interface RrdGraphConstants {
-	/**
-	 * Default graph starting time
-	 */
-	String DEFAULT_START = "end-1d";
-	/**
-	 * Default graph ending time
-	 */
-	String DEFAULT_END = "now";
-
-	/**
-	 * Constant to represent second
-	 */
-	int SECOND = Calendar.SECOND;
-	/**
-	 * Constant to represent minute
-	 */
-	int MINUTE = Calendar.MINUTE;
-	/**
-	 * Constant to represent hour
-	 */
-	int HOUR = Calendar.HOUR_OF_DAY;
-	/**
-	 * Constant to represent day
-	 */
-	int DAY = Calendar.DAY_OF_MONTH;
-	/**
-	 * Constant to represent week
-	 */
-	int WEEK = Calendar.WEEK_OF_YEAR;
-	/**
-	 * Constant to represent month
-	 */
-	int MONTH = Calendar.MONTH;
-	/**
-	 * Constant to represent year
-	 */
-	int YEAR = Calendar.YEAR;
-
-	/**
-	 * Constant to represent Monday
-	 */
-	int MONDAY = Calendar.MONDAY;
-	/**
-	 * Constant to represent Tuesday
-	 */
-	int TUESDAY = Calendar.TUESDAY;
-	/**
-	 * Constant to represent Wednesday
-	 */
-	int WEDNESDAY = Calendar.WEDNESDAY;
-	/**
-	 * Constant to represent Thursday
-	 */
-	int THURSDAY = Calendar.THURSDAY;
-	/**
-	 * Constant to represent Friday
-	 */
-	int FRIDAY = Calendar.FRIDAY;
-	/**
-	 * Constant to represent Saturday
-	 */
-	int SATURDAY = Calendar.SATURDAY;
-	/**
-	 * Constant to represent Sunday
-	 */
-	int SUNDAY = Calendar.SUNDAY;
-
-	/**
-	 * Index of the canvas color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_CANVAS = 0;
-	/**
-	 * Index of the background color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_BACK = 1;
-	/**
-	 * Index of the top-left graph shade color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_SHADEA = 2;
-	/**
-	 * Index of the bottom-right graph shade color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_SHADEB = 3;
-	/**
-	 * Index of the minor grid color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_GRID = 4;
-	/**
-	 * Index of the major grid color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_MGRID = 5;
-	/**
-	 * Index of the font color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_FONT = 6;
-	/**
-	 * Index of the frame color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_FRAME = 7;
-	/**
-	 * Index of the arrow color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
-	 */
-	int COLOR_ARROW = 8;
-
-	/**
-	 * Allowed color names which can be used in {@link RrdGraphDef#setColor(String, java.awt.Paint)} method
-	 */
-	String[] COLOR_NAMES = {
-			"canvas", "back", "shadea", "shadeb", "grid", "mgrid", "font", "frame", "arrow"
-	};
-
-	/**
-	 * Default first day of the week (obtained from the default locale)
-	 */
-	int FIRST_DAY_OF_WEEK = Calendar.getInstance(Locale.getDefault()).getFirstDayOfWeek();
-
-	/**
-	 * Default graph canvas color
-	 */
-	Color DEFAULT_CANVAS_COLOR = Color.WHITE;
-	/**
-	 * Default graph background color
-	 */
-	Color DEFAULT_BACK_COLOR = new Color(245, 245, 245);
-	/**
-	 * Default top-left graph shade color
-	 */
-	Color DEFAULT_SHADEA_COLOR = new Color(200, 200, 200);
-	/**
-	 * Default bottom-right graph shade color
-	 */
-	Color DEFAULT_SHADEB_COLOR = new Color(150, 150, 150);
-	/**
-	 * Default minor grid color
-	 */
-	Color DEFAULT_GRID_COLOR = new Color(171, 171, 171, 95);
-	// Color DEFAULT_GRID_COLOR = new Color(140, 140, 140);
-	/**
-	 * Default major grid color
-	 */
-	Color DEFAULT_MGRID_COLOR = new Color(255, 91, 91, 95);
-	// Color DEFAULT_MGRID_COLOR = new Color(130, 30, 30);
-	/**
-	 * Default font color
-	 */
-	Color DEFAULT_FONT_COLOR = Color.BLACK;
-	/**
-	 * Default frame color
-	 */
-	Color DEFAULT_FRAME_COLOR = Color.BLACK;
-	/**
-	 * Default arrow color
-	 */
-	Color DEFAULT_ARROW_COLOR = Color.RED;
-
-	/**
-	 * Constant to represent left alignment marker
-	 */
-	String ALIGN_LEFT_MARKER = "\\l";
-	/**
-	 * Constant to represent centered alignment marker
-	 */
-	String ALIGN_CENTER_MARKER = "\\c";
-	/**
-	 * Constant to represent right alignment marker
-	 */
-	String ALIGN_RIGHT_MARKER = "\\r";
-	/**
-	 * Constant to represent justified alignment marker
-	 */
-	String ALIGN_JUSTIFIED_MARKER = "\\j";
-	/**
-	 * Constant to represent "glue" marker
-	 */
-	String GLUE_MARKER = "\\g";
-	/**
-	 * Constant to represent vertical spacing marker
-	 */
-	String VERTICAL_SPACING_MARKER = "\\s";
-	/**
-	 * Constant to represent no justification markers
-	 */
-	String NO_JUSTIFICATION_MARKER = "\\J";
-	/**
-	 * Used internally
-	 */
-	String[] MARKERS = {
-			ALIGN_LEFT_MARKER, ALIGN_CENTER_MARKER, ALIGN_RIGHT_MARKER,
-			ALIGN_JUSTIFIED_MARKER, GLUE_MARKER, VERTICAL_SPACING_MARKER, NO_JUSTIFICATION_MARKER
-	};
-
-	/**
-	 * Constant to represent in-memory image name
-	 */
-	String IN_MEMORY_IMAGE = "-";
-
-	/**
-	 * Default units length
-	 */
-	int DEFAULT_UNITS_LENGTH = 9;
-	/**
-	 * Default graph width
-	 */
-	int DEFAULT_WIDTH = 400;
-	/**
-	 * Default graph height
-	 */
-	int DEFAULT_HEIGHT = 100;
-	/**
-	 * Default image format
-	 */
-	String DEFAULT_IMAGE_FORMAT = "gif";
-	/**
-	 * Default image quality, used only for jpeg graphs
-	 */
-	float DEFAULT_IMAGE_QUALITY = 0.8F; // only for jpegs, not used for png/gif
-	/**
-	 * Default value base
-	 */
-	double DEFAULT_BASE = 1000;
-
-	/**
-	 * Default font name, determined based on the current operating system
-	 */
-	String DEFAULT_FONT_NAME = System.getProperty("os.name").toLowerCase().contains("windows") ?
-			"Lucida Sans Typewriter" : "Monospaced";
-
-	/**
-	 * Default graph small font
-	 */
-	String DEFAULT_MONOSPACE_FONT_FILE = "DejaVuSansMono.ttf";
-
-	/**
-	 * Default graph large font
-	 */
-	String DEFAULT_PROPORTIONAL_FONT_FILE = "DejaVuSans-Bold.ttf";
-
-	/**
-	 * Used internally
-	 */
-	double LEGEND_LEADING = 1.2; // chars
-	/**
-	 * Used internally
-	 */
-	double LEGEND_LEADING_SMALL = 0.7; // chars
-	/**
-	 * Used internally
-	 */
-	double LEGEND_BOX_SPACE = 1.2; // chars
-	/**
-	 * Used internally
-	 */
-	double LEGEND_BOX = 0.7; // chars
-	/**
-	 * Used internally
-	 */
-	int LEGEND_INTERSPACING = 2; // chars
-	/**
-	 * Used internally
-	 */
-	int PADDING_LEFT = 0; // pix - absent vertical label provides padding here
-	/**
-	 * Used internally
-	 */
-	int PADDING_TOP = 5; // pix -- additional top pixels added by frame border
-	/**
-	 * Used internally
-	 */
-	int PADDING_TITLE = 7; // pix
-	/**
-	 * Used internally
-	 */
-	int PADDING_RIGHT = 20; // pix
-	/**
-	 * Used internally
-	 */
-	double PADDING_PLOT = 1.7; //chars
-	/**
-	 * Used internally
-	 */
-	double PADDING_LEGEND = 2.1; // chars
-	/**
-	 * Used internally
-	 */
-	int PADDING_BOTTOM = 6; // pix
-	/**
-	 * Used internally
-	 */
-	int PADDING_VLABEL = 8; // pix
-
-	/**
-	 * Stroke used to draw grid
-	 */
-	// solid line
-	//Stroke GRID_STROKE = new BasicStroke(1);
-
-	// dotted line
-	 Stroke GRID_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, new float[] {1, 1}, 0);
-	/**
-	 * Stroke used to draw ticks
-	 */
-	Stroke TICK_STROKE = new BasicStroke(1);
-
-	/**
-	 * Index of the default font. Used in {@link RrdGraphDef#setFont(int, java.awt.Font)}
-	 */
-	int FONTTAG_DEFAULT   = 0;
-
-	/**
-	 * Index of the title font. Used in {@link RrdGraphDef#setFont(int, java.awt.Font)}
-	 */
-	int FONTTAG_TITLE     = 1;
-
-	/**
-	 * Index of the axis label font. Used in {@link RrdGraphDef#setFont(int, java.awt.Font)}
-	 */
-	int FONTTAG_AXIS      = 2;
-
-	/**
-	 * Index of the vertical unit label font. Used in {@link RrdGraphDef#setFont(int, java.awt.Font)}
-	 */
-	int FONTTAG_UNIT      = 3;
-
-	/**
-	 * Index of the graph legend font. Used in {@link RrdGraphDef#setFont(int, java.awt.Font)}
-	 */
-	int FONTTAG_LEGEND    = 4;
-
-	/**
-	 * Index of the edge watermark font. Used in {@link RrdGraphDef#setFont(int, java.awt.Font)}
-	 */
-	int FONTTAG_WATERMARK = 5;
-
-	/**
-	 * Allowed font tag names which can be used in {@link RrdGraphDef#setFont(String, java.awt.Font)} method
-	 */
-	String[] FONTTAG_NAMES = {
-		"DEFAULT", "TITLE", "AXIS", "UNIT", "LEGEND", "WATERMARK"
-	};
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/RrdGraphDef.java b/apps/jrobin/java/src/org/jrobin/graph/RrdGraphDef.java
deleted file mode 100644
index 6f4e966a395d4c99534e14885c1efaf37a55b66f..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/RrdGraphDef.java
+++ /dev/null
@@ -1,1223 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-import org.jrobin.data.Plottable;
-
-import java.awt.*;
-import java.io.File;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class which should be used to define new JRobin graph. Once constructed and populated with data
- * object of this class should be passed to the constructor of the {@link RrdGraph} class which
- * will actually create the graph.
- * <p>
- * The text printed below the actual graph can be formated by appending
- * special escaped characters at the end of a text. When ever such a
- * character occurs, all pending text is pushed onto the graph according to
- * the character specified.
- * <p>
- * Valid markers are: \j for justified, \l for left aligned, \r for right
- * aligned and \c for centered.
- * <p>
- * Normally there are two space characters inserted between every two
- * items printed into the graph. The space following a string can be
- * suppressed by putting a \g at the end of the string. The \g also squashes
- * any space inside the string if it is at the very end of the string.
- * This can be used in connection with %s to suppress empty unit strings.
- * <p>
- * A special case is COMMENT:\s this inserts some additional vertical
- * space before placing the next row of legends.
- * <p>
- * When text has to be formated without special instructions from your
- * side, RRDTool will automatically justify the text as soon as one string
- * goes over the right edge. If you want to prevent the justification
- * without forcing a newline, you can use the special tag \J at the end of
- * the string to disable the auto justification.
- */
-public class RrdGraphDef implements RrdGraphConstants {
-    boolean poolUsed = false; // ok
-    boolean antiAliasing = false; // ok
-    String filename = RrdGraphConstants.IN_MEMORY_IMAGE; // ok
-    long startTime, endTime; // ok
-    TimeAxisSetting timeAxisSetting = null; // ok
-    ValueAxisSetting valueAxisSetting = null; // ok
-    boolean altYGrid = false; // ok
-    boolean noMinorGrid = false; // ok
-    boolean altYMrtg = false; // ok
-    boolean altAutoscale = false; // ok
-    boolean altAutoscaleMax = false; // ok
-    int unitsExponent = Integer.MAX_VALUE; // ok
-    int unitsLength = DEFAULT_UNITS_LENGTH; // ok
-    String verticalLabel = null; // ok
-    int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT; // ok
-    boolean interlaced = false; // ok
-    String imageInfo = null; // ok
-    String imageFormat = DEFAULT_IMAGE_FORMAT; // ok
-    float imageQuality = DEFAULT_IMAGE_QUALITY; // ok
-    String backgroundImage = null; // ok
-    String overlayImage = null; // ok
-    String unit = null; // ok
-    String signature = "Created with JRobin"; // ok
-    boolean lazy = false; // ok
-    double minValue = Double.NaN; // ok
-    double maxValue = Double.NaN; // ok
-    boolean rigid = false; // ok
-    double base = DEFAULT_BASE;  // ok
-    boolean logarithmic = false; // ok
-    Paint[] colors = new Paint[]{
-            // ok
-            DEFAULT_CANVAS_COLOR,
-            DEFAULT_BACK_COLOR,
-            DEFAULT_SHADEA_COLOR,
-            DEFAULT_SHADEB_COLOR,
-            DEFAULT_GRID_COLOR,
-            DEFAULT_MGRID_COLOR,
-            DEFAULT_FONT_COLOR,
-            DEFAULT_FRAME_COLOR,
-            DEFAULT_ARROW_COLOR
-    };
-    boolean noLegend = false; // ok
-    boolean onlyGraph = false; // ok
-    boolean forceRulesLegend = false; // ok
-    String title = null; // ok
-    long step = 0; // ok
-    Font[] fonts = new Font[FONTTAG_NAMES.length];
-    boolean drawXGrid = true; // ok
-    boolean drawYGrid = true; // ok
-    int firstDayOfWeek = FIRST_DAY_OF_WEEK; // ok
-    boolean showSignature = true;
-    File fontDir = null;
-
-    List<Source> sources = new ArrayList<Source>();
-    List<CommentText> comments = new ArrayList<CommentText>();
-    List<PlotElement> plotElements = new ArrayList<PlotElement>();
-
-    /**
-     * Creates RrdGraphDef object and sets default time span (default ending time is 'now',
-     * default starting time is 'end-1day'.
-     */
-    public RrdGraphDef() {
-        try {
-            setTimeSpan(Util.getTimestamps(DEFAULT_START, DEFAULT_END));
-        } catch (RrdException e) {
-            throw new RuntimeException(e);
-        }
-
-        String fontdirProperty = System.getProperty("jrobin.fontdir");
-        if (fontdirProperty != null && fontdirProperty.length() != 0) {
-            fontDir = new File(fontdirProperty);
-        }
-
-        fonts[FONTTAG_DEFAULT]   = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 8);
-        fonts[FONTTAG_TITLE]     = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 9);
-        fonts[FONTTAG_AXIS]      = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 7);
-        fonts[FONTTAG_UNIT]      = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 8);
-        fonts[FONTTAG_LEGEND]    = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 8);
-        fonts[FONTTAG_WATERMARK] = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 1).deriveFont(5.5F);
-    }
-
-    protected Font getFontFromResourceName(String name) {
-        Font font = null;
-        Exception exception = null;
-        URL file = null;
-
-        if (fontDir != null) {
-            try {
-                file = new URL("file://" + new File(fontDir, name).getAbsolutePath());
-            } catch (MalformedURLException e) {
-                // fall through to the jar
-                exception = e;
-            }
-        }
-        if (file == null) {
-            file = this.getClass().getResource(name);
-        }
-
-        if (file != null) {
-            // System.err.println("Found a font URL: " + file.toExternalForm());
-            try {
-                InputStream fontStream = file.openStream();
-                font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
-                fontStream.close();
-            } catch (Exception e) {
-                exception = e;
-            }
-        }
-        else {
-            // we can't find our fonts, fall back to the system font
-            System.err.println("An error occurred loading the font '" + name + "'.  Falling back to the default.");
-            if (exception != null) {
-                System.err.println(exception.getLocalizedMessage());
-            }
-            font = new Font(DEFAULT_FONT_NAME, Font.PLAIN, 10);
-        }
-
-        if (font == null) {
-            font = new Font(null, Font.PLAIN, 10);
-        }
-        return font;
-    }
-
-    /**
-     * Sets the signature string that runs along the right-side of the graph.
-     * Defaults to "Created with JRobin".
-     *
-     * @param signature the string to print
-     */
-    public void setSignature(String signature) {
-        this.signature = signature;
-    }
-
-    /**
-     * Gets the signature string that runs along the right-side of the graph.
-     *
-     * @return the signature string
-     */
-    public String getSignature() {
-        return this.signature;
-    }
-
-    /**
-     * Sets the time when the graph should begin. Time in seconds since epoch
-     * (1970-01-01) is required. Negative numbers are relative to the current time.
-     *
-     * @param time Starting time for the graph in seconds since epoch
-     */
-    public void setStartTime(long time) {
-        this.startTime = time;
-        if (time <= 0) {
-            this.startTime += Util.getTime();
-        }
-    }
-
-    /**
-     * Sets the time when the graph should end. Time in seconds since epoch
-     * (1970-01-01) is required. Negative numbers are relative to the current time.
-     *
-     * @param time Ending time for the graph in seconds since epoch
-     */
-    public void setEndTime(long time) {
-        this.endTime = time;
-        if (time <= 0) {
-            this.endTime += Util.getTime();
-        }
-    }
-
-    /**
-     * Sets starting and ending time for the for the graph. Timestamps in seconds since epoch are
-     * required. Negative numbers are relative to the current time.
-     *
-     * @param startTime Starting time in seconds since epoch
-     * @param endTime   Ending time in seconds since epoch
-     */
-    public void setTimeSpan(long startTime, long endTime) {
-        setStartTime(startTime);
-        setEndTime(endTime);
-    }
-
-    /**
-     * Sets starting and ending time for the for the graph. Timestamps in seconds since epoch are
-     * required.
-     *
-     * @param timestamps Array of timestamps. The first array item will be chosen for the starting
-     *                   timestamp. The last array item will be chosen for the ending timestamp.
-     */
-    public void setTimeSpan(long[] timestamps) {
-        setTimeSpan(timestamps[0], timestamps[timestamps.length - 1]);
-    }
-
-    /**
-     * Sets RrdDbPool usage policy (defaults to true). If set to true,
-     * {@link org.jrobin.core.RrdDbPool RrdDbPool} will be used to
-     * access individual RRD files. If set to false, RRD files will be accessed directly.
-     *
-     * @param poolUsed true, if RrdDbPool class should be used. False otherwise.
-     */
-    public void setPoolUsed(boolean poolUsed) {
-        this.poolUsed = poolUsed;
-    }
-
-    /**
-     * Sets the name of the graph to generate. Since JRobin outputs GIFs, PNGs,
-     * and JPEGs it's recommended that the filename end in either .gif,
-     * .png or .jpg. JRobin does not enforce this, however. If the filename is
-     * set to '-' the image will be created only in memory (no file will be created).
-     * PNG and GIF formats are recommended but JPEGs should be avoided.
-     *
-     * @param filename Path to the image file
-     */
-    public void setFilename(String filename) {
-        this.filename = filename;
-    }
-
-    /**
-     * Configures x-axis grid and labels. The x-axis label is quite complex to configure.
-     * So if you don't have very special needs, you can rely on the autoconfiguration to
-     * get this right.
-     * <p>
-     * Otherwise, you have to configure three elements making up the x-axis labels
-     * and grid. The base grid, the major grid and the labels.
-     * The configuration is based on the idea that you first specify a well
-     * known amount of time and then say how many times
-     * it has to pass between each minor/major grid line or label. For the label
-     * you have to define two additional items: The precision of the label
-     * in seconds and the format used to generate the text
-     * of the label.
-     * <p>
-     * For example, if you wanted a graph with a base grid every 10 minutes and a major
-     * one every hour, with labels every hour you would use the following
-     * x-axis definition.
-     * <p>
-     * <pre>
-     * setTimeAxis(RrdGraphConstants.MINUTE, 10,
-     *             RrdGraphConstants.HOUR, 1,
-     *             RrdGraphConstants.HOUR, 1,
-     *             0, "%H:%M")
-     * </pre>
-     * <p>
-     * The precision in this example is 0 because the %X format is exact.
-     * If the label was the name of the day, we would have had a precision
-     * of 24 hours, because when you say something like 'Monday' you mean
-     * the whole day and not Monday morning 00:00. Thus the label should
-     * be positioned at noon. By defining a precision of 24 hours or
-     * rather 86400 seconds, you make sure that this happens.
-     *
-     * @param minorUnit        Minor grid unit. Minor grid, major grid and label units
-     *                         can be one of the following constants defined in
-     *                         {@link RrdGraphConstants}: {@link RrdGraphConstants#SECOND SECOND},
-     *                         {@link RrdGraphConstants#MINUTE MINUTE}, {@link RrdGraphConstants#HOUR HOUR},
-     *                         {@link RrdGraphConstants#DAY DAY}, {@link RrdGraphConstants#WEEK WEEK},
-     *                         {@link RrdGraphConstants#MONTH MONTH}, {@link RrdGraphConstants#YEAR YEAR}.
-     * @param minorUnitCount   Number of minor grid units between minor grid lines.
-     * @param majorUnit        Major grid unit.
-     * @param majorUnitCount   Number of major grid units between major grid lines.
-     * @param labelUnit        Label unit.
-     * @param labelUnitCount   Number of label units between labels.
-     * @param labelSpan        Label precision
-     * @param simpleDateFormat Date format (SimpleDateFormat pattern of strftime-like pattern)
-     */
-    public void setTimeAxis(int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
-                            int labelUnit, int labelUnitCount, int labelSpan, String simpleDateFormat) {
-        timeAxisSetting = new TimeAxisSetting(minorUnit, minorUnitCount, majorUnit, majorUnitCount,
-                labelUnit, labelUnitCount, labelSpan, simpleDateFormat);
-    }
-
-    /**
-     * Sets vertical axis grid and labels. Makes vertical grid lines appear
-     * at gridStep interval. Every labelFactor*gridStep, a major grid line is printed,
-     * along with label showing the value of the grid line.
-     *
-     * @param gridStep    Minor grid step
-     * @param labelFactor Specifies how many minor minor grid steps will appear between labels
-     *                    (major grid lines)
-     */
-    public void setValueAxis(double gridStep, int labelFactor) {
-        valueAxisSetting = new ValueAxisSetting(gridStep, labelFactor);
-    }
-
-    /**
-     * Places Y grid dynamically based on graph Y range. Algorithm ensures
-     * that you always have grid, that there are enough but not too many
-     * grid lines and the grid is metric. That is grid lines are placed
-     * every 1, 2, 5 or 10 units.
-     *
-     * @param altYGrid true, if Y grid should be calculated dynamically (defaults to false)
-     */
-    public void setAltYGrid(boolean altYGrid) {
-        this.altYGrid = altYGrid;
-    }
-
-    /**
-     * Use this method to turn off minor grid lines (printed by default)
-     *
-     * @param noMinorGrid true, to turn off, false to turn on (default)
-     */
-    public void setNoMinorGrid(boolean noMinorGrid) {
-        this.noMinorGrid = noMinorGrid;
-    }
-
-    /**
-     * Use this method to request MRTG-like graph (false by default)
-     *
-     * @param altYMrtg true, to create MRTG-like graph, false otherwise (default)
-     */
-    public void setAltYMrtg(boolean altYMrtg) {
-        this.altYMrtg = altYMrtg;
-    }
-
-    /**
-     * Computes Y range based on function absolute minimum and maximum
-     * values. Default algorithm uses predefined set of ranges.  This is
-     * good in many cases but it fails miserably when you need to graph
-     * something like 260 + 0.001 * sin(x). Default algorithm will use Y
-     * range from 250 to 300 and on the graph you will see almost straight
-     * line. With --alt-autoscale Y range will be from slightly less the
-     * 260 - 0.001 to slightly more then 260 + 0.001 and periodic behavior
-     * will be seen.
-     *
-     * @param altAutoscale true to request alternative autoscaling, false otherwise
-     *                     (default).
-     */
-    public void setAltAutoscale(boolean altAutoscale) {
-        this.altAutoscale = altAutoscale;
-    }
-
-    /**
-     * Computes Y range based on function absolute minimum and maximum
-     * values. Where setAltAutoscale(true) will modify both the absolute maximum AND
-     * minimum values, this option will only affect the maximum value. The
-     * minimum value, if not defined elsewhere, will be 0. This
-     * option can be useful when graphing router traffic when the WAN line
-     * uses compression, and thus the throughput may be higher than the
-     * WAN line speed.
-     *
-     * @param altAutoscaleMax true to request alternative autoscaling, false
-     *                        otherwise (default)
-     */
-    public void setAltAutoscaleMax(boolean altAutoscaleMax) {
-        this.altAutoscaleMax = altAutoscaleMax;
-    }
-
-    /**
-     * Sets the 10**unitsExponent scaling of the y-axis values. Normally
-     * values will be scaled to the appropriate units (k, M, etc.). However
-     * you may wish to display units always in k (Kilo, 10e3) even if
-     * the data is in the M (Mega, 10e6) range for instance.  Value should
-     * be an integer which is a multiple of 3 between -18 and 18, inclu-
-     * sive. It is the exponent on the units you which to use.  For example,
-     * use 3 to display the y-axis values in k (Kilo, 10e3, thou-
-     * sands), use -6 to display the y-axis values in u (Micro, 10e-6,
-     * millionths). Use a value of 0 to prevent any scaling of the y-axis
-     * values.
-     *
-     * @param unitsExponent the 10**unitsExponent value for scaling y-axis values.
-     */
-    public void setUnitsExponent(int unitsExponent) {
-        this.unitsExponent = unitsExponent;
-    }
-
-    /**
-     * Sets the character width on the left side of the graph for
-     * y-axis values.
-     *
-     * @param unitsLength Number of characters on the left side of the graphs
-     *                    reserved for vertical axis labels.
-     */
-    public void setUnitsLength(int unitsLength) {
-        this.unitsLength = unitsLength;
-    }
-
-    /**
-     * Sets vertical label on the left side of the graph. This is normally used
-     * to specify the units used.
-     *
-     * @param verticalLabel Vertical axis label
-     */
-    public void setVerticalLabel(String verticalLabel) {
-        this.verticalLabel = verticalLabel;
-    }
-
-    /**
-     * Sets width of the drawing area within the graph. This affects the total
-     * size of the image.
-     *
-     * @param width Width of the drawing area.
-     */
-    public void setWidth(int width) {
-        this.width = width;
-    }
-
-    /**
-     * Sets height of the drawing area within the graph. This affects the total
-     * size of the image.
-     *
-     * @param height Height of the drawing area.
-     */
-    public void setHeight(int height) {
-        this.height = height;
-    }
-
-    /**
-     * Creates interlaced GIF image (currently not supported,
-     * method is present only for RRDTool comaptibility).
-     *
-     * @param interlaced true, if GIF image should be interlaced.
-     */
-    public void setInterlaced(boolean interlaced) {
-        this.interlaced = interlaced;
-    }
-
-    /**
-     * Creates additional image information.
-     * After the image has been created, the graph function uses imageInfo
-     * format string (printf-like) to create output similar to
-     * the {@link #print(String, String, String)} function.
-     * The format string is supplied with the following parameters:
-     * filename, xsize and ysize (in that particular order).
-     * <p>
-     * For example, in order to generate an IMG tag
-     * suitable for including the graph into a web page, the command
-     * would look like this:
-     * <pre>
-     * setImageInfo(&quot;&lt;IMG SRC='/img/%s' WIDTH='%d' HEIGHT='%d' ALT='Demo'&gt;&quot;);
-     * </pre>
-     *
-     * @param imageInfo Image info format. Use %s placeholder for filename, %d placeholder for
-     *                  image width and height.
-     */
-    public void setImageInfo(String imageInfo) {
-        this.imageInfo = imageInfo;
-    }
-
-    /**
-     * Sets image format.
-     *
-     * @param imageFormat "PNG", "GIF" or "JPG".
-     */
-    public void setImageFormat(String imageFormat) {
-        this.imageFormat = imageFormat;
-    }
-
-    /**
-     * Sets background image - currently, only PNG images can be used as background.
-     *
-     * @param backgroundImage Path to background image
-     */
-    public void setBackgroundImage(String backgroundImage) {
-        this.backgroundImage = backgroundImage;
-    }
-
-    /**
-     * Sets overlay image - currently, only PNG images can be used as overlay. Overlay image is
-     * printed on the top of the image, once it is completely created.
-     *
-     * @param overlayImage Path to overlay image
-     */
-    public void setOverlayImage(String overlayImage) {
-        this.overlayImage = overlayImage;
-    }
-
-    /**
-     * Sets unit to be displayed on y axis. It is wise to use only short units on graph, however.
-     *
-     * @param unit Unit description
-     */
-    public void setUnit(String unit) {
-        this.unit = unit;
-    }
-
-    /**
-     * Creates graph only if the current graph is out of date or not existent.
-     *
-     * @param lazy true, if graph should be 'lazy', false otherwise (defualt)
-     */
-    public void setLazy(boolean lazy) {
-        this.lazy = lazy;
-    }
-
-    /**
-     * Sets the lower limit of a graph. But rather, this is the
-     * maximum lower bound of a graph. For example, the value -100 will
-     * result in a graph that has a lower limit of -100 or less.  Use this
-     * method to expand graphs down.
-     *
-     * @param minValue Minimal value displayed on the graph
-     */
-    public void setMinValue(double minValue) {
-        this.minValue = minValue;
-    }
-
-    /**
-     * Defines the value normally located at the upper border of the
-     * graph. If the graph contains higher values, the upper border will
-     * move upwards to accommodate these values as well.
-     * <p>
-     * If you want to define an upper-limit which will not move in any
-     * event you have to use {@link #setRigid(boolean)} method as well.
-     *
-     * @param maxValue Maximal value displayed on the graph.
-     */
-    public void setMaxValue(double maxValue) {
-        this.maxValue = maxValue;
-    }
-
-    /**
-     * Sets rigid boundaries mode. Normally JRObin will automatically expand
-     * the lower and upper limit if the graph contains a value outside the
-     * valid range. With the <code>true</code> argument you can disable this behavior.
-     *
-     * @param rigid true if uper and lower limits should not be expanded to accomodate
-     *              values outside of the specified range. False otherwise (default).
-     */
-    public void setRigid(boolean rigid) {
-        this.rigid = rigid;
-    }
-
-    /**
-     * Sets default base for magnitude scaling. If you are graphing memory
-     * (and NOT network traffic) this switch should be set to 1024 so that 1Kb is 1024 byte.
-     * For traffic measurement, 1 kb/s is 1000 b/s.
-     *
-     * @param base Base value (defaults to 1000.0)
-     */
-    public void setBase(double base) {
-        this.base = base;
-    }
-
-    /**
-     * Sets logarithmic y-axis scaling.
-     *
-     * @param logarithmic true, for logarithmic scaling, false otherwise (default).
-     */
-    public void setLogarithmic(boolean logarithmic) {
-        this.logarithmic = logarithmic;
-    }
-
-    /**
-     * Overrides the colors for the standard elements of the graph. The colorTag
-     * must be one of the following constants defined in the
-     * {@link RrdGraphConstants}:
-     * {@link RrdGraphConstants#COLOR_BACK COLOR_BACK} background,
-     * {@link RrdGraphConstants#COLOR_CANVAS COLOR_CANVAS} canvas,
-     * {@link RrdGraphConstants#COLOR_SHADEA COLOR_SHADEA} left/top border,
-     * {@link RrdGraphConstants#COLOR_SHADEB COLOR_SHADEB} right/bottom border,
-     * {@link RrdGraphConstants#COLOR_GRID COLOR_GRID} major grid,
-     * {@link RrdGraphConstants#COLOR_MGRID COLOR_MGRID} minor grid,
-     * {@link RrdGraphConstants#COLOR_FONT COLOR_FONT} font,
-     * {@link RrdGraphConstants#COLOR_FRAME COLOR_FRAME} axis of the graph,
-     * {@link RrdGraphConstants#COLOR_ARROW COLOR_ARROW} arrow. This method can
-     * be called multiple times to set several colors.
-     *
-     * @param colorTag Color tag, as explained above.
-     * @param color    Any color (paint) you like
-     * @throws RrdException Thrown if invalid colorTag is supplied.
-     */
-    public void setColor(int colorTag, Paint color) throws RrdException {
-        if (colorTag >= 0 && colorTag < colors.length) {
-            colors[colorTag] = color;
-        }
-        else {
-            throw new RrdException("Invalid color index specified: " + colorTag);
-        }
-    }
-
-    /**
-     * Overrides the colors for the standard elements of the graph by element name.
-     * See {@link #setColor(int, java.awt.Paint)} for full explanation.
-     *
-     * @param colorName One of the following strings: "BACK", "CANVAS", "SHADEA", "SHADEB",
-     *                  "GRID", "MGRID", "FONT", "FRAME", "ARROW"
-     * @param color     Any color (paint) you like
-     * @throws RrdException Thrown if invalid element name is supplied.
-     */
-    public void setColor(String colorName, Paint color) throws RrdException {
-        setColor(getColorTagByName(colorName), color);
-    }
-
-    private static int getColorTagByName(String colorName) throws RrdException {
-        for (int i = 0; i < COLOR_NAMES.length; i++) {
-            if (COLOR_NAMES[i].equalsIgnoreCase(colorName)) {
-                return i;
-            }
-        }
-        throw new RrdException("Unknown color name specified: " + colorName);
-    }
-
-    /**
-     * Suppress generation of legend, only render the graph.
-     *
-     * @param noLegend true if graph legend should be omitted. False otherwise (default).
-     */
-    public void setNoLegend(boolean noLegend) {
-        this.noLegend = noLegend;
-    }
-
-    /**
-     * Suppresses anything but the graph, works only for height &lt; 64.
-     *
-     * @param onlyGraph true if only graph should be created, false otherwise (default).
-     */
-    public void setOnlyGraph(boolean onlyGraph) {
-        this.onlyGraph = onlyGraph;
-    }
-
-    /**
-     * Force the generation of HRULE and VRULE legend even if those HRULE
-     * or VRULE will not be drawn because out of graph boundaries.
-     *
-     * @param forceRulesLegend true if rule legend should be always printed,
-     *                         false otherwise (default).
-     */
-    public void setForceRulesLegend(boolean forceRulesLegend) {
-        this.forceRulesLegend = forceRulesLegend;
-    }
-
-    /**
-     * Defines a title to be written into the graph.
-     *
-     * @param title Graph title.
-     */
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    /**
-     * Suggests which time step should be used by JRobin while processing data from RRD files.
-     *
-     * @param step Desired time step (don't use this method if you don't know what you're doing).
-     */
-    public void setStep(long step) {
-        this.step = step;
-    }
-
-    /**
-     * Get the default small font for graphing.
-     *
-     * @return the font
-     */
-    public Font getSmallFont() {
-        return this.fonts[FONTTAG_DEFAULT];
-    }
-
-    /**
-     * Get the default large font for graphing.
-     *
-     * @return the font
-     */
-    public Font getLargeFont() {
-        return this.fonts[FONTTAG_TITLE];
-    }
-
-    /**
-     * Sets default font for graphing. Note that JRobin will behave unpredictably if proportional
-     * font is selected.
-     *
-     * @param smallFont Default font for graphing. Use only monospaced fonts.
-     * @throws RrdException Thrown if invalid fontTag is supplied.
-     */
-    public void setSmallFont(final Font smallFont) throws RrdException{
-        this.setFont(FONTTAG_DEFAULT, smallFont);
-    }
-
-    /**
-     * Sets title font.
-     *
-     * @param largeFont Font to be used for graph title.
-     * @throws RrdException Thrown if invalid fontTag is supplied.
-     */
-    public void setLargeFont(final Font largeFont) throws RrdException {
-        this.setFont(FONTTAG_TITLE, largeFont);
-    }
-
-    /**
-     * Sets font to be used for a specific font tag. The fontTag
-     * must be one of the following constants defined in the
-     * {@link RrdGraphConstants}:
-     * {@link RrdGraphConstants#FONTTAG_DEFAULT FONTTAG_DEFAULT} default font,,
-     * {@link RrdGraphConstants#FONTTAG_TITLE FONTTAG_TITLE} title,
-     * {@link RrdGraphConstants#FONTTAG_AXIS FONTTAG_AXIS} grid axis,,
-     * {@link RrdGraphConstants#FONTTAG_UNIT FONTTAG_UNIT} vertical unit label,,
-     * {@link RrdGraphConstants#FONTTAG_LEGEND FONTTAG_LEGEND} legend,
-     * {@link RrdGraphConstants#FONTTAG_WATERMARK FONTTAG_WATERMARK} watermark.
-     * This method can be called multiple times to set several fonts.
-     *
-     * @param fontTag Font tag, as explained above.
-     * @param font Font to be used for tag
-     * @throws RrdException Thrown if invalid fontTag is supplied.
-     */
-    public void setFont(final int fontTag, final Font font) throws RrdException {
-        this.setFont(fontTag, font, false);
-    }
-
-    /**
-     * Sets font.
-     *
-     * @param fontTag Font tag, as explained above.
-     * @param font Font to be used for tag
-     * @param setAll Boolean to flag whether to set all fonts if fontTag == FONTTAG_DEFAULT
-     * @throws RrdException Thrown if invalid fontTag is supplied.
-     */
-    public void setFont(final int fontTag, final Font font, final boolean setAll) throws RrdException {
-        this.setFont(fontTag, font, setAll, false);
-    }
-
-    /**
-     * Sets font.
-     *
-     * @param fontTag Font tag, as explained above.
-     * @param font Font to be used for tag
-     * @param setAll Boolean to flag whether to set all fonts if fontTag == FONTTAG_DEFAULT
-     * @param keepSizes Boolean to flag whether to keep original font sizes if setting all fonts.
-     */
-    public void setFont(final int fontTag, final Font font, final boolean setAll, final boolean keepSizes) {
-        if (fontTag == FONTTAG_DEFAULT && setAll) {
-            if (keepSizes) {
-               this.fonts[FONTTAG_DEFAULT] = font.deriveFont(this.fonts[FONTTAG_DEFAULT].getSize());
-               this.fonts[FONTTAG_TITLE] = font.deriveFont(this.fonts[FONTTAG_TITLE].getSize());
-               this.fonts[FONTTAG_AXIS] = font.deriveFont(this.fonts[FONTTAG_AXIS].getSize());
-               this.fonts[FONTTAG_UNIT] = font.deriveFont(this.fonts[FONTTAG_UNIT].getSize());
-               this.fonts[FONTTAG_LEGEND] = font.deriveFont(this.fonts[FONTTAG_LEGEND].getSize());
-               this.fonts[FONTTAG_WATERMARK] = font.deriveFont(this.fonts[FONTTAG_WATERMARK].getSize());
-            } else {
-               this.fonts[FONTTAG_DEFAULT] = font;
-               this.fonts[FONTTAG_TITLE] = null;
-               this.fonts[FONTTAG_AXIS] = null;
-               this.fonts[FONTTAG_UNIT] = null;
-               this.fonts[FONTTAG_LEGEND] = null;
-               this.fonts[FONTTAG_WATERMARK] = null;
-            }
-        } else {
-            this.fonts[fontTag] = font;
-        }
-    }
-
-    /**
-     * Sets font.
-     *
-     * @param fontTag Font tag as String, as explained in {@link RrdGraphDef#setFont setFont(int, java.awt.Font)}.
-     * @param font Font to be used for tag
-     * @throws RrdException Thrown if invalid fontTag is supplied.
-     */
-    public void setFont(final String fontTag, final Font font) throws RrdException {
-        this.setFont(getFontTagByName(fontTag), font);
-    }
-
-    /**
-     * Sets font.
-     *
-     * @param fontTag Font tag as String, as explained in {@link RrdGraphDef#setFont setFont(int, java.awt.Font)}.
-     * @param font Font to be used for tag
-     * @param setAll Boolean to flag whether to set all fonts if fontTag == FONTTAG_DEFAULT
-     * @throws RrdException Thrown if invalid fontTag is supplied.
-     */
-    public void setFont(final String fontTag, final Font font, final boolean setAll) throws RrdException {
-        this.setFont(getFontTagByName(fontTag), font, setAll);
-    }
-
-    /**
-     * Sets font.
-     *
-     * @param fontTag Font tag as String, as explained in {@link RrdGraphDef#setFont setFont(int, java.awt.Font)}.
-     * @param font Font to be used for tag
-     * @param setAll Boolean to flag whether to set all fonts if fontTag == FONTTAG_DEFAULT
-     * @param keepSizes Boolean to flag whether to keep original font sizes if setting all fonts.
-     * @throws RrdException Thrown if invalid fontTag is supplied.
-     */
-    public void setFont(final String fontTag, final Font font, final boolean setAll, final boolean keepSizes) throws RrdException {
-        this.setFont(getFontTagByName(fontTag), font, setAll, keepSizes);
-    }
-
-    private static int getFontTagByName(String tagName) throws RrdException {
-        for (int i = 0; i < FONTTAG_NAMES.length; i++) {
-            if (FONTTAG_NAMES[i].equalsIgnoreCase(tagName)) {
-                return i;
-            }
-        }
-        throw new RrdException("Unknown tag name specified: " + tagName);
-    }
-
-    public Font getFont(int tag) {
-        return this.fonts[tag] == null ? this.fonts[FONTTAG_DEFAULT] : this.fonts[tag];
-    }
-
-    /**
-     * Defines virtual datasource. This datasource can then be used
-     * in other methods like {@link #datasource(String, String)} or
-     * {@link #gprint(String, String, String)}.
-     *
-     * @param name      Source name
-     * @param rrdPath   Path to RRD file
-     * @param dsName    Datasource name in the specified RRD file
-     * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
-     */
-    public void datasource(String name, String rrdPath, String dsName, String consolFun) {
-        sources.add(new Def(name, rrdPath, dsName, consolFun));
-    }
-
-    /**
-     * Defines virtual datasource. This datasource can then be used
-     * in other methods like {@link #datasource(String, String)} or
-     * {@link #gprint(String, String, String)}.
-     *
-     * @param name      Source name
-     * @param rrdPath   Path to RRD file
-     * @param dsName    Datasource name in the specified RRD file
-     * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
-     * @param backend   Backend to be used while fetching data from a RRD file.
-     */
-    public void datasource(String name, String rrdPath, String dsName, String consolFun, String backend) {
-        sources.add(new Def(name, rrdPath, dsName, consolFun, backend));
-    }
-
-    /**
-     * Create a new virtual datasource by evaluating a mathematical
-     * expression, specified in Reverse Polish Notation (RPN).
-     *
-     * @param name          Source name
-     * @param rpnExpression RPN expression.
-     */
-    public void datasource(String name, String rpnExpression) {
-        sources.add(new CDef(name, rpnExpression));
-    }
-
-    /**
-     * Creates a new (static) virtual datasource. The value of the datasource is constant. This value is
-     * evaluated by applying the given consolidation function to another virtual datasource.
-     *
-     * @param name      Source name
-     * @param defName   Other source name
-     * @param consolFun Consolidation function to be applied to other datasource.
-     */
-    public void datasource(String name, String defName, String consolFun) {
-        sources.add(new SDef(name, defName, consolFun));
-    }
-
-    /**
-     * Creates a new (plottable) datasource. Datasource values are obtained from the given plottable
-     * object.
-     *
-     * @param name      Source name.
-     * @param plottable Plottable object.
-     */
-    public void datasource(String name, Plottable plottable) {
-        sources.add(new PDef(name, plottable));
-    }
-
-    /**
-     * Creates a new static virtual datasource that performs a percentile calculation on an
-     * another named datasource to yield a single value.
-     * <p>
-     * Requires that the other datasource has already been defined otherwise it throws an exception
-     * (we need to look at the existing data source to extract the required data)
-     *
-     * @param name       - the new virtual datasource name
-     * @param sourceName - the datasource from which to extract the percentile.  Must be a previously
-     *                   defined virtula datasource
-     * @param percentile - the percentile to extract from the source datasource
-     */
-    public void datasource(String name, String sourceName, double percentile) {
-        sources.add(new PercentileDef(name, sourceName, percentile));
-    }
-
-    /**
-     * Creates a new static virtual datasource that performs a percentile calculation on an
-     * another named datasource to yield a single value.
-     * <p>
-     * Requires that the other datasource has already been defined otherwise it throws an exception
-     * (we need to look at the existing data source to extract the required data)
-     *
-     * @param name       - the new virtual datasource name
-     * @param sourceName - the datasource from which to extract the percentile.  Must be a previously
-     *                   defined virtula datasource
-     * @param percentile - the percentile to extract from the source datasource
-     * @param includenan - whether to include NaNs in the percentile calculations.
-     */
-    public void datasource(String name, String sourceName, double percentile, boolean includenan) {
-        sources.add(new PercentileDef(name, sourceName, percentile, includenan));
-    }
-
-    /**
-     * Calculates the chosen consolidation function CF over the given datasource
-     * and creates the result by using the given format string.  In
-     * the format string there should be a '%[l]f', '%[l]g' or '%[l]e' marker in
-     * the place where the number should be printed.
-     * <p>
-     * If an additional '%s' is found AFTER the marker, the value will be
-     * scaled and an appropriate SI magnitude unit will be printed in
-     * place of the '%s' marker. The scaling will take the '--base' argument
-     * into consideration!
-     * <p>
-     * If a '%S' is used instead of a '%s', then instead of calculating
-     * the appropriate SI magnitude unit for this value, the previously
-     * calculated SI magnitude unit will be used.  This is useful if you
-     * want all the values in a print statement to have the same SI magnitude
-     * unit.  If there was no previous SI magnitude calculation made,
-     * then '%S' behaves like a '%s', unless the value is 0, in which case
-     * it does not remember a SI magnitude unit and a SI magnitude unit
-     * will only be calculated when the next '%s' is seen or the next '%S'
-     * for a non-zero value.
-     * <p>
-     * Print results are collected in the {@link RrdGraphInfo} object which is retrieved
-     * from the {@link RrdGraph object} once the graph is created.
-     *
-     * @param srcName   Virtual source name
-     * @param consolFun Consolidation function to be applied to the source
-     * @param format    Format string (like "average = %10.3f %s")
-     */
-    public void print(String srcName, String consolFun, String format) {
-        comments.add(new PrintText(srcName, consolFun, format, false));
-    }
-
-    /**
-     * This method does basically the same thing as {@link #print(String, String, String)},
-     * but the result is printed on the graph itself, below the chart area.
-     *
-     * @param srcName   Virtual source name
-     * @param consolFun Consolidation function to be applied to the source
-     * @param format    Format string (like "average = %10.3f %s")
-     */
-    public void gprint(String srcName, String consolFun, String format) {
-        comments.add(new PrintText(srcName, consolFun, format, true));
-    }
-
-    /**
-     * Comment to be printed on the graph.
-     *
-     * @param text Comment text
-     */
-    public void comment(String text) {
-        comments.add(new CommentText(text));
-    }
-
-    /**
-     * Draws a horizontal rule into the graph and optionally adds a legend
-     *
-     * @param value  Position of the rule
-     * @param color  Rule color
-     * @param legend Legend text. If null, legend text will be omitted.
-     */
-    public void hrule(double value, Paint color, String legend) {
-        hrule(value, color, legend, 1.0F);
-    }
-
-    /**
-     * Draws a horizontal rule into the graph and optionally adds a legend
-     *
-     * @param value  Position of the rule
-     * @param color  Rule color
-     * @param legend Legend text. If null, legend text will be omitted.
-     * @param width  Rule width
-     */
-    public void hrule(double value, Paint color, String legend, float width) {
-        LegendText legendText = new LegendText(color, legend);
-        comments.add(legendText);
-        plotElements.add(new HRule(value, color, legendText, width));
-    }
-
-    /**
-     * Draws a vertical rule into the graph and optionally adds a legend
-     *
-     * @param timestamp Position of the rule (seconds since epoch)
-     * @param color     Rule color
-     * @param legend    Legend text. Use null to omit the text.
-     */
-    public void vrule(long timestamp, Paint color, String legend) {
-        vrule(timestamp, color, legend, 1.0F);
-    }
-
-    /**
-     * Draws a vertical rule into the graph and optionally adds a legend
-     *
-     * @param timestamp Position of the rule (seconds since epoch)
-     * @param color     Rule color
-     * @param legend    Legend text. Use null to omit the text.
-     * @param width     Rule width
-     */
-    public void vrule(long timestamp, Paint color, String legend, float width) {
-        LegendText legendText = new LegendText(color, legend);
-        comments.add(legendText);
-        plotElements.add(new VRule(timestamp, color, legendText, width));
-    }
-
-    /**
-     * Plots requested data as a line, using the color and the line width specified.
-     *
-     * @param srcName Virtual source name
-     * @param color   Line color
-     * @param legend  Legend text
-     * @param width   Line width (default: 1.0F)
-     */
-    public void line(String srcName, Paint color, String legend, float width) {
-        if (legend != null) {
-            comments.add(new LegendText(color, legend));
-        }
-        plotElements.add(new Line(srcName, color, width));
-    }
-
-    /**
-     * Plots requested data as a line, using the color specified. Line width is assumed to be
-     * 1.0F.
-     *
-     * @param srcName Virtual source name
-     * @param color   Line color
-     * @param legend  Legend text
-     */
-    public void line(String srcName, Paint color, String legend) {
-        line(srcName, color, legend, 1F);
-    }
-
-    /**
-     * Plots requested data in the form of the filled area starting from zero,
-     * using the color specified.
-     *
-     * @param srcName Virtual source name.
-     * @param color   Color of the filled area.
-     * @param legend  Legend text.
-     */
-    public void area(String srcName, Paint color, String legend) {
-        area(srcName, color);
-        if ((legend != null) && (legend.length() > 0)) {
-            LegendText legendText = new LegendText(color, legend);
-            comments.add(legendText);
-        }
-    }
-
-    /**
-     * Plots requested data in the form of the filled area starting from zero,
-     * using the color specified.
-     *
-     * @param srcName Virtual source name.
-     * @param color   Color of the filled area.
-     */
-    public void area(String srcName, Paint color) {
-        plotElements.add(new Area(srcName, color));
-    }
-
-    /**
-     * Does the same as {@link #line(String, java.awt.Paint, String)},
-     * but the graph gets stacked on top of the
-     * previous LINE, AREA or STACK graph. Depending on the type of the
-     * previous graph, the STACK will be either a LINE or an AREA.  This
-     * obviously implies that the first STACK must be preceded by an AREA
-     * or LINE.
-     * <p>
-     * Note, that when you STACK onto *UNKNOWN* data, JRobin will not
-     * draw any graphics ... *UNKNOWN* is not zero.
-     *
-     * @param srcName Virtual source name
-     * @param color   Stacked graph color
-     * @param legend  Legend text
-     * @throws RrdException Thrown if this STACK has no previously defined AREA, STACK or LINE
-     *                      graph bellow it.
-     */
-    public void stack(String srcName, Paint color, String legend) throws RrdException {
-        // find parent AREA or LINE
-        SourcedPlotElement parent = null;
-        for (int i = plotElements.size() - 1; i >= 0; i--) {
-            PlotElement plotElement = plotElements.get(i);
-            if (plotElement instanceof SourcedPlotElement) {
-                parent = (SourcedPlotElement) plotElement;
-                break;
-            }
-        }
-        if (parent == null) {
-            throw new RrdException("You have to stack graph onto something (line or area)");
-        }
-        else {
-            LegendText legendText = new LegendText(color, legend);
-            comments.add(legendText);
-            plotElements.add(new Stack(parent, srcName, color));
-        }
-    }
-
-    /**
-     * Sets visibility of the X-axis grid.
-     *
-     * @param drawXGrid True if X-axis grid should be created (default), false otherwise.
-     */
-    public void setDrawXGrid(boolean drawXGrid) {
-        this.drawXGrid = drawXGrid;
-    }
-
-    /**
-     * Sets visibility of the Y-axis grid.
-     *
-     * @param drawYGrid True if Y-axis grid should be created (default), false otherwise.
-     */
-    public void setDrawYGrid(boolean drawYGrid) {
-        this.drawYGrid = drawYGrid;
-    }
-
-    /**
-     * Sets image quality. Relevant only for JPEG images.
-     *
-     * @param imageQuality (0F=worst, 1F=best).
-     */
-    public void setImageQuality(float imageQuality) {
-        this.imageQuality = imageQuality;
-    }
-
-    /**
-     * Controls if the chart area of the image should be antialiased or not.
-     *
-     * @param antiAliasing use true to turn antialiasing on, false to turn it off (default)
-     */
-    public void setAntiAliasing(boolean antiAliasing) {
-        this.antiAliasing = antiAliasing;
-    }
-
-    /**
-     * Shows or hides graph signature (gator) in the top right corner of the graph
-     *
-     * @param showSignature true, if signature should be seen (default), false otherwise
-     */
-    public void setShowSignature(boolean showSignature) {
-        this.showSignature = showSignature;
-    }
-
-    /**
-     * Sets first day of the week.
-     *
-     * @param firstDayOfWeek One of the following constants:
-     *                       {@link RrdGraphConstants#MONDAY MONDAY},
-     *                       {@link RrdGraphConstants#TUESDAY TUESDAY},
-     *                       {@link RrdGraphConstants#WEDNESDAY WEDNESDAY},
-     *                       {@link RrdGraphConstants#THURSDAY THURSDAY},
-     *                       {@link RrdGraphConstants#FRIDAY FRIDAY},
-     *                       {@link RrdGraphConstants#SATURDAY SATURDAY},
-     *                       {@link RrdGraphConstants#SUNDAY SUNDAY}
-     */
-    public void setFirstDayOfWeek(int firstDayOfWeek) {
-        this.firstDayOfWeek = firstDayOfWeek;
-    }
-
-    // helper methods
-
-    int printStatementCount() {
-        int count = 0;
-        for (CommentText comment : comments) {
-            if (comment instanceof PrintText) {
-                if (comment.isPrint()) {
-                    count++;
-                }
-            }
-        }
-        return count;
-    }
-
-    boolean shouldPlot() {
-        if (plotElements.size() > 0) {
-            return true;
-        }
-        for (CommentText comment : comments) {
-            if (comment.isValidGraphElement()) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/RrdGraphDefTemplate.java b/apps/jrobin/java/src/org/jrobin/graph/RrdGraphDefTemplate.java
deleted file mode 100644
index 7b0c7f1c51f8574d9fb96379773f8e403bc5eb53..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/RrdGraphDefTemplate.java
+++ /dev/null
@@ -1,982 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-import org.jrobin.core.XmlTemplate;
-import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
-
-import java.awt.*;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Class used to create an arbitrary number of RrdGraphDef (graph definition) objects
- * from a single XML template. XML template can be supplied as an XML InputSource,
- * XML file or XML formatted string.
- * <p>
- * Here is an example of a properly formatted XML template with all available options in it
- * (unwanted options can be removed/ignored):
- * <p>
- * <pre>
- * &lt;rrd_graph_def&gt;
- *     &lt;!-- use '-' to represent in-memory graph --&gt;
- *     &lt;filename&gt;test.png&lt;/filename&gt;
- *     &lt;!--
- *         starting and ending timestamps can be specified by
- *         using at-style time specification, or by specifying
- *         exact timestamps since epoch (without milliseconds)
- *     --&gt;
- *     &lt;span&gt;
- *         &lt;start&gt;now - 1d&lt;/start&gt;
- *         &lt;end&gt;now&lt;/end&gt;
- *     &lt;/span&gt;
- *     &lt;options&gt;
- *         &lt;!--
- *             specify 'true' if you want to use RrdDbPool while
- *             creating graph
- *         --&gt;
- *         &lt;use_pool&gt;false&lt;/use_pool&gt;
- *         &lt;anti_aliasing&gt;true&lt;/anti_aliasing&gt;
- *         &lt;time_grid&gt;
- *             &lt;show_grid&gt;true&lt;/show_grid&gt;
- *             &lt;!-- allowed units: second, minute, hour, day, week, month, year --&gt;
- *             &lt;minor_grid_unit&gt;minute&lt;/minor_grid_unit&gt;
- *             &lt;minor_grid_unit_count&gt;60&lt;/minor_grid_unit_count&gt;
- *             &lt;major_grid_unit&gt;hour&lt;/major_grid_unit&gt;
- *             &lt;major_grid_unit_count&gt;2&lt;/major_grid_unit_count&gt;
- *             &lt;label_unit&gt;hour&lt;/label_unit&gt;
- *             &lt;label_unit_count&gt;2&lt;/label_unit_count&gt;
- *             &lt;label_span&gt;1200&lt;/label_span&gt;
- *             &lt;!-- use SimpleDateFormat or strftime-like format to format labels --&gt;
- *             &lt;label_format&gt;dd-MMM-yy&lt;/label_format&gt;
- *         &lt;/time_grid&gt;
- *         &lt;value_grid&gt;
- *             &lt;show_grid&gt;true&lt;/show_grid&gt;
- *             &lt;grid_step&gt;100.0&lt;/grid_step&gt;
- *             &lt;label_factor&gt;5&lt;/label_factor&gt;
- *         &lt;/value_grid&gt;
- *         &lt;no_minor_grid&gt;true&lt;/no_minor_grid&gt;
- *         &lt;alt_y_grid&gt;true&lt;/alt_y_grid&gt;
- *         &lt;alt_y_mrtg&gt;true&lt;/alt_y_mrtg&gt;
- *         &lt;alt_autoscale&gt;true&lt;/alt_autoscale&gt;
- *         &lt;alt_autoscale_max&gt;true&lt;/alt_autoscale_max&gt;
- *         &lt;units_exponent&gt;3&lt;/units_exponent&gt;
- *         &lt;units_length&gt;13&lt;/units_length&gt;
- *         &lt;vertical_label&gt;Speed (kbits/sec)&lt;/vertical_label&gt;
- *         &lt;width&gt;444&lt;/width&gt;
- *         &lt;height&gt;222&lt;/height&gt;
- *         &lt;interlaced&gt;true&lt;/interlaced&gt;
- *         &lt;image_info&gt;filename = %s, width=%d, height=%d&lt;/image_info&gt;
- *         &lt;image_format&gt;png&lt;/image_format&gt;
- *         &lt;image_quality&gt;0.8&lt;/image_quality&gt;
- *         &lt;background_image&gt;luka.png&lt;/background_image&gt;
- *         &lt;overlay_image&gt;luka.png&lt;/overlay_image&gt;
- *         &lt;unit&gt;kilos&lt;/unit&gt;
- *         &lt;lazy&gt;false&lt;/lazy&gt;
- *         &lt;min_value&gt;0&lt;/min_value&gt;
- *         &lt;max_value&gt;5000&lt;/max_value&gt;
- *         &lt;rigid&gt;true&lt;/rigid&gt;
- *         &lt;base&gt;1000&lt;/base&gt;
- *         &lt;logarithmic&gt;false&lt;/logarithmic&gt;
- *         &lt;colors&gt;
- *             &lt;canvas&gt;#FFFFFF&lt;/canvas&gt;
- *             &lt;back&gt;#FFFFFF&lt;/back&gt;
- *             &lt;shadea&gt;#AABBCC&lt;/shadea&gt;
- *             &lt;shadeb&gt;#DDDDDD&lt;/shadeb&gt;
- *             &lt;grid&gt;#FF0000&lt;/grid&gt;
- *             &lt;mgrid&gt;#00FF00&lt;/mgrid&gt;
- *             &lt;font&gt;#FFFFFF&lt;/font&gt;
- *             &lt;frame&gt;#EE00FF&lt;/frame&gt;
- *             &lt;arrow&gt;#FF0000&lt;/arrow&gt;
- *         &lt;/colors&gt;
- *         &lt;no_legend&gt;false&lt;/no_legend&gt;
- *         &lt;only_graph&gt;false&lt;/only_graph&gt;
- *         &lt;force_rules_legend&gt;false&lt;/force_rules_legend&gt;
- *         &lt;title&gt;This is a title&lt;/title&gt;
- *         &lt;step&gt;300&lt;/step&gt;
- *         &lt;fonts&gt;
- *             &lt;small_font&gt;
- *                 &lt;name&gt;Courier&lt;/name&gt;
- *                 &lt;style&gt;bold italic&lt;/style&gt;
- *                 &lt;size&gt;12&lt;/size&gt;
- *             &lt;/small_font&gt;
- *             &lt;large_font&gt;
- *                 &lt;name&gt;Courier&lt;/name&gt;
- *                 &lt;style&gt;plain&lt;/style&gt;
- *                 &lt;size&gt;11&lt;/size&gt;
- *             &lt;/large_font&gt;
- *         &lt;/fonts&gt;
- *         &lt;first_day_of_week&gt;SUNDAY&lt;/first_day_of_week&gt;
- *     &lt;/options&gt;
- *     &lt;datasources&gt;
- *         &lt;def&gt;
- *             &lt;name&gt;x&lt;/name&gt;
- *             &lt;rrd&gt;test.rrd&lt;/rrd&gt;
- *             &lt;source&gt;sun&lt;/source&gt;
- *             &lt;cf&gt;AVERAGE&lt;/cf&gt;
- *             &lt;backend&gt;FILE&lt;/backend&gt;
- *         &lt;/def&gt;
- *         &lt;def&gt;
- *             &lt;name&gt;y&lt;/name&gt;
- *             &lt;rrd&gt;test.rrd&lt;/rrd&gt;
- *             &lt;source&gt;shade&lt;/source&gt;
- *             &lt;cf&gt;AVERAGE&lt;/cf&gt;
- *         &lt;/def&gt;
- *         &lt;cdef&gt;
- *             &lt;name&gt;x_plus_y&lt;/name&gt;
- *             &lt;rpn&gt;x,y,+&lt;/rpn&gt;
- *         &lt;/cdef&gt;
- *         &lt;cdef&gt;
- *             &lt;name&gt;x_minus_y&lt;/name&gt;
- *             &lt;rpn&gt;x,y,-&lt;/rpn&gt;
- *         &lt;/cdef&gt;
- *         &lt;sdef&gt;
- *             &lt;name&gt;x_avg&lt;/name&gt;
- *             &lt;source&gt;x&lt;/source&gt;
- *             &lt;cf&gt;AVERAGE&lt;/cf&gt;
- *         &lt;/sdef&gt;
- *         &lt;sdef&gt;
- *             &lt;name&gt;y_max&lt;/name&gt;
- *             &lt;source&gt;y&lt;/source&gt;
- *             &lt;cf&gt;MAX&lt;/cf&gt;
- *         &lt;/sdef&gt;
- *     &lt;/datasources&gt;
- *     &lt;graph&gt;
- *         &lt;area&gt;
- *             &lt;datasource&gt;x&lt;/datasource&gt;
- *             &lt;color&gt;#FF0000&lt;/color&gt;
- *             &lt;legend&gt;X value\r&lt;/legend&gt;
- *         &lt;/area&gt;
- *         &lt;stack&gt;
- *             &lt;datasource&gt;y&lt;/datasource&gt;
- *             &lt;color&gt;#00FF00&lt;/color&gt;
- *             &lt;legend&gt;Y value\r&lt;/legend&gt;
- *         &lt;/stack&gt;
- *         &lt;line&gt;
- *             &lt;datasource&gt;x&lt;/datasource&gt;
- *             &lt;color&gt;#FF0000&lt;/color&gt;
- *             &lt;legend&gt;X value\r&lt;/legend&gt;
- *             &lt;width&gt;2&lt;/width&gt;
- *         &lt;/line&gt;
- *         &lt;print&gt;
- *             &lt;datasource&gt;x&lt;/datasource&gt;
- *             &lt;cf&gt;AVERAGE&lt;/cf&gt;
- *             &lt;format&gt;Average is %7.3f\c&lt;/format&gt;
- *         &lt;/print&gt;
- *         &lt;gprint&gt;
- *             &lt;datasource&gt;y&lt;/datasource&gt;
- *             &lt;cf&gt;MAX&lt;/cf&gt;
- *             &lt;format&gt;Max is %7.3f\c&lt;/format&gt;
- *         &lt;/gprint&gt;
- *         &lt;hrule&gt;
- *             &lt;value&gt;1250&lt;/value&gt;
- *             &lt;color&gt;#0000FF&lt;/color&gt;
- *             &lt;legend&gt;This is a horizontal rule&lt;/legend&gt;
- *         &lt;/hrule&gt;
- *         &lt;vrule&gt;
- *             &lt;time&gt;now-6h&lt;/time&gt;
- *             &lt;color&gt;#0000FF&lt;/color&gt;
- *             &lt;legend&gt;This is a vertical rule&lt;/legend&gt;
- *         &lt;/vrule&gt;
- *         &lt;comment&gt;Simple comment&lt;/comment&gt;
- *         &lt;comment&gt;One more comment\c&lt;/comment&gt;
- *     &lt;/graph&gt;
- * &lt;/rrd_graph_def&gt;
- * </pre>
- * Notes on the template syntax:
- * <p>
- * <ul>
- * <li>There is a strong relation between the XML template syntax and the syntax of
- * {@link RrdGraphDef} class methods. If you are not sure what some XML tag means, check javadoc
- * for the corresponding class method.
- * <li>hard-coded timestamps in templates should be long integeres
- * (like: 1000243567) or at-style formatted strings
- * <li>whitespaces are not harmful
- * <li>use <code>true</code>, <code>on</code>, <code>yes</code>, <code>y</code>,
- * or <code>1</code> to specify boolean <code>true</code> value (anything else will
- * be treated as <code>false</code>).
- * <li>floating point values: anything that cannot be parsed will be treated as Double.NaN
- * (like: U, unknown, 12r.23)
- * <li>use #RRGGBB or #RRGGBBAA format to specify colors.
- * <li>valid font styles are: PLAIN, ITALIC, BOLD, BOLDITALIC
- * <li>comments are allowed.
- * </ul>
- * Any template value (text between <code>&lt;some_tag&gt;</code> and
- * <code>&lt;/some_tag&gt;</code>) can be replaced with
- * a variable of the following form: <code>${variable_name}</code>. Use
- * {@link XmlTemplate#setVariable(String, String) setVariable()}
- * methods from the base class to replace
- * template variables with real values at runtime.
- * <p>
- * Typical usage scenario:
- * <p>
- * <ul>
- * <li>Create your XML template and save it to a file (template.xml, for example)
- * <li>Replace template values with variables if you want to change them during runtime.
- * For example, time span should not be hard-coded in the template - you probably want to create
- * many different graphs with different time spans from the same XML template.
- * For example, your XML template could start with:
- * <pre>
- * &lt;rrd_graph_def&gt;
- *     ...
- *     &lt;span&gt;
- *         &lt;start&gt;${start}&lt;/start&gt;
- *         &lt;end&gt;${end}&lt;/end&gt;
- *     &lt;/span&gt;
- *     ...
- * </pre>
- * <li>In your Java code, create RrdGraphDefTemplate object using your XML template file:
- * <pre>
- * RrdGraphDefTemplate t = new RrdGraphDefTemplate(new File(template.xml));
- * </pre>
- * <li>Then, specify real values for template variables:
- * <pre>
- * t.setVariable("start", new GregorianCalendar(2004, 2, 25));
- * t.setVariable("end", new GregorianCalendar(2004, 2, 26));
- * </pre>
- * <li>Once all template variables are set, just use the template object to create RrdGraphDef
- * object. This object is actually used to create JRobin grahps:
- * <pre>
- * RrdGraphDef gdef = t.getRrdGraphDef();
- * RrdGraph g = new RrdGraph(gdef);
- * </pre>
- * </ul>
- * You should create new RrdGraphDefTemplate object only once for each XML template. Single template
- * object can be reused to create as many RrdGraphDef objects as needed, with different values
- * specified for template variables. XML synatax check is performed only once - the first graph
- * definition object gets created relatively slowly, but it will be created much faster next time.
- */
-public class RrdGraphDefTemplate extends XmlTemplate implements RrdGraphConstants {
-	static final Color BLIND_COLOR = new Color(0, 0, 0, 0);
-
-	private RrdGraphDef rrdGraphDef;
-
-	/**
-	 * Creates template object from any parsable XML source
-	 *
-	 * @param inputSource XML source
-	 * @throws IOException  thrown in case of I/O error
-	 * @throws RrdException usually thrown in case of XML related error
-	 */
-	public RrdGraphDefTemplate(InputSource inputSource) throws IOException, RrdException {
-		super(inputSource);
-	}
-
-	/**
-	 * Creates template object from the file containing XML template code
-	 *
-	 * @param xmlFile file containing XML template
-	 * @throws IOException  thrown in case of I/O error
-	 * @throws RrdException usually thrown in case of XML related error
-	 */
-	public RrdGraphDefTemplate(File xmlFile) throws IOException, RrdException {
-		super(xmlFile);
-	}
-
-	/**
-	 * Creates template object from the string containing XML template code
-	 *
-	 * @param xmlString string containing XML template
-	 * @throws IOException  thrown in case of I/O error
-	 * @throws RrdException usually thrown in case of XML related error
-	 */
-	public RrdGraphDefTemplate(String xmlString) throws IOException, RrdException {
-		super(xmlString);
-	}
-
-	/**
-	 * Creates RrdGraphDef object which can be used to create RrdGraph
-	 * object (actual JRobin graphs). Before this method is called, all template variables (if any)
-	 * must be resolved (replaced with real values).
-	 * See {@link XmlTemplate#setVariable(String, String) setVariable()} method information to
-	 * understand how to supply values for template variables.
-	 *
-	 * @return Graph definition which can be used to create RrdGraph object (actual JRobin graphs)
-	 * @throws RrdException Thrown if parsed XML template contains invalid (unrecognized) tags
-	 */
-	public RrdGraphDef getRrdGraphDef() throws RrdException {
-		// basic check
-		if (!root.getTagName().equals("rrd_graph_def")) {
-			throw new RrdException("XML definition must start with <rrd_graph_def>");
-		}
-		validateTagsOnlyOnce(root, new String[] {"filename", "span", "options", "datasources", "graph"});
-		rrdGraphDef = new RrdGraphDef();
-		// traverse all nodes
-		Node[] childNodes = getChildNodes(root);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("filename")) {
-				resolveFilename(childNode);
-			}
-			// SPAN
-			else if (nodeName.equals("span")) {
-				resolveSpan(childNode);
-			}
-			// OPTIONS
-			else if (nodeName.equals("options")) {
-				resolveOptions(childNode);
-			}
-			// DATASOURCES
-			else if (nodeName.equals("datasources")) {
-				resolveDatasources(childNode);
-			}
-			// GRAPH ELEMENTS
-			else if (nodeName.equals("graph")) {
-				resolveGraphElements(childNode);
-			}
-		}
-		return rrdGraphDef;
-	}
-
-	private void resolveGraphElements(Node graphNode) throws RrdException {
-		validateTagsOnlyOnce(graphNode, new String[] {"area*", "line*", "stack*",
-				"print*", "gprint*", "hrule*", "vrule*", "comment*"});
-		Node[] childNodes = getChildNodes(graphNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("area")) {
-				resolveArea(childNode);
-			}
-			else if (nodeName.equals("line")) {
-				resolveLine(childNode);
-			}
-			else if (nodeName.equals("stack")) {
-				resolveStack(childNode);
-			}
-			else if (nodeName.equals("print")) {
-				resolvePrint(childNode, false);
-			}
-			else if (nodeName.equals("gprint")) {
-				resolvePrint(childNode, true);
-			}
-			else if (nodeName.equals("hrule")) {
-				resolveHRule(childNode);
-			}
-			else if (nodeName.equals("vrule")) {
-				resolveVRule(childNode);
-			}
-			else if (nodeName.equals("comment")) {
-				rrdGraphDef.comment(getValue(childNode));
-			}
-		}
-	}
-
-	private void resolveVRule(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"time", "color", "legend"});
-		long timestamp = Long.MIN_VALUE;
-		Paint color = null;
-		String legend = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("time")) {
-				timestamp = Util.getTimestamp(getValue(childNode));
-			}
-			else if (nodeName.equals("color")) {
-				color = getValueAsColor(childNode);
-			}
-			else if (nodeName.equals("legend")) {
-				legend = getValue(childNode);
-			}
-		}
-		if (timestamp != Long.MIN_VALUE && color != null) {
-			rrdGraphDef.vrule(timestamp, color, legend);
-		}
-		else {
-			throw new RrdException("Incomplete VRULE settings");
-		}
-	}
-
-	private void resolveHRule(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"value", "color", "legend"});
-		double value = Double.NaN;
-		Paint color = null;
-		String legend = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("value")) {
-				value = getValueAsDouble(childNode);
-			}
-			else if (nodeName.equals("color")) {
-				color = getValueAsColor(childNode);
-			}
-			else if (nodeName.equals("legend")) {
-				legend = getValue(childNode);
-			}
-		}
-		if (!Double.isNaN(value) && color != null) {
-			rrdGraphDef.hrule(value, color, legend);
-		}
-		else {
-			throw new RrdException("Incomplete HRULE settings");
-		}
-	}
-
-	private void resolvePrint(Node parentNode, boolean isInGraph) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"datasource", "cf", "format"});
-		String datasource = null, cf = null, format = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("datasource")) {
-				datasource = getValue(childNode);
-			}
-			else if (nodeName.equals("cf")) {
-				cf = getValue(childNode);
-			}
-			else if (nodeName.equals("format")) {
-				format = getValue(childNode);
-			}
-		}
-		if (datasource != null && cf != null && format != null) {
-			if (isInGraph) {
-				rrdGraphDef.gprint(datasource, cf, format);
-			}
-			else {
-				rrdGraphDef.print(datasource, cf, format);
-			}
-		}
-		else {
-			throw new RrdException("Incomplete " + (isInGraph ? "GRPINT" : "PRINT") + " settings");
-		}
-	}
-
-	private void resolveStack(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"datasource", "color", "legend"});
-		String datasource = null, legend = null;
-		Paint color = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("datasource")) {
-				datasource = getValue(childNode);
-			}
-			else if (nodeName.equals("color")) {
-				color = getValueAsColor(childNode);
-			}
-			else if (nodeName.equals("legend")) {
-				legend = getValue(childNode);
-			}
-		}
-		if (datasource != null) {
-			if (color != null) {
-				rrdGraphDef.stack(datasource, color, legend);
-			}
-			else {
-				rrdGraphDef.stack(datasource, BLIND_COLOR, legend);
-			}
-		}
-		else {
-			throw new RrdException("Incomplete STACK settings");
-		}
-	}
-
-	private void resolveLine(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"datasource", "color", "legend", "width"});
-		String datasource = null, legend = null;
-		Paint color = null;
-		float width = 1.0F;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("datasource")) {
-				datasource = getValue(childNode);
-			}
-			else if (nodeName.equals("color")) {
-				color = getValueAsColor(childNode);
-			}
-			else if (nodeName.equals("legend")) {
-				legend = getValue(childNode);
-			}
-			else if (nodeName.equals("width")) {
-				width = (float) getValueAsDouble(childNode);
-			}
-		}
-		if (datasource != null) {
-			if (color != null) {
-				rrdGraphDef.line(datasource, color, legend, width);
-			}
-			else {
-				rrdGraphDef.line(datasource, BLIND_COLOR, legend, width);
-			}
-		}
-		else {
-			throw new RrdException("Incomplete LINE settings");
-		}
-	}
-
-	private void resolveArea(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"datasource", "color", "legend"});
-		String datasource = null, legend = null;
-		Paint color = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("datasource")) {
-				datasource = getValue(childNode);
-			}
-			else if (nodeName.equals("color")) {
-				color = getValueAsColor(childNode);
-			}
-			else if (nodeName.equals("legend")) {
-				legend = getValue(childNode);
-			}
-		}
-		if (datasource != null) {
-			if (color != null) {
-				rrdGraphDef.area(datasource, color, legend);
-			}
-			else {
-				rrdGraphDef.area(datasource, BLIND_COLOR, legend);
-			}
-		}
-		else {
-			throw new RrdException("Incomplete AREA settings");
-		}
-	}
-
-	private void resolveDatasources(Node datasourcesNode) throws RrdException {
-		validateTagsOnlyOnce(datasourcesNode, new String[] {"def*", "cdef*", "sdef*"});
-		Node[] childNodes = getChildNodes(datasourcesNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("def")) {
-				resolveDef(childNode);
-			}
-			else if (nodeName.equals("cdef")) {
-				resolveCDef(childNode);
-			}
-			else if (nodeName.equals("sdef")) {
-				resolveSDef(childNode);
-			}
-		}
-	}
-
-	private void resolveSDef(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"name", "source", "cf"});
-		String name = null, source = null, cf = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("name")) {
-				name = getValue(childNode);
-			}
-			else if (nodeName.equals("source")) {
-				source = getValue(childNode);
-			}
-			else if (nodeName.equals("cf")) {
-				cf = getValue(childNode);
-			}
-		}
-		if (name != null && source != null && cf != null) {
-			rrdGraphDef.datasource(name, source, cf);
-		}
-		else {
-			throw new RrdException("Incomplete SDEF settings");
-		}
-	}
-
-	private void resolveCDef(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"name", "rpn"});
-		String name = null, rpn = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("name")) {
-				name = getValue(childNode);
-			}
-			else if (nodeName.equals("rpn")) {
-				rpn = getValue(childNode);
-			}
-		}
-		if (name != null && rpn != null) {
-			rrdGraphDef.datasource(name, rpn);
-		}
-		else {
-			throw new RrdException("Incomplete CDEF settings");
-		}
-	}
-
-	private void resolveDef(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"name", "rrd", "source", "cf", "backend"});
-		String name = null, rrd = null, source = null, cf = null, backend = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("name")) {
-				name = getValue(childNode);
-			}
-			else if (nodeName.equals("rrd")) {
-				rrd = getValue(childNode);
-			}
-			else if (nodeName.equals("source")) {
-				source = getValue(childNode);
-			}
-			else if (nodeName.equals("cf")) {
-				cf = getValue(childNode);
-			}
-			else if (nodeName.equals("backend")) {
-				backend = getValue(childNode);
-			}
-		}
-		if (name != null && rrd != null && source != null && cf != null) {
-			rrdGraphDef.datasource(name, rrd, source, cf, backend);
-		}
-		else {
-			throw new RrdException("Incomplete DEF settings");
-		}
-	}
-
-	private void resolveFilename(Node filenameNode) {
-		String filename = getValue(filenameNode);
-		rrdGraphDef.setFilename(filename);
-	}
-
-	private void resolveSpan(Node spanNode) throws RrdException {
-		validateTagsOnlyOnce(spanNode, new String[] {"start", "end"});
-		String startStr = getChildValue(spanNode, "start");
-		String endStr = getChildValue(spanNode, "end");
-		long[] span = Util.getTimestamps(startStr, endStr);
-		rrdGraphDef.setStartTime(span[0]);
-		rrdGraphDef.setEndTime(span[1]);
-	}
-
-	private void resolveOptions(Node rootOptionNode) throws RrdException {
-		validateTagsOnlyOnce(rootOptionNode, new String[] {
-				"anti_aliasing", "use_pool", "time_grid", "value_grid", "alt_y_grid", "alt_y_mrtg",
-				"no_minor_grid", "alt_autoscale", "alt_autoscale_max", "units_exponent", "units_length",
-				"vertical_label", "width", "height", "interlaced", "image_info", "image_format",
-				"image_quality", "background_image", "overlay_image", "unit", "lazy",
-				"min_value", "max_value", "rigid", "base", "logarithmic", "colors",
-				"no_legend", "only_graph", "force_rules_legend", "title", "step", "fonts",
-				"first_day_of_week", "signature"
-		});
-		Node[] optionNodes = getChildNodes(rootOptionNode);
-		for (Node optionNode : optionNodes) {
-			String option = optionNode.getNodeName();
-			if (option.equals("use_pool")) {
-				rrdGraphDef.setPoolUsed(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("anti_aliasing")) {
-				rrdGraphDef.setAntiAliasing(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("time_grid")) {
-				resolveTimeGrid(optionNode);
-			}
-			else if (option.equals("value_grid")) {
-				resolveValueGrid(optionNode);
-			}
-			else if (option.equals("no_minor_grid")) {
-				rrdGraphDef.setNoMinorGrid(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("alt_y_grid")) {
-				rrdGraphDef.setAltYGrid(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("alt_y_mrtg")) {
-				rrdGraphDef.setAltYMrtg(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("alt_autoscale")) {
-				rrdGraphDef.setAltAutoscale(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("alt_autoscale_max")) {
-				rrdGraphDef.setAltAutoscaleMax(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("units_exponent")) {
-				rrdGraphDef.setUnitsExponent(getValueAsInt(optionNode));
-			}
-			else if (option.equals("units_length")) {
-				rrdGraphDef.setUnitsLength(getValueAsInt(optionNode));
-			}
-			else if (option.equals("vertical_label")) {
-				rrdGraphDef.setVerticalLabel(getValue(optionNode));
-			}
-			else if (option.equals("width")) {
-				rrdGraphDef.setWidth(getValueAsInt(optionNode));
-			}
-			else if (option.equals("height")) {
-				rrdGraphDef.setHeight(getValueAsInt(optionNode));
-			}
-			else if (option.equals("interlaced")) {
-				rrdGraphDef.setInterlaced(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("image_info")) {
-				rrdGraphDef.setImageInfo(getValue(optionNode));
-			}
-			else if (option.equals("image_format")) {
-				rrdGraphDef.setImageFormat(getValue(optionNode));
-			}
-			else if (option.equals("image_quality")) {
-				rrdGraphDef.setImageQuality((float) getValueAsDouble(optionNode));
-			}
-			else if (option.equals("background_image")) {
-				rrdGraphDef.setBackgroundImage(getValue(optionNode));
-			}
-			else if (option.equals("overlay_image")) {
-				rrdGraphDef.setOverlayImage(getValue(optionNode));
-			}
-			else if (option.equals("unit")) {
-				rrdGraphDef.setUnit(getValue(optionNode));
-			}
-			else if (option.equals("lazy")) {
-				rrdGraphDef.setLazy(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("min_value")) {
-				rrdGraphDef.setMinValue(getValueAsDouble(optionNode));
-			}
-			else if (option.equals("max_value")) {
-				rrdGraphDef.setMaxValue(getValueAsDouble(optionNode));
-			}
-			else if (option.equals("rigid")) {
-				rrdGraphDef.setRigid(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("base")) {
-				rrdGraphDef.setBase(getValueAsDouble(optionNode));
-			}
-			else if (option.equals("logarithmic")) {
-				rrdGraphDef.setLogarithmic(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("colors")) {
-				resolveColors(optionNode);
-			}
-			else if (option.equals("no_legend")) {
-				rrdGraphDef.setNoLegend(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("only_graph")) {
-				rrdGraphDef.setOnlyGraph(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("force_rules_legend")) {
-				rrdGraphDef.setForceRulesLegend(getValueAsBoolean(optionNode));
-			}
-			else if (option.equals("title")) {
-				rrdGraphDef.setTitle(getValue(optionNode));
-			}
-			else if (option.equals("step")) {
-				rrdGraphDef.setStep(getValueAsLong(optionNode));
-			}
-			else if (option.equals("fonts")) {
-				resolveFonts(optionNode);
-			}
-			else if (option.equals("first_day_of_week")) {
-				int dayIndex = resolveFirstDayOfWeek(getValue(optionNode));
-				rrdGraphDef.setFirstDayOfWeek(dayIndex);
-			}
-			else if (option.equals("signature")) {
-				rrdGraphDef.setShowSignature(getValueAsBoolean(optionNode));
-			}
-		}
-	}
-
-	private int resolveFirstDayOfWeek(String firstDayOfWeek) throws RrdException {
-		if (firstDayOfWeek.equalsIgnoreCase("sunday")) {
-			return SUNDAY;
-		}
-		else if (firstDayOfWeek.equalsIgnoreCase("monday")) {
-			return MONDAY;
-		}
-		else if (firstDayOfWeek.equalsIgnoreCase("tuesday")) {
-			return TUESDAY;
-		}
-		else if (firstDayOfWeek.equalsIgnoreCase("wednesday")) {
-			return WEDNESDAY;
-		}
-		else if (firstDayOfWeek.equalsIgnoreCase("thursday")) {
-			return THURSDAY;
-		}
-		else if (firstDayOfWeek.equalsIgnoreCase("friday")) {
-			return FRIDAY;
-		}
-		else if (firstDayOfWeek.equalsIgnoreCase("saturday")) {
-			return SATURDAY;
-		}
-		throw new RrdException("Never heard for this day of week: " + firstDayOfWeek);
-	}
-
-	private void resolveFonts(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"small_font", "large_font"});
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("small_font")) {
-				rrdGraphDef.setSmallFont(resolveFont(childNode));
-			}
-			else if (nodeName.equals("large_font")) {
-				rrdGraphDef.setLargeFont(resolveFont(childNode));
-			}
-		}
-	}
-
-	private Font resolveFont(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"name", "style", "size"});
-		String name = null, style = null;
-		int size = 0;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("name")) {
-				name = getValue(childNode);
-			}
-			else if (nodeName.equals("style")) {
-				style = getValue(childNode).toLowerCase();
-			}
-			else if (nodeName.equals("size")) {
-				size = getValueAsInt(childNode);
-			}
-		}
-		if (name != null && style != null && size > 0) {
-			boolean isItalic = style.contains("italic"), isBold = style.contains("bold");
-			int fstyle = Font.PLAIN;
-			if (isItalic && isBold) {
-				fstyle = Font.BOLD + Font.ITALIC;
-			}
-			else if (isItalic) {
-				fstyle = Font.ITALIC;
-			}
-			else if (isBold) {
-				fstyle = Font.BOLD;
-			}
-			return new Font(name, fstyle, size);
-		}
-		else {
-			throw new RrdException("Incomplete font specification");
-		}
-	}
-
-	private void resolveColors(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, COLOR_NAMES);
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String colorName = childNode.getNodeName();
-			rrdGraphDef.setColor(colorName, getValueAsColor(childNode));
-		}
-	}
-
-	private void resolveValueGrid(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {"show_grid", "grid_step", "label_factor"});
-		boolean showGrid = true;
-		double gridStep = Double.NaN;
-		int NOT_SET = Integer.MIN_VALUE, labelFactor = NOT_SET;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("show_grid")) {
-				showGrid = getValueAsBoolean(childNode);
-			}
-			else if (nodeName.equals("grid_step")) {
-				gridStep = getValueAsDouble(childNode);
-			}
-			else if (nodeName.equals("label_factor")) {
-				labelFactor = getValueAsInt(childNode);
-			}
-		}
-		rrdGraphDef.setDrawYGrid(showGrid);
-		if (!Double.isNaN(gridStep) && labelFactor != NOT_SET) {
-			rrdGraphDef.setValueAxis(gridStep, labelFactor);
-		}
-		else if (!Double.isNaN(gridStep) || labelFactor != NOT_SET) {
-			throw new RrdException("Incomplete value axis settings");
-		}
-	}
-
-	private void resolveTimeGrid(Node parentNode) throws RrdException {
-		validateTagsOnlyOnce(parentNode, new String[] {
-				"show_grid", "minor_grid_unit",
-				"minor_grid_unit_count", "major_grid_unit",
-				"major_grid_unit_count", "label_unit", "label_unit_count",
-				"label_span", "label_format"
-		});
-		boolean showGrid = true;
-		final int NOT_SET = Integer.MIN_VALUE;
-		int minorGridUnit = NOT_SET, minorGridUnitCount = NOT_SET,
-				majorGridUnit = NOT_SET, majorGridUnitCount = NOT_SET,
-				labelUnit = NOT_SET, labelUnitCount = NOT_SET, labelSpan = NOT_SET;
-		String labelFormat = null;
-		Node[] childNodes = getChildNodes(parentNode);
-		for (Node childNode : childNodes) {
-			String nodeName = childNode.getNodeName();
-			if (nodeName.equals("show_grid")) {
-				showGrid = getValueAsBoolean(childNode);
-			}
-			else if (nodeName.equals("minor_grid_unit")) {
-				minorGridUnit = resolveTimeUnit(getValue(childNode));
-			}
-			else if (nodeName.equals("minor_grid_unit_count")) {
-				minorGridUnitCount = getValueAsInt(childNode);
-			}
-			else if (nodeName.equals("major_grid_unit")) {
-				majorGridUnit = resolveTimeUnit(getValue(childNode));
-			}
-			else if (nodeName.equals("major_grid_unit_count")) {
-				majorGridUnitCount = getValueAsInt(childNode);
-			}
-			else if (nodeName.equals("label_unit")) {
-				labelUnit = resolveTimeUnit(getValue(childNode));
-			}
-			else if (nodeName.equals("label_unit_count")) {
-				labelUnitCount = getValueAsInt(childNode);
-			}
-			else if (nodeName.equals("label_span")) {
-				labelSpan = getValueAsInt(childNode);
-			}
-			else if (nodeName.equals("label_format")) {
-				labelFormat = getValue(childNode);
-			}
-		}
-		rrdGraphDef.setDrawXGrid(showGrid);
-		if (minorGridUnit != NOT_SET && minorGridUnitCount != NOT_SET &&
-				majorGridUnit != NOT_SET && majorGridUnitCount != NOT_SET &&
-				labelUnit != NOT_SET && labelUnitCount != NOT_SET && labelSpan != NOT_SET && labelFormat != null) {
-			rrdGraphDef.setTimeAxis(minorGridUnit, minorGridUnitCount, majorGridUnit, majorGridUnitCount,
-					labelUnit, labelUnitCount, labelSpan, labelFormat);
-		}
-		else if (minorGridUnit != NOT_SET || minorGridUnitCount != NOT_SET ||
-				majorGridUnit != NOT_SET || majorGridUnitCount != NOT_SET ||
-				labelUnit != NOT_SET || labelUnitCount != NOT_SET || labelSpan != NOT_SET || labelFormat != null) {
-			throw new RrdException("Incomplete time axis settings");
-		}
-	}
-
-	private int resolveTimeUnit(String unit) throws RrdException {
-		if (unit.equalsIgnoreCase("second")) {
-			return RrdGraphConstants.SECOND;
-		}
-		else if (unit.equalsIgnoreCase("minute")) {
-			return RrdGraphConstants.MINUTE;
-		}
-		else if (unit.equalsIgnoreCase("hour")) {
-			return RrdGraphConstants.HOUR;
-		}
-		else if (unit.equalsIgnoreCase("day")) {
-			return RrdGraphConstants.DAY;
-		}
-		else if (unit.equalsIgnoreCase("week")) {
-			return RrdGraphConstants.WEEK;
-		}
-		else if (unit.equalsIgnoreCase("month")) {
-			return RrdGraphConstants.MONTH;
-		}
-		else if (unit.equalsIgnoreCase("year")) {
-			return RrdGraphConstants.YEAR;
-		}
-		throw new RrdException("Unknown time unit specified: " + unit);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/RrdGraphInfo.java b/apps/jrobin/java/src/org/jrobin/graph/RrdGraphInfo.java
deleted file mode 100644
index 469eb2efededf4128bee1229c2e3456129b13434..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/RrdGraphInfo.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class to represent successfully created JRobin graph. Objects of this class are created by method
- * {@link RrdGraph#getRrdGraphInfo()}.
- */
-public class RrdGraphInfo {
-	String filename;
-	int width, height;
-	byte[] bytes;
-	String imgInfo;
-	private List<String> printLines = new ArrayList<String>();
-
-	RrdGraphInfo() {
-		// cannot instantiate this class
-	}
-
-	void addPrintLine(String printLine) {
-		printLines.add(printLine);
-	}
-
-	/**
-	 * Returns filename of the graph
-	 *
-	 * @return filename of the graph. '-' denotes in-memory graph (no file created)
-	 */
-	public String getFilename() {
-		return filename;
-	}
-
-	/**
-	 * Returns total graph width
-	 *
-	 * @return total graph width
-	 */
-	public int getWidth() {
-		return width;
-	}
-
-	/**
-	 * Returns total graph height
-	 *
-	 * @return total graph height
-	 */
-	public int getHeight() {
-		return height;
-	}
-
-	/**
-	 * Returns graph bytes
-	 *
-	 * @return Graph bytes
-	 */
-	public byte[] getBytes() {
-		return bytes;
-	}
-
-	/**
-	 * Returns PRINT lines requested by {@link RrdGraphDef#print(String, String, String)} method.
-	 *
-	 * @return An array of formatted PRINT lines
-	 */
-	public String[] getPrintLines() {
-		return printLines.toArray(new String[printLines.size()]);
-	}
-
-	/**
-	 * Returns image information requested by {@link RrdGraphDef#setImageInfo(String)} method
-	 *
-	 * @return Image information
-	 */
-	public String getImgInfo() {
-		return imgInfo;
-	}
-
-	/**
-	 * Returns the number of bytes in the graph file
-	 *
-	 * @return Length of the graph file
-	 */
-	public int getByteCount() {
-		return bytes != null ? bytes.length : 0;
-	}
-
-	/**
-	 * Dumps complete graph information. Useful for debugging purposes.
-	 *
-	 * @return String containing complete graph information
-	 */
-	public String dump() {
-		StringBuffer b = new StringBuffer();
-		b.append("filename = \"").append(getFilename()).append("\"\n");
-		b.append("width = ").append(getWidth()).append(", height = ").append(getHeight()).append("\n");
-		b.append("byteCount = ").append(getByteCount()).append("\n");
-		b.append("imginfo = \"").append(getImgInfo()).append("\"\n");
-		String[] plines = getPrintLines();
-		if (plines.length == 0) {
-			b.append("No print lines found\n");
-		}
-		else {
-			for (int i = 0; i < plines.length; i++) {
-				b.append("print[").append(i).append("] = \"").append(plines[i]).append("\"\n");
-			}
-		}
-		return b.toString();
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/Rule.java b/apps/jrobin/java/src/org/jrobin/graph/Rule.java
deleted file mode 100644
index 3dccdad8ca22d1301e6eafc7b148b10a500f3043..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/Rule.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-
-class Rule extends PlotElement {
-	final LegendText legend;
-	final float width;
-
-	Rule(Paint color, LegendText legend, float width) {
-		super(color);
-		this.legend = legend;
-		this.width = width;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/SDef.java b/apps/jrobin/java/src/org/jrobin/graph/SDef.java
deleted file mode 100644
index 09f857b76d5270a2c377f79112f82b1e9ed9d72a..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/SDef.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.data.DataProcessor;
-
-class SDef extends Source {
-	private String defName, consolFun;
-
-	SDef(String name, String defName, String consolFun) {
-		super(name);
-		this.defName = defName;
-		this.consolFun = consolFun;
-	}
-
-	void requestData(DataProcessor dproc) {
-		dproc.addDatasource(name, defName, consolFun);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/Source.java b/apps/jrobin/java/src/org/jrobin/graph/Source.java
deleted file mode 100644
index 733c9c464d0aab680f9056bf22065bf7c5b999dd..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/Source.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.data.DataProcessor;
-
-abstract class Source {
-	final String name;
-
-	Source(String name) {
-		this.name = name;
-	}
-
-	abstract void requestData(DataProcessor dproc);
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/SourcedPlotElement.java b/apps/jrobin/java/src/org/jrobin/graph/SourcedPlotElement.java
deleted file mode 100644
index fdcd81e24b1a7b211c9ed61441a685e7b2e4ffe4..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/SourcedPlotElement.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.core.Util;
-import org.jrobin.data.DataProcessor;
-
-import java.awt.*;
-
-class SourcedPlotElement extends PlotElement {
-	final String srcName;
-	double[] values;
-
-	SourcedPlotElement(String srcName, Paint color) {
-		super(color);
-		this.srcName = srcName;
-	}
-
-	void assignValues(DataProcessor dproc) throws RrdException {
-		values = dproc.getValues(srcName);
-	}
-
-	double[] getValues() {
-		return values;
-	}
-
-	double getMinValue() {
-		return Util.min(values);
-	}
-
-	double getMaxValue() {
-		return Util.max(values);
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/Stack.java b/apps/jrobin/java/src/org/jrobin/graph/Stack.java
deleted file mode 100644
index a4eaff55bc5d21f7d9d6aa92caf84bdba02331e3..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/Stack.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.RrdException;
-import org.jrobin.data.DataProcessor;
-
-import java.awt.*;
-
-class Stack extends SourcedPlotElement {
-	private final SourcedPlotElement parent;
-
-	Stack(SourcedPlotElement parent, String srcName, Paint color) {
-		super(srcName, color);
-		this.parent = parent;
-	}
-
-	void assignValues(DataProcessor dproc) throws RrdException {
-		double[] parentValues = parent.getValues();
-		double[] procValues = dproc.getValues(srcName);
-		values = new double[procValues.length];
-		for (int i = 0; i < values.length; i++) {
-			values[i] = parentValues[i] + procValues[i];
-		}
-	}
-
-	float getParentLineWidth() {
-		if (parent instanceof Line) {
-			return ((Line) parent).width;
-		}
-		else if (parent instanceof Area) {
-			return -1F;
-		}
-		else /* if(parent instanceof Stack) */ {
-			return ((Stack) parent).getParentLineWidth();
-		}
-	}
-
-	Paint getParentColor() {
-		return parent.color;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/TimeAxis.java b/apps/jrobin/java/src/org/jrobin/graph/TimeAxis.java
deleted file mode 100644
index 5a19f851dd7c543ce3ffebd0be394200ab93a743..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/TimeAxis.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Locale;
-
-class TimeAxis implements RrdGraphConstants {
-	private static final TimeAxisSetting[] tickSettings = {
-			new TimeAxisSetting(0, SECOND, 30, MINUTE, 5, MINUTE, 5, 0, "HH:mm"),
-			new TimeAxisSetting(2, MINUTE, 1, MINUTE, 5, MINUTE, 5, 0, "HH:mm"),
-			new TimeAxisSetting(5, MINUTE, 2, MINUTE, 10, MINUTE, 10, 0, "HH:mm"),
-			new TimeAxisSetting(10, MINUTE, 5, MINUTE, 20, MINUTE, 20, 0, "HH:mm"),
-			new TimeAxisSetting(30, MINUTE, 10, HOUR, 1, HOUR, 1, 0, "HH:mm"),
-			new TimeAxisSetting(60, MINUTE, 30, HOUR, 2, HOUR, 2, 0, "HH:mm"),
-			new TimeAxisSetting(180, HOUR, 1, HOUR, 6, HOUR, 6, 0, "HH:mm"),
-			new TimeAxisSetting(600, HOUR, 6, DAY, 1, DAY, 1, 24 * 3600, "EEE"),
-			new TimeAxisSetting(1800, HOUR, 12, DAY, 1, DAY, 2, 24 * 3600, "EEE"),
-			new TimeAxisSetting(3600, DAY, 1, WEEK, 1, WEEK, 1, 7 * 24 * 3600, "'Week 'w"),
-			new TimeAxisSetting(3 * 3600, WEEK, 1, MONTH, 1, WEEK, 2, 7 * 24 * 3600, "'Week 'w"),
-			new TimeAxisSetting(6 * 3600, MONTH, 1, MONTH, 1, MONTH, 1, 30 * 24 * 3600, "MMM"),
-			new TimeAxisSetting(48 * 3600, MONTH, 1, MONTH, 3, MONTH, 3, 30 * 24 * 3600, "MMM"),
-			new TimeAxisSetting(10 * 24 * 3600, YEAR, 1, YEAR, 1, YEAR, 1, 365 * 24 * 3600, "yy"),
-			new TimeAxisSetting(-1, MONTH, 0, MONTH, 0, MONTH, 0, 0, "")
-	};
-
-	private TimeAxisSetting tickSetting;
-	private RrdGraph rrdGraph;
-	private double secPerPix;
-	private Calendar calendar;
-
-	TimeAxis(RrdGraph rrdGraph) {
-		this.rrdGraph = rrdGraph;
-		if (rrdGraph.im.xsize > 0) {
-			this.secPerPix = (rrdGraph.im.end - rrdGraph.im.start) / Double.valueOf(rrdGraph.im.xsize);
-		}
-		this.calendar = Calendar.getInstance(Locale.getDefault());
-		this.calendar.setFirstDayOfWeek(rrdGraph.gdef.firstDayOfWeek);
-	}
-
-	void draw() {
-		chooseTickSettings();
-		if (tickSetting == null) {
-			return;
-		}
-		drawMinor();
-		drawMajor();
-		drawLabels();
-	}
-
-	private void drawMinor() {
-		if (!rrdGraph.gdef.noMinorGrid) {
-			adjustStartingTime(tickSetting.minorUnit, tickSetting.minorUnitCount);
-			Paint color = rrdGraph.gdef.colors[COLOR_GRID];
-			int y0 = rrdGraph.im.yorigin, y1 = y0 - rrdGraph.im.ysize;
-			for (int status = getTimeShift(); status <= 0; status = getTimeShift()) {
-				if (status == 0) {
-					long time = calendar.getTime().getTime() / 1000L;
-					int x = rrdGraph.mapper.xtr(time);
-					rrdGraph.worker.drawLine(x, y0 - 1, x, y0 + 1, color, TICK_STROKE);
-					rrdGraph.worker.drawLine(x, y0, x, y1, color, GRID_STROKE);
-				}
-				findNextTime(tickSetting.minorUnit, tickSetting.minorUnitCount);
-			}
-		}
-	}
-
-	private void drawMajor() {
-		adjustStartingTime(tickSetting.majorUnit, tickSetting.majorUnitCount);
-		Paint color = rrdGraph.gdef.colors[COLOR_MGRID];
-		int y0 = rrdGraph.im.yorigin, y1 = y0 - rrdGraph.im.ysize;
-		for (int status = getTimeShift(); status <= 0; status = getTimeShift()) {
-			if (status == 0) {
-				long time = calendar.getTime().getTime() / 1000L;
-				int x = rrdGraph.mapper.xtr(time);
-				rrdGraph.worker.drawLine(x, y0 - 2, x, y0 + 2, color, TICK_STROKE);
-				rrdGraph.worker.drawLine(x, y0, x, y1, color, GRID_STROKE);
-			}
-			findNextTime(tickSetting.majorUnit, tickSetting.majorUnitCount);
-		}
-	}
-
-	private void drawLabels() {
-		// escape strftime like format string
-		String labelFormat = tickSetting.format.replaceAll("([^%]|^)%([^%t])", "$1%t$2");
-		Font font = rrdGraph.gdef.getFont(FONTTAG_AXIS);
-		Paint color = rrdGraph.gdef.colors[COLOR_FONT];
-		adjustStartingTime(tickSetting.labelUnit, tickSetting.labelUnitCount);
-		int y = rrdGraph.im.yorigin + (int) rrdGraph.worker.getFontHeight(font) + 2;
-		for (int status = getTimeShift(); status <= 0; status = getTimeShift()) {
-			String label = formatLabel(labelFormat, calendar.getTime());
-			long time = calendar.getTime().getTime() / 1000L;
-			int x1 = rrdGraph.mapper.xtr(time);
-			int x2 = rrdGraph.mapper.xtr(time + tickSetting.labelSpan);
-			int labelWidth = (int) rrdGraph.worker.getStringWidth(label, font);
-			int x = x1 + (x2 - x1 - labelWidth) / 2;
-			if (x >= rrdGraph.im.xorigin && x + labelWidth <= rrdGraph.im.xorigin + rrdGraph.im.xsize) {
-				rrdGraph.worker.drawString(label, x, y, font, color);
-			}
-			findNextTime(tickSetting.labelUnit, tickSetting.labelUnitCount);
-		}
-	}
-
-	private static String formatLabel(String format, Date date) {
-		if (format.contains("%")) {
-			// strftime like format string
-			return String.format(format, date);
-		}
-		else {
-			// simple date format
-			return new SimpleDateFormat(format).format(date);
-		}
-	}
-
-	private void findNextTime(int timeUnit, int timeUnitCount) {
-		switch (timeUnit) {
-			case SECOND:
-				calendar.add(Calendar.SECOND, timeUnitCount);
-				break;
-			case MINUTE:
-				calendar.add(Calendar.MINUTE, timeUnitCount);
-				break;
-			case HOUR:
-				calendar.add(Calendar.HOUR_OF_DAY, timeUnitCount);
-				break;
-			case DAY:
-				calendar.add(Calendar.DAY_OF_MONTH, timeUnitCount);
-				break;
-			case WEEK:
-				calendar.add(Calendar.DAY_OF_MONTH, 7 * timeUnitCount);
-				break;
-			case MONTH:
-				calendar.add(Calendar.MONTH, timeUnitCount);
-				break;
-			case YEAR:
-				calendar.add(Calendar.YEAR, timeUnitCount);
-				break;
-		}
-	}
-
-	private int getTimeShift() {
-		long time = calendar.getTime().getTime() / 1000L;
-		return (time < rrdGraph.im.start) ? -1 : (time > rrdGraph.im.end) ? +1 : 0;
-	}
-
-	private void adjustStartingTime(int timeUnit, int timeUnitCount) {
-		calendar.setTime(new Date(rrdGraph.im.start * 1000L));
-		switch (timeUnit) {
-			case SECOND:
-				calendar.add(Calendar.SECOND, -(calendar.get(Calendar.SECOND) % timeUnitCount));
-				break;
-			case MINUTE:
-				calendar.set(Calendar.SECOND, 0);
-				calendar.add(Calendar.MINUTE, -(calendar.get(Calendar.MINUTE) % timeUnitCount));
-				break;
-			case HOUR:
-				calendar.set(Calendar.SECOND, 0);
-				calendar.set(Calendar.MINUTE, 0);
-				calendar.add(Calendar.HOUR_OF_DAY, -(calendar.get(Calendar.HOUR_OF_DAY) % timeUnitCount));
-				break;
-			case DAY:
-				calendar.set(Calendar.SECOND, 0);
-				calendar.set(Calendar.MINUTE, 0);
-				calendar.set(Calendar.HOUR_OF_DAY, 0);
-				break;
-			case WEEK:
-				calendar.set(Calendar.SECOND, 0);
-				calendar.set(Calendar.MINUTE, 0);
-				calendar.set(Calendar.HOUR_OF_DAY, 0);
-				int diffDays = calendar.get(Calendar.DAY_OF_WEEK) - calendar.getFirstDayOfWeek();
-				if (diffDays < 0) {
-					diffDays += 7;
-				}
-				calendar.add(Calendar.DAY_OF_MONTH, -diffDays);
-				break;
-			case MONTH:
-				calendar.set(Calendar.SECOND, 0);
-				calendar.set(Calendar.MINUTE, 0);
-				calendar.set(Calendar.HOUR_OF_DAY, 0);
-				calendar.set(Calendar.DAY_OF_MONTH, 1);
-				calendar.add(Calendar.MONTH, -(calendar.get(Calendar.MONTH) % timeUnitCount));
-				break;
-			case YEAR:
-				calendar.set(Calendar.SECOND, 0);
-				calendar.set(Calendar.MINUTE, 0);
-				calendar.set(Calendar.HOUR_OF_DAY, 0);
-				calendar.set(Calendar.DAY_OF_MONTH, 1);
-				calendar.set(Calendar.MONTH, 0);
-				calendar.add(Calendar.YEAR, -(calendar.get(Calendar.YEAR) % timeUnitCount));
-				break;
-		}
-	}
-
-
-	private void chooseTickSettings() {
-		if (rrdGraph.gdef.timeAxisSetting != null) {
-			tickSetting = new TimeAxisSetting(rrdGraph.gdef.timeAxisSetting);
-		}
-		else {
-			for (int i = 0; tickSettings[i].secPerPix >= 0 && secPerPix > tickSettings[i].secPerPix; i++) {
-				tickSetting = tickSettings[i];
-			}
-		}
-	}
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/TimeAxisSetting.java b/apps/jrobin/java/src/org/jrobin/graph/TimeAxisSetting.java
deleted file mode 100644
index ab955e77378cff999c8a333d987571f2c6d7384f..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/TimeAxisSetting.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-class TimeAxisSetting {
-	final long secPerPix;
-	final int minorUnit, minorUnitCount, majorUnit, majorUnitCount;
-	final int labelUnit, labelUnitCount, labelSpan;
-	final String format;
-
-	TimeAxisSetting(long secPerPix, int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
-					int labelUnit, int labelUnitCount, int labelSpan, String format) {
-		this.secPerPix = secPerPix;
-		this.minorUnit = minorUnit;
-		this.minorUnitCount = minorUnitCount;
-		this.majorUnit = majorUnit;
-		this.majorUnitCount = majorUnitCount;
-		this.labelUnit = labelUnit;
-		this.labelUnitCount = labelUnitCount;
-		this.labelSpan = labelSpan;
-		this.format = format;
-	}
-
-	TimeAxisSetting(TimeAxisSetting s) {
-		this.secPerPix = s.secPerPix;
-		this.minorUnit = s.minorUnit;
-		this.minorUnitCount = s.minorUnitCount;
-		this.majorUnit = s.majorUnit;
-		this.majorUnitCount = s.majorUnitCount;
-		this.labelUnit = s.labelUnit;
-		this.labelUnitCount = s.labelUnitCount;
-		this.labelSpan = s.labelSpan;
-		this.format = s.format;
-	}
-
-	TimeAxisSetting(int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
-					int labelUnit, int labelUnitCount, int labelSpan, String format) {
-		this(0, minorUnit, minorUnitCount, majorUnit, majorUnitCount,
-				labelUnit, labelUnitCount, labelSpan, format);
-	}
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/VRule.java b/apps/jrobin/java/src/org/jrobin/graph/VRule.java
deleted file mode 100644
index a3178b18044ff6a083fd725cb8cfe1454237dd5c..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/VRule.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.*;
-
-class VRule extends Rule {
-	final long timestamp;
-
-	VRule(long timestamp, Paint color, LegendText legend, float width) {
-		super(color, legend, width);
-		this.timestamp = timestamp;
-	}
-
-	void setLegendVisibility(long minval, long maxval, boolean forceLegend) {
-		legend.enabled &= (forceLegend || (timestamp >= minval && timestamp <= maxval));
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/ValueAxis.java b/apps/jrobin/java/src/org/jrobin/graph/ValueAxis.java
deleted file mode 100644
index 29e4e0dedffb3133e16dbdfec81b64d043f36115..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/ValueAxis.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import java.awt.Font;
-import java.awt.Paint;
-
-import org.jrobin.core.Util;
-
-class ValueAxis implements RrdGraphConstants {
-	private static final YLab[] ylab = {
-		new YLab(0.1, 1, 2, 5, 10),
-		new YLab(0.2, 1, 5, 10, 20),
-		new YLab(0.5, 1, 2, 4, 10),
-		new YLab(1.0, 1, 2, 5, 10),
-		new YLab(2.0, 1, 5, 10, 20),
-		new YLab(5.0, 1, 2, 4, 10),
-		new YLab(10.0, 1, 2, 5, 10),
-		new YLab(20.0, 1, 5, 10, 20),
-		new YLab(50.0, 1, 2, 4, 10),
-		new YLab(100.0, 1, 2, 5, 10),
-		new YLab(200.0, 1, 5, 10, 20),
-		new YLab(500.0, 1, 2, 4, 10),
-		new YLab(1000.0, 1, 2, 5, 10),
-		new YLab(2000.0, 1, 5, 10, 20),
-		new YLab(5000.0, 1, 2, 4, 10),
-		new YLab(10000.0, 1, 2, 5, 10),
-		new YLab(20000.0, 1, 5, 10, 20),
-		new YLab(50000.0, 1, 2, 4, 10),
-		new YLab(100000.0, 1, 2, 5, 10),
-		new YLab(0.0, 0, 0, 0, 0)
-	};
-
-	//private RrdGraph rrdGraph;
-	private ImageParameters im;
-	private ImageWorker worker;
-	private RrdGraphDef gdef;
-	private Mapper mapper;
-
-	ValueAxis(RrdGraph rrdGraph) {
-		this(rrdGraph.im, rrdGraph.worker, rrdGraph.gdef, rrdGraph.mapper);
-	}
-
-	ValueAxis(ImageParameters im, ImageWorker worker, RrdGraphDef gdef, Mapper mapper) {
-		this.im = im;
-		this.gdef = gdef;
-		this.worker = worker;
-		this.mapper = mapper;
-	}
-
-	boolean draw() {
-		Font font = gdef.getFont(FONTTAG_AXIS);
-		Paint gridColor = gdef.colors[COLOR_GRID];
-		Paint mGridColor = gdef.colors[COLOR_MGRID];
-		Paint fontColor = gdef.colors[COLOR_FONT];
-		int labelOffset = (int) (worker.getFontAscent(font) / 2);
-		int labfact = 2;
-		double range = im.maxval - im.minval;
-		double scaledrange = range / im.magfact;
-		double gridstep;
-		if (Double.isNaN(scaledrange)) {
-			return false;
-		}
-		String labfmt = null;
-		if (Double.isNaN(im.ygridstep)) {
-			if (gdef.altYGrid) {
-				/* find the value with max number of digits. Get number of digits */
-				int decimals = (int) Math.ceil(Math.log10(Math.max(Math.abs(im.maxval),
-						Math.abs(im.minval))));
-				if (decimals <= 0) /* everything is small. make place for zero */ {
-					decimals = 1;
-				}
-				int fractionals = (int) Math.floor(Math.log10(range));
-				if (fractionals < 0) /* small amplitude. */ {
-					labfmt = Util.sprintf("%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
-				}
-				else {
-					labfmt = Util.sprintf("%%%d.1f", decimals + 1);
-				}
-				gridstep = Math.pow(10, fractionals);
-				if (gridstep == 0) /* range is one -> 0.1 is reasonable scale */ {
-					gridstep = 0.1;
-				}
-				/* should have at least 5 lines but no more then 15 */
-				if (range / gridstep < 5) {
-					gridstep /= 10;
-				}
-				if (range / gridstep > 15) {
-					gridstep *= 10;
-				}
-				if (range / gridstep > 5) {
-					labfact = 1;
-					if (range / gridstep > 8) {
-						labfact = 2;
-					}
-				}
-				else {
-					gridstep /= 5;
-					labfact = 5;
-				}
-			}
-			else {
-				//Start looking for a minimum of 3 labels, but settle for 2 or 1 if need be
-				int minimumLabelCount = 3;
-				YLab selectedYLab = null;
-				while(selectedYLab == null) {
-					selectedYLab = findYLab(minimumLabelCount);
-					minimumLabelCount--;
-				}
-				gridstep = selectedYLab.grid * im.magfact;
-				labfact = findLabelFactor(selectedYLab);
-			}
-		}
-		else {
-			gridstep = im.ygridstep;
-			labfact = im.ylabfact;
-		}
-		int x0 = im.xorigin, x1 = x0 + im.xsize;
-		int sgrid = (int) (im.minval / gridstep - 1);
-		int egrid = (int) (im.maxval / gridstep + 1);
-		double scaledstep = gridstep / im.magfact;
-		for (int i = sgrid; i <= egrid; i++) {
-			int y = this.mapper.ytr(gridstep * i);
-			if (y >= im.yorigin - im.ysize && y <= im.yorigin) {
-				if (i % labfact == 0) {
-					String graph_label;
-					if (i == 0 || im.symbol == ' ') {
-						if (scaledstep < 1) {
-							if (i != 0 && gdef.altYGrid) {
-								graph_label = Util.sprintf(labfmt, scaledstep * i);
-							}
-							else {
-								graph_label = Util.sprintf("%4.1f", scaledstep * i);
-							}
-						}
-						else {
-							graph_label = Util.sprintf("%4.0f", scaledstep * i);
-						}
-					}
-					else {
-						if (scaledstep < 1) {
-							graph_label = Util.sprintf("%4.1f %c", scaledstep * i, im.symbol);
-						}
-						else {
-							graph_label = Util.sprintf("%4.0f %c", scaledstep * i, im.symbol);
-						}
-					}
-					int length = (int) (worker.getStringWidth(graph_label, font));
-					worker.drawString(graph_label, x0 - length - PADDING_VLABEL, y + labelOffset, font, fontColor);
-					worker.drawLine(x0 - 2, y, x0 + 2, y, mGridColor, TICK_STROKE);
-					worker.drawLine(x1 - 2, y, x1 + 2, y, mGridColor, TICK_STROKE);
-					worker.drawLine(x0, y, x1, y, mGridColor, GRID_STROKE);
-				}
-				else if (!(gdef.noMinorGrid)) {
-					worker.drawLine(x0 - 1, y, x0 + 1, y, gridColor, TICK_STROKE);
-					worker.drawLine(x1 - 1, y, x1 + 1, y, gridColor, TICK_STROKE);
-					worker.drawLine(x0, y, x1, y, gridColor, GRID_STROKE);
-				}
-			}
-		}
-		return true;
-	}
-
-/**
-* Finds an acceptable YLab object for the current graph
-* If the graph covers positive and negative on the y-axis, then
-* desiredMinimumLabelCount is checked as well, to ensure the chosen YLab definition
-        * will result in the required number of labels
-*
-	* Returns null if none are acceptable (none the right size or with
-* enough labels)
-*/
-private YLab findYLab(int desiredMinimumLabelCount) {
-	double scaledrange = this.getScaledRange();
-	int labelFactor;
-	//Check each YLab definition to see if it's acceptable
-	for (int i = 0; ylab[i].grid > 0; i++) {
-		YLab thisYLab = ylab[i];
-		//First cut is whether this gridstep would give enough space per gridline
-		if (this.getPixelsPerGridline(thisYLab) > 5 ) {
-			//Yep; now we might have to check the number of labels
-			if(im.minval < 0.0 && im.maxval > 0.0) {
-				//The graph covers positive and negative values, so we need the
-				// desiredMinimumLabelCount number of labels, which is going to
-				// usually be 3, then maybe 2, then only as a last resort, 1.
-				// So, we need to find out what the label factor would be
-				// if we chose this ylab definition
-				labelFactor = findLabelFactor(thisYLab);
-				if(labelFactor == -1) {
-					//Default to too many to satisfy the label count test, unless we're looking for just 1
-					// in which case be sure to satisfy the label count test
-					labelFactor = desiredMinimumLabelCount==1?1:desiredMinimumLabelCount+1;
-				}
-				//Adding one?  Think fenceposts (need one more than just dividing length by space between)
-				int labelCount = ((int)(scaledrange/thisYLab.grid)/labelFactor)+1;
-				if(labelCount > desiredMinimumLabelCount) {
-					return thisYLab; //Enough pixels, *and* enough labels
-				}
-
-			} else {
-				//Only positive or negative on the graph y-axis.  No need to
-				// care about the label count.
-				return thisYLab;
-			}
-		}
-	}
-
-	double val = 1;
-	while(val < scaledrange) {
-	    val = val * 10;
-	}
-	return new YLab(val/10, 1, 2, 5, 10);
-}
-
-/**
-	 * Find the smallest labelFactor acceptable (can fit labels) for the given YLab definition
- * Returns the label factor if one is ok, otherwise returns -1 if none are acceptable
- */
-private int findLabelFactor(YLab thisYLab) {
-	int pixel = this.getPixelsPerGridline(thisYLab);
-	int fontHeight = (int) Math.ceil(worker.getFontHeight(gdef.getFont(FONTTAG_AXIS)));
-	for (int j = 0; j < 4; j++) {
-		if (pixel * thisYLab.lfac[j] >= 2 * fontHeight) {
-			return thisYLab.lfac[j];
-		}
-	}
-	return -1;
-}
-
-/**
- * Finds the number of pixels per gridline that the given YLab definition will result in
- */
-private int getPixelsPerGridline(YLab thisYLab) {
-	double scaledrange = this.getScaledRange();
-	return (int) (im.ysize / (scaledrange / thisYLab.grid));
-}
-
-private double getScaledRange() {
-	double range = im.maxval - im.minval;
-	return range / im.magfact;
-}
-
-
-	static class YLab {
-		double grid;
-		int[] lfac;
-
-		YLab(double grid, int lfac1, int lfac2, int lfac3, int lfac4) {
-			this.grid = grid;
-			lfac = new int[] {lfac1, lfac2, lfac3, lfac4};
-		}
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/ValueAxisLogarithmic.java b/apps/jrobin/java/src/org/jrobin/graph/ValueAxisLogarithmic.java
deleted file mode 100644
index 68ffc0f453ed616100180d7c168cc9d18a8b0635..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/ValueAxisLogarithmic.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.Util;
-
-import java.awt.*;
-
-class ValueAxisLogarithmic implements RrdGraphConstants {
-	private static final double[][] yloglab = {
-			{1e9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-			{1e3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-			{1e1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-			/* {  1e1, 1,  5,  0,  0,  0,  0,  0,  0,  0,  0,  0 }, */
-			{1e1, 1, 2.5, 5, 7.5, 0, 0, 0, 0, 0, 0, 0},
-			{1e1, 1, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0},
-			{1e1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0},
-			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-	};
-
-	private RrdGraph rrdGraph;
-	private ImageParameters im;
-	private ImageWorker worker;
-	private RrdGraphDef gdef;
-
-	ValueAxisLogarithmic(RrdGraph rrdGraph) {
-		this.rrdGraph = rrdGraph;
-		this.im = rrdGraph.im;
-		this.gdef = rrdGraph.gdef;
-		this.worker = rrdGraph.worker;
-	}
-
-	boolean draw() {
-		Font font = gdef.getFont(FONTTAG_AXIS);
-		Paint gridColor = gdef.colors[COLOR_GRID];
-		Paint mGridColor = gdef.colors[COLOR_MGRID];
-		Paint fontColor = gdef.colors[COLOR_FONT];
-		int fontHeight = (int) Math.ceil(worker.getFontHeight(font));
-		int labelOffset = (int) (worker.getFontAscent(font) / 2);
-
-		double pixpex = (double) im.ysize / (Math.log10(im.maxval) - Math.log10(im.minval));
-		if (Double.isNaN(pixpex)) {
-			return false;
-		}
-		double minstep, pixperstep;
-		int minoridx = 0, majoridx = 0;
-		for (int i = 0; yloglab[i][0] > 0; i++) {
-			minstep = Math.log10(yloglab[i][0]);
-			for (int ii = 1; yloglab[i][ii + 1] > 0; ii++) {
-				if (yloglab[i][ii + 2] == 0) {
-					minstep = Math.log10(yloglab[i][ii + 1]) - Math.log10(yloglab[i][ii]);
-					break;
-				}
-			}
-			pixperstep = pixpex * minstep;
-			if (pixperstep > 5) {
-				minoridx = i;
-			}
-			if (pixperstep > 2 * fontHeight) {
-				majoridx = i;
-			}
-		}
-		int x0 = im.xorigin, x1 = x0 + im.xsize;
-		for (double value = Math.pow(10, Math.log10(im.minval)
-				- Math.log10(im.minval) % Math.log10(yloglab[minoridx][0]));
-			 value <= im.maxval;
-			 value *= yloglab[minoridx][0]) {
-			if (value < im.minval) {
-				continue;
-			}
-			int i = 0;
-			while (yloglab[minoridx][++i] > 0) {
-				int y = rrdGraph.mapper.ytr(value * yloglab[minoridx][i]);
-				if (y <= im.yorigin - im.ysize) {
-					break;
-				}
-				worker.drawLine(x0 - 1, y, x0 + 1, y, gridColor, TICK_STROKE);
-				worker.drawLine(x1 - 1, y, x1 + 1, y, gridColor, TICK_STROKE);
-				worker.drawLine(x0, y, x1, y, gridColor, GRID_STROKE);
-			}
-		}
-		for (double value = Math.pow(10, Math.log10(im.minval)
-				- (Math.log10(im.minval) % Math.log10(yloglab[majoridx][0])));
-			 value <= im.maxval;
-			 value *= yloglab[majoridx][0]) {
-			if (value < im.minval) {
-				continue;
-			}
-			int i = 0;
-			while (yloglab[majoridx][++i] > 0) {
-				int y = rrdGraph.mapper.ytr(value * yloglab[majoridx][i]);
-				if (y <= im.yorigin - im.ysize) {
-					break;
-				}
-				worker.drawLine(x0 - 2, y, x0 + 2, y, mGridColor, TICK_STROKE);
-				worker.drawLine(x1 - 2, y, x1 + 2, y, mGridColor, TICK_STROKE);
-				worker.drawLine(x0, y, x1, y, mGridColor, GRID_STROKE);
-				String graph_label = Util.sprintf("%3.0e", value * yloglab[majoridx][i]);
-				int length = (int) (worker.getStringWidth(graph_label, font));
-				worker.drawString(graph_label, x0 - length - PADDING_VLABEL, y + labelOffset, font, fontColor);
-			}
-		}
-		return true;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/ValueAxisMrtg.java b/apps/jrobin/java/src/org/jrobin/graph/ValueAxisMrtg.java
deleted file mode 100644
index 9788519ccfd9fbd03ab23288969b6a8199a6be61..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/ValueAxisMrtg.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-import org.jrobin.core.Util;
-
-import java.awt.*;
-
-class ValueAxisMrtg implements RrdGraphConstants {
-	private ImageParameters im;
-	private ImageWorker worker;
-	private RrdGraphDef gdef;
-
-	ValueAxisMrtg(RrdGraph rrdGraph) {
-		this.im = rrdGraph.im;
-		this.gdef = rrdGraph.gdef;
-		this.worker = rrdGraph.worker;
-		im.unit = gdef.unit;
-	}
-
-	boolean draw() {
-		Font font = gdef.getFont(FONTTAG_AXIS);
-		Paint mGridColor = gdef.colors[COLOR_MGRID];
-		Paint fontColor = gdef.colors[COLOR_FONT];
-		int labelOffset = (int) (worker.getFontAscent(font) / 2);
-
-		if (Double.isNaN((im.maxval - im.minval) / im.magfact)) {
-			return false;
-		}
-
-		int xLeft = im.xorigin;
-		int xRight = im.xorigin + im.xsize;
-		String labfmt;
-		if (im.scaledstep / im.magfact * Math.max(Math.abs(im.quadrant), Math.abs(4 - im.quadrant)) <= 1.0) {
-			labfmt = "%5.2f";
-		}
-		else {
-			labfmt = Util.sprintf("%%4.%df", 1 - ((im.scaledstep / im.magfact > 10.0 || Math.ceil(im.scaledstep / im.magfact) == im.scaledstep / im.magfact) ? 1 : 0));
-		}
-		if (im.symbol != ' ' || im.unit != null) {
-			labfmt += " ";
-		}
-		if (im.symbol != ' ') {
-			labfmt += im.symbol;
-		}
-		if (im.unit != null) {
-			labfmt += im.unit;
-		}
-		for (int i = 0; i <= 4; i++) {
-			int y = im.yorigin - im.ysize * i / 4;
-			if (y >= im.yorigin - im.ysize && y <= im.yorigin) {
-				String graph_label = Util.sprintf(labfmt, im.scaledstep / im.magfact * (i - im.quadrant));
-				int length = (int) (worker.getStringWidth(graph_label, font));
-				worker.drawString(graph_label, xLeft - length - PADDING_VLABEL, y + labelOffset, font, fontColor);
-				worker.drawLine(xLeft - 2, y, xLeft + 2, y, mGridColor, TICK_STROKE);
-				worker.drawLine(xRight - 2, y, xRight + 2, y, mGridColor, TICK_STROKE);
-				worker.drawLine(xLeft, y, xRight, y, mGridColor, GRID_STROKE);
-			}
-		}
-		return true;
-	}
-
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/ValueAxisSetting.java b/apps/jrobin/java/src/org/jrobin/graph/ValueAxisSetting.java
deleted file mode 100644
index 272763438c186309da460eca85300da09a4b7add..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/ValueAxisSetting.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-class ValueAxisSetting {
-	final double gridStep;
-	final int labelFactor;
-
-	ValueAxisSetting(double gridStep, int labelFactor) {
-		this.gridStep = gridStep;
-		this.labelFactor = labelFactor;
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/ValueScaler.java b/apps/jrobin/java/src/org/jrobin/graph/ValueScaler.java
deleted file mode 100644
index a63a76e6c47819e7e3f96afc589495666f9ab0ef..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/ValueScaler.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
- * Copyright (c) 2011 The OpenNMS Group, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *******************************************************************************/
-package org.jrobin.graph;
-
-class ValueScaler {
-	static final String UNIT_UNKNOWN = "?";
-	static final String UNIT_SYMBOLS[] = {
-			"a", "f", "p", "n", "u", "m", " ", "k", "M", "G", "T", "P", "E"
-	};
-	static final int SYMB_CENTER = 6;
-
-	private final double base;
-	private double magfact = -1; // nothing scaled before, rescale
-	private String unit;
-
-	ValueScaler(double base) {
-		this.base = base;
-	}
-
-	Scaled scale(double value, boolean mustRescale) {
-		Scaled scaled;
-		if (mustRescale) {
-			scaled = rescale(value);
-		}
-		else if (magfact >= 0) {
-			// already scaled, need not rescale
-			scaled = new Scaled(value / magfact, unit);
-		}
-		else {
-			// scaling not requested, but never scaled before - must rescale anyway
-			scaled = rescale(value);
-			// if zero, scale again on the next try
-			if (scaled.value == 0.0 || Double.isNaN(scaled.value)) {
-				magfact = -1.0;
-			}
-		}
-		return scaled;
-	}
-
-	private Scaled rescale(double value) {
-		int sindex;
-		if (value == 0.0 || Double.isNaN(value)) {
-			sindex = 0;
-			magfact = 1.0;
-		}
-		else {
-			sindex = (int) (Math.floor(Math.log(Math.abs(value)) / Math.log(base)));
-			magfact = Math.pow(base, sindex);
-		}
-		if (sindex <= SYMB_CENTER && sindex >= -SYMB_CENTER) {
-			unit = UNIT_SYMBOLS[sindex + SYMB_CENTER];
-		}
-		else {
-			unit = UNIT_UNKNOWN;
-		}
-		return new Scaled(value / magfact, unit);
-	}
-
-	static class Scaled {
-		double value;
-		String unit;
-
-		public Scaled(double value, String unit) {
-			this.value = value;
-			this.unit = unit;
-		}
-
-		void dump() {
-			System.out.println("[" + value + unit + "]");
-		}
-	}
-}
diff --git a/apps/jrobin/java/src/org/jrobin/graph/package.html b/apps/jrobin/java/src/org/jrobin/graph/package.html
deleted file mode 100644
index 5b9630221f8b60d3e6156351a9a5cea2f43d0bd9..0000000000000000000000000000000000000000
--- a/apps/jrobin/java/src/org/jrobin/graph/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-  <body>
-  JRobin graph capabilities.
-  </body>
-</html>
\ No newline at end of file
diff --git a/apps/jrobin/java/src/org/rrd4j/ConsolFun.java b/apps/jrobin/java/src/org/rrd4j/ConsolFun.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7012ac1794ca8db0a358012e05afc15f67a8748
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/ConsolFun.java
@@ -0,0 +1,72 @@
+package org.rrd4j;
+
+import org.rrd4j.data.Variable;
+
+/**
+ * Enumeration of available consolidation functions. Note that data aggregation inevitably leads to
+ * loss of precision and information. The trick is to pick the aggregate function such that the interesting
+ * properties of your data are kept across the aggregation process.
+ */
+public enum ConsolFun {
+    /**
+     * The average of the data points is stored.
+     */
+    AVERAGE {
+        @Override
+        public Variable getVariable() {
+            return new Variable.AVERAGE();
+        }
+    },
+
+    /**
+     * The smallest of the data points is stored.
+     */
+    MIN {
+        @Override
+        public Variable getVariable() {
+            return new Variable.MIN();
+        }
+    },
+
+    /**
+     * The largest of the data points is stored.
+     */
+    MAX {
+        @Override
+        public Variable getVariable() {
+            return new Variable.MAX();
+        }
+    },
+
+    /**
+     * The last data point is used.
+     */
+    LAST {
+        @Override
+        public Variable getVariable() {
+            return new Variable.LAST();
+        }
+    },
+
+    /**
+     * The fist data point is used.
+     */
+    FIRST {
+        @Override
+        public Variable getVariable() {
+            return new Variable.FIRST();
+        }
+    },
+
+    /**
+     * The total of the data points is stored.
+     */
+    TOTAL {
+        @Override
+        public Variable getVariable() {
+            return new Variable.TOTAL();
+        }
+    };
+
+    public abstract Variable getVariable();
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/DsType.java b/apps/jrobin/java/src/org/rrd4j/DsType.java
new file mode 100644
index 0000000000000000000000000000000000000000..744cb8dda602a8a1f3abfabeed37dfdb132ac117
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/DsType.java
@@ -0,0 +1,26 @@
+package org.rrd4j;
+
+/**
+ * Enumeration of available datasource types.
+ */
+public enum DsType {
+    /**
+     * Is for things like temperatures or number of people in a room or the value of a RedHat share.
+     */
+    GAUGE,
+
+    /**
+     * Is for continuous incrementing counters like the ifInOctets counter in a router.
+     */
+    COUNTER,
+
+    /**
+     * Will store the derivative of the line going from the last to the current value of the data source.
+     */
+    DERIVE,
+
+    /**
+     * Is for counters which get reset upon reading.
+     */
+    ABSOLUTE
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/Aggregates.java b/apps/jrobin/java/src/org/rrd4j/data/Aggregates.java
new file mode 100644
index 0000000000000000000000000000000000000000..e29d6334170c71da543c77d26dc0657c79fb0ff7
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/Aggregates.java
@@ -0,0 +1,114 @@
+package org.rrd4j.data;
+
+import org.rrd4j.ConsolFun;
+import org.rrd4j.core.Util;
+
+/**
+ * Simple class which holds aggregated values (MIN, MAX, FIRST, LAST, AVERAGE and TOTAL). You
+ * don't need to create objects of this class directly. Objects of this class are returned from
+ * <code>getAggregates()</code> method in
+ * {@link org.rrd4j.core.FetchData#getAggregates(String) FetchData} and
+ * {@link org.rrd4j.data.DataProcessor#getAggregates(String)} DataProcessor classes.
+ *
+ * @deprecated This class is deprecated. Uses instance of {@link org.rrd4j.data.Variable}, used with {@link org.rrd4j.data.DataProcessor#addDatasource(String, String, Variable)}.
+ */
+@Deprecated
+public class Aggregates {
+    double min = Double.NaN, max = Double.NaN;
+    double first = Double.NaN, last = Double.NaN;
+    double average = Double.NaN, total = Double.NaN;
+
+    /**
+     * Returns the minimal value
+     *
+     * @return Minimal value
+     */
+    public double getMin() {
+        return min;
+    }
+
+    /**
+     * Returns the maximum value
+     *
+     * @return Maximum value
+     */
+    public double getMax() {
+        return max;
+    }
+
+    /**
+     * Returns the first value
+     *
+     * @return First value
+     */
+    public double getFirst() {
+        return first;
+    }
+
+    /**
+     * Returns the last value
+     *
+     * @return Last value
+     */
+    public double getLast() {
+        return last;
+    }
+
+    /**
+     * Returns average
+     *
+     * @return Average value
+     */
+    public double getAverage() {
+        return average;
+    }
+
+    /**
+     * Returns total value
+     *
+     * @return Total value
+     */
+    public double getTotal() {
+        return total;
+    }
+
+
+    /**
+     * Returns single aggregated value for the give consolidation function
+     *
+     * @param consolFun Consolidation function: MIN, MAX, FIRST, LAST, AVERAGE, TOTAL. These constants
+     *                  are conveniently defined in the {@link org.rrd4j.ConsolFun ConsolFun} interface.
+     * @return Aggregated value
+     * @throws java.lang.IllegalArgumentException Thrown if unsupported consolidation function is supplied
+     */
+    public double getAggregate(ConsolFun consolFun) {
+        switch (consolFun) {
+        case AVERAGE:
+            return average;
+        case FIRST:
+            return first;
+        case LAST:
+            return last;
+        case MAX:
+            return max;
+        case MIN:
+            return min;
+        case TOTAL:
+            return total;
+        }
+        throw new IllegalArgumentException("Unknown consolidation function: " + consolFun);
+    }
+
+    /**
+     * Returns String representing all aggregated values. Just for debugging purposes.
+     *
+     * @return String containing all aggregated values
+     */
+    public String dump() {
+        StringBuilder bl = new StringBuilder();
+        for(ConsolFun cf: ConsolFun.values()) {
+            bl.append(cf.name() + '=' + Util.formatDouble(this.getAggregate(cf)));
+        }
+        return bl.toString();
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/Aggregator.java b/apps/jrobin/java/src/org/rrd4j/data/Aggregator.java
new file mode 100644
index 0000000000000000000000000000000000000000..bfbc8f6dd847217d408815e0f615bc031b11015d
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/Aggregator.java
@@ -0,0 +1,93 @@
+package org.rrd4j.data;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class Aggregator {
+    private final long timestamps[], step;
+    private final double[] values;
+
+    Aggregator(long[] timestamps, double[] values) {
+        assert timestamps.length == values.length : "Incompatible timestamps/values arrays (unequal lengths)";
+        assert timestamps.length >= 2 : "At least two timestamps must be supplied";
+        this.timestamps = timestamps;
+        this.values = values;
+        this.step = timestamps[1] - timestamps[0];
+    }
+
+    @Deprecated
+    Aggregates getAggregates(long tStart, long tEnd) {
+        Aggregates agg = new Aggregates();
+        long totalSeconds = 0;
+        int cnt = 0;
+
+        for (int i = 0; i < timestamps.length; i++) {
+            long left = Math.max(timestamps[i] - step, tStart);
+            long right = Math.min(timestamps[i], tEnd);
+            long delta = right - left;
+
+            // delta is only > 0 when the time stamp for a given buck is within the range of tStart and tEnd
+            if (delta > 0) {
+                double value = values[i];
+
+                if (!Double.isNaN(value)) {
+                    totalSeconds += delta;
+                    cnt++;
+
+                    if (cnt == 1) {
+                        agg.last = agg.first = agg.total = agg.min = agg.max = value;
+                    }
+                    else {
+                        if (delta >= step) {  // an entire bucket is included in this range
+                            agg.last = value;
+                        }
+
+                        agg.min = Math.min(agg.min, value);
+                        agg.max = Math.max(agg.max, value);
+                        agg.total += value;
+
+                    }
+                }
+            }
+        }
+
+        if(cnt > 0) {
+            agg.average = agg.total / totalSeconds;
+        }
+
+        return agg;
+    }
+
+    double getPercentile(long tStart, long tEnd, double percentile) {
+        List<Double> valueList = new ArrayList<Double>();
+        // create a list of included datasource values (different from NaN)
+        for (int i = 0; i < timestamps.length; i++) {
+            long left = Math.max(timestamps[i] - step, tStart);
+            long right = Math.min(timestamps[i], tEnd);
+            if (right > left && !Double.isNaN(values[i])) {
+                valueList.add(Double.valueOf(values[i]));
+            }
+        }
+        // create an array to work with
+        int count = valueList.size();
+        if (count > 1) {
+            double[] valuesCopy = new double[count];
+            for (int i = 0; i < count; i++) {
+                valuesCopy[i] = valueList.get(i).doubleValue();
+            }
+            // sort array
+            Arrays.sort(valuesCopy);
+            // skip top (100% - percentile) values
+            double topPercentile = (100.0 - percentile) / 100.0;
+            count -= (int) Math.ceil(count * topPercentile);
+            // if we have anything left...
+            if (count > 0) {
+                return valuesCopy[count - 1];
+            }
+        }
+        // not enough data available
+        return Double.NaN;
+    }
+}
+
diff --git a/apps/jrobin/java/src/org/rrd4j/data/CDef.java b/apps/jrobin/java/src/org/rrd4j/data/CDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..75301674e59366cb71f5596ebffd015793aaadeb
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/CDef.java
@@ -0,0 +1,20 @@
+package org.rrd4j.data;
+
+class CDef extends Source implements NonRrdSource {
+    private final String rpnExpression;
+
+    CDef(String name, String rpnExpression) {
+        super(name);
+        this.rpnExpression = rpnExpression;
+    }
+
+    String getRpnExpression() {
+        return rpnExpression;
+    }
+    
+    /** {@inheritDoc} */
+    public void calculate(long tStart, long tEnd, DataProcessor dataProcessor) {
+        RpnCalculator calc = new RpnCalculator(rpnExpression, getName(), dataProcessor);
+        setValues(calc.calculateValues());
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/CubicSplineInterpolator.java b/apps/jrobin/java/src/org/rrd4j/data/CubicSplineInterpolator.java
new file mode 100644
index 0000000000000000000000000000000000000000..94d851615ab138754a7ed351d94a9d899bec61fb
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/CubicSplineInterpolator.java
@@ -0,0 +1,177 @@
+package org.rrd4j.data;
+
+import org.rrd4j.core.Util;
+
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * Class used to interpolate datasource values from the collection of (timestamp, values)
+ * points using natural cubic spline interpolation.
+ * <p>
+ *
+ * <b>WARNING</b>: So far, this class cannot handle NaN datasource values
+ * (an exception will be thrown by the constructor). Future releases might change this.
+ */
+public class CubicSplineInterpolator extends Plottable {
+    private double[] x;
+    private double[] y;
+
+    // second derivates come here
+    private double[] y2;
+
+    // internal spline variables
+    private int n, klo, khi;
+
+    /**
+     * Creates cubic spline interpolator from arrays of timestamps and corresponding
+     * datasource values.
+     *
+     * @param timestamps timestamps in seconds
+     * @param values     corresponding datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if supplied arrays do not contain at least 3 values, or if
+     *                                  timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
+     */
+    public CubicSplineInterpolator(long[] timestamps, double[] values) {
+        this.x = new double[timestamps.length];
+        for (int i = 0; i < timestamps.length; i++) {
+            this.x[i] = timestamps[i];
+        }
+        this.y = values;
+        validate();
+        spline();
+    }
+
+    /**
+     * Creates cubic spline interpolator from arrays of Date objects and corresponding
+     * datasource values.
+     *
+     * @param dates  Array of Date objects
+     * @param values corresponding datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if supplied arrays do not contain at least 3 values, or if
+     *                                  timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
+     */
+    public CubicSplineInterpolator(Date[] dates, double[] values) {
+        this.x = new double[dates.length];
+        for (int i = 0; i < dates.length; i++) {
+            this.x[i] = Util.getTimestamp(dates[i]);
+        }
+        this.y = values;
+        validate();
+        spline();
+    }
+
+    /**
+     * Creates cubic spline interpolator from arrays of GregorianCalendar objects and corresponding
+     * datasource values.
+     *
+     * @param dates  Array of GregorianCalendar objects
+     * @param values corresponding datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if supplied arrays do not contain at least 3 values, or if
+     *                                  timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
+     */
+    public CubicSplineInterpolator(Calendar[] dates, double[] values) {
+        this.x = new double[dates.length];
+        for (int i = 0; i < dates.length; i++) {
+            this.x[i] = Util.getTimestamp(dates[i]);
+        }
+        this.y = values;
+        validate();
+        spline();
+    }
+
+    /**
+     * Creates cubic spline interpolator for an array of 2D-points.
+     *
+     * @param x x-axis point coordinates
+     * @param y y-axis point coordinates
+     * @throws java.lang.IllegalArgumentException Thrown if supplied arrays do not contain at least 3 values, or if
+     *                                  timestamps are not ordered, or array lengths are not equal, or some datasource value is NaN.
+     */
+    public CubicSplineInterpolator(double[] x, double[] y) {
+        this.x = x;
+        this.y = y;
+        validate();
+        spline();
+    }
+
+    private void validate() {
+        boolean ok = true;
+        if (x.length != y.length || x.length < 3) {
+            ok = false;
+        }
+        for (int i = 0; i < x.length - 1 && ok; i++) {
+            if (x[i] >= x[i + 1] || Double.isNaN(y[i])) {
+                ok = false;
+            }
+        }
+        if (!ok) {
+            throw new IllegalArgumentException("Invalid plottable data supplied");
+        }
+    }
+
+    private void spline() {
+        n = x.length;
+        y2 = new double[n];
+        double[] u = new double[n - 1];
+        y2[0] = y2[n - 1] = 0.0;
+        u[0] = 0.0; // natural spline
+        for (int i = 1; i <= n - 2; i++) {
+            double sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
+            double p = sig * y2[i - 1] + 2.0;
+            y2[i] = (sig - 1.0) / p;
+            u[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
+            u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
+        }
+        for (int k = n - 2; k >= 0; k--) {
+            y2[k] = y2[k] * y2[k + 1] + u[k];
+        }
+        // prepare everything for getValue()
+        klo = 0;
+        khi = n - 1;
+    }
+
+    /**
+     * Calculates spline-interpolated y-value for the corresponding x-value. Call
+     * this if you need spline-interpolated values in your code.
+     *
+     * @param xval x-value
+     * @return inteprolated y-value
+     */
+    public double getValue(double xval) {
+        if (xval < x[0] || xval > x[n - 1]) {
+            return Double.NaN;
+        }
+        if (xval < x[klo] || xval > x[khi]) {
+            // out of bounds
+            klo = 0;
+            khi = n - 1;
+        }
+        while (khi - klo > 1) {
+            // find bounding interval using bisection method
+            int k = (khi + klo) / 2;
+            if (x[k] > xval) {
+                khi = k;
+            }
+            else {
+                klo = k;
+            }
+        }
+        double h = x[khi] - x[klo];
+        double a = (x[khi] - xval) / h;
+        double b = (xval - x[klo]) / h;
+        return a * y[klo] + b * y[khi] +
+                ((a * a * a - a) * y2[klo] + (b * b * b - b) * y2[khi]) * (h * h) / 6.0;
+    }
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * Method overridden from the base class. This method will be called by the framework. Call
+	 * this method only if you need spline-interpolated values in your code.
+	 */
+	public double getValue(long timestamp) {
+		return getValue((double)timestamp);
+	}
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/DataProcessor.java b/apps/jrobin/java/src/org/rrd4j/data/DataProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..7780f782c7906c1f0e398c35c288ad052972ba59
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/DataProcessor.java
@@ -0,0 +1,901 @@
+package org.rrd4j.data;
+
+import org.rrd4j.ConsolFun;
+import org.rrd4j.core.FetchData;
+import org.rrd4j.core.FetchRequest;
+import org.rrd4j.core.RrdBackendFactory;
+import org.rrd4j.core.RrdDb;
+import org.rrd4j.core.RrdDbPool;
+import org.rrd4j.core.Util;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+
+/**
+ * <p>Class which should be used for all calculations based on the data fetched from RRD files. This class
+ * supports ordinary DEF datasources (defined in RRD files), CDEF datasources (RPN expressions evaluation),
+ * SDEF (static datasources - extension of Rrd4j) and PDEF (plottables, see
+ * {@link org.rrd4j.data.Plottable Plottable} for more information.</p>
+ *
+ * <p>Typical class usage:</p>
+ * <pre>
+ * final long t1 = ...
+ * final long t2 = ...
+ * DataProcessor dp = new DataProcessor(t1, t2);
+ * // DEF datasource
+ * dp.addDatasource("x", "demo.rrd", "some_source", "AVERAGE");
+ * // DEF datasource
+ * dp.addDatasource("y", "demo.rrd", "some_other_source", "AVERAGE");
+ * // CDEF datasource, z = (x + y) / 2
+ * dp.addDatasource("z", "x,y,+,2,/");
+ * // ACTION!
+ * dp.processData();
+ * // Dump calculated values
+ * System.out.println(dp.dump());
+ * </pre>
+ */
+public class DataProcessor {
+
+    /**
+     * Constant representing the default number of pixels on a Rrd4j graph (will be used if
+     * no other value is specified with {@link #setStep(long) setStep()} method.
+     */
+    public static final int DEFAULT_PIXEL_COUNT = 600;
+    /** Constant <code>DEFAULT_PERCENTILE=95.0</code> */
+    public static final double DEFAULT_PERCENTILE = 95.0; // %
+
+    private int pixelCount = DEFAULT_PIXEL_COUNT;
+
+    /**
+     * Constant that defines the default {@link RrdDbPool} usage policy. Defaults to <code>false</code>
+     * (i.e. the pool will not be used to fetch data from RRD files)
+     */
+    public static final boolean DEFAULT_POOL_USAGE_POLICY = false;
+    private boolean poolUsed = DEFAULT_POOL_USAGE_POLICY;
+    private RrdDbPool pool = null;
+
+    private final long tStart;
+    private long tEnd, timestamps[];
+    private long lastRrdArchiveUpdateTime = 0;
+    // this will be adjusted later
+    private long step = 0;
+    // resolution to be used for RRD fetch operation
+    private long fetchRequestResolution = 1;
+    // The timezone to use
+    private TimeZone tz = TimeZone.getDefault();
+
+    // the order is important, ordinary HashMap is unordered
+    private Map<String, Source> sources = new LinkedHashMap<String, Source>();
+
+    private Def[] defSources;
+
+    /**
+     * Creates new DataProcessor object for the given time span. Ending timestamp may be set to zero.
+     * In that case, the class will try to find the optimal ending timestamp based on the last update time of
+     * RRD files processed with the {@link #processData()} method.
+     *
+     * @param t1 Starting timestamp in seconds without milliseconds
+     * @param t2 Ending timestamp in seconds without milliseconds
+     */
+    public DataProcessor(long t1, long t2) {
+        if ((t1 < t2 && t1 > 0 && t2 > 0) || (t1 > 0 && t2 == 0)) {
+            this.tStart = t1;
+            this.tEnd = t2;
+        }
+        else {
+            throw new IllegalArgumentException("Invalid timestamps specified: " + t1 + ", " + t2);
+        }
+    }
+
+    /**
+     * Creates new DataProcessor object for the given time span. Ending date may be set to null.
+     * In that case, the class will try to find optimal ending date based on the last update time of
+     * RRD files processed with the {@link #processData()} method.
+     *
+     * @param d1 Starting date
+     * @param d2 Ending date
+     */
+    public DataProcessor(Date d1, Date d2) {
+        this(Util.getTimestamp(d1), d2 != null ? Util.getTimestamp(d2) : 0);
+    }
+
+    /**
+     * Creates new DataProcessor object for the given time span. Ending date may be set to null.
+     * In that case, the class will try to find optimal ending date based on the last update time of
+     * RRD files processed with the {@link #processData()} method.
+     * 
+     * It use the time zone for starting calendar date.
+     *
+     * @param gc1 Starting Calendar date
+     * @param gc2 Ending Calendar date
+     */
+    public DataProcessor(Calendar gc1, Calendar gc2) {
+        this(Util.getTimestamp(gc1), gc2 != null ? Util.getTimestamp(gc2) : 0);
+        this.tz = gc1.getTimeZone();
+    }
+
+    /**
+     * Returns boolean value representing {@link org.rrd4j.core.RrdDbPool RrdDbPool} usage policy.
+     *
+     * @return true, if the pool will be used internally to fetch data from RRD files, false otherwise.
+     */
+    public boolean isPoolUsed() {
+        return poolUsed;
+    }
+
+    /**
+     * Sets the {@link org.rrd4j.core.RrdDbPool RrdDbPool} usage policy.
+     *
+     * @param poolUsed true, if the pool should be used to fetch data from RRD files, false otherwise.
+     */
+    public void setPoolUsed(boolean poolUsed) {
+        this.poolUsed = poolUsed;
+    }
+
+    public RrdDbPool getPool() {
+        return pool;
+    }
+
+    /**
+     * Defines the {@link org.rrd4j.core.RrdDbPool RrdDbPool} to use. If not defined, but {{@link #setPoolUsed(boolean)}
+     * set to true, the default {@link RrdDbPool#getInstance()} will be used.
+     * @param pool
+     */
+    public void setPool(RrdDbPool pool) {
+        this.pool = pool;
+    }
+
+
+    /**
+     * <p>Sets the number of pixels (target graph width). This number is used only to calculate pixel coordinates
+     * for Rrd4j graphs (methods {@link #getValuesPerPixel(String)} and {@link #getTimestampsPerPixel()}),
+     * but has influence neither on datasource values calculated with the
+     * {@link #processData()} method nor on aggregated values returned from {@link #getAggregates(String)}
+     * and similar methods. In other words, aggregated values will not change once you decide to change
+     * the dimension of your graph.</p>
+     *
+     * The default number of pixels is defined by constant {@link #DEFAULT_PIXEL_COUNT}
+     * and can be changed with a {@link #setPixelCount(int)} method.
+     *
+     * @param pixelCount The number of pixels. If you process RRD data in order to display it on the graph,
+     *                   this should be the width of your graph.
+     */
+    public void setPixelCount(int pixelCount) {
+        this.pixelCount = pixelCount;
+    }
+
+    /**
+     * Returns the number of pixels (target graph width). See {@link #setPixelCount(int)} for more information.
+     *
+     * @return Target graph width
+     */
+    public int getPixelCount() {
+        return pixelCount;
+    }
+
+    /**
+     * <p>Roughly corresponds to the --step option in RRDTool's graph/xport commands. Here is an explanation borrowed
+     * from RRDTool:</p>
+     * <p><i>"By default rrdgraph calculates the width of one pixel in the time
+     * domain and tries to get data at that resolution from the RRD. With
+     * this switch you can override this behavior. If you want rrdgraph to
+     * get data at 1 hour resolution from the RRD, then you can set the
+     * step to 3600 seconds. Note, that a step smaller than 1 pixel will
+     * be silently ignored."</i></p>
+     * <p>I think this option is not that useful, but it's here just for compatibility.</p>
+     * @param step Time step at which data should be fetched from RRD files. If this method is not used,
+     *             the step will be equal to the smallest RRD step of all processed RRD files. If no RRD file is processed,
+     *             the step will be roughly equal to the with of one graph pixel (in seconds).
+     */
+    public void setStep(long step) {
+        this.step = step;
+    }
+
+    /**
+     * Returns the time step used for data processing. Initially, this method returns zero.
+     * Once {@link #processData()} is finished, the method will return the real value used for
+     * all internal computations. Roughly corresponds to the --step option in RRDTool's graph/xport commands.
+     *
+     * @return Step used for data processing.
+     */
+    public long getStep() {
+        return step;
+    }
+
+    /**
+     * Returns desired RRD archive step (resolution) in seconds to be used while fetching data
+     * from RRD files. In other words, this value will used as the last parameter of
+     * {@link org.rrd4j.core.RrdDb#createFetchRequest(ConsolFun, long, long, long) RrdDb.createFetchRequest()} method
+     * when this method is called internally by this DataProcessor.
+     *
+     * @return Desired archive step (fetch resolution) in seconds.
+     */
+    public long getFetchRequestResolution() {
+        return fetchRequestResolution;
+    }
+
+    /**
+     * Sets desired RRD archive step in seconds to be used internally while fetching data
+     * from RRD files. In other words, this value will used as the last parameter of
+     * {@link org.rrd4j.core.RrdDb#createFetchRequest(ConsolFun, long, long, long) RrdDb.createFetchRequest()} method
+     * when this method is called internally by this DataProcessor. If this method is never called, fetch
+     * request resolution defaults to 1 (smallest possible archive step will be chosen automatically).
+     *
+     * @param fetchRequestResolution Desired archive step (fetch resolution) in seconds.
+     */
+    public void setFetchRequestResolution(long fetchRequestResolution) {
+        this.fetchRequestResolution = fetchRequestResolution;
+    }
+
+    public TimeZone getTimeZone() {
+        return tz;
+    }
+
+    public void setTimeZone(TimeZone tz) {
+        this.tz = tz;
+    }
+
+    /**
+     * Returns ending timestamp. Basically, this value is equal to the ending timestamp
+     * specified in the constructor. However, if the ending timestamps was zero, it
+     * will be replaced with the real timestamp when the {@link #processData()} method returns. The real
+     * value will be calculated from the last update times of processed RRD files.
+     *
+     * @return Ending timestamp in seconds
+     */
+    public long getEndingTimestamp() {
+        return tEnd;
+    }
+
+    /**
+     * Returns consolidated timestamps created with the {@link #processData()} method.
+     *
+     * @return array of timestamps in seconds
+     */
+    public long[] getTimestamps() {
+        if (timestamps == null) {
+            throw new IllegalArgumentException("Timestamps not calculated yet");
+        }
+        else {
+            return timestamps;
+        }
+    }
+
+    /**
+     * Returns calculated values for a single datasource. Corresponding timestamps can be obtained from
+     * the {@link #getTimestamps()} method.
+     *
+     * @param sourceName Datasource name
+     * @return an array of datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if invalid datasource name is specified,
+     *                                  or if datasource values are not yet calculated (method {@link #processData()}
+     *                                  was not called)
+     */
+    public double[] getValues(String sourceName) {
+        Source source = getSource(sourceName);
+        double[] values = source.getValues();
+        if (values == null) {
+            throw new IllegalArgumentException("Values not available for source [" + sourceName + "]");
+        }
+        return values;
+    }
+
+    /**
+     * Returns single aggregated value for a single datasource.
+     *
+     * @param sourceName Datasource name
+     * @param consolFun  Consolidation function to be applied to fetched datasource values.
+     *                   Valid consolidation functions are MIN, MAX, LAST, FIRST, AVERAGE and TOTAL
+     *                   (these string constants are conveniently defined in the {@link org.rrd4j.ConsolFun} class)
+     * @throws java.lang.IllegalArgumentException Thrown if invalid datasource name is specified,
+     *                                  or if datasource values are not yet calculated (method {@link #processData()}
+     *                                  was not called)
+     * @return a aggregate value as a double calculated from the source.
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public double getAggregate(String sourceName, ConsolFun consolFun) {
+        Variable v = consolFun.getVariable();
+        Source source = getSource(sourceName);
+        v.calculate(source, tStart, tEnd);
+        return v.getValue().value;
+    }
+
+    /**
+     * Returns all (MIN, MAX, LAST, FIRST, AVERAGE and TOTAL) aggregated values for a single datasource.
+     *
+     * @param sourceName Datasource name
+     * @return Object containing all aggregated values
+     * @throws java.lang.IllegalArgumentException Thrown if invalid datasource name is specified,
+     *                                  or if datasource values are not yet calculated (method {@link #processData()}
+     *                                  was not called)
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public Aggregates getAggregates(String sourceName) {
+        Source source = getSource(sourceName);
+        return source.getAggregates(tStart, tEnd);
+    }
+
+    /**
+     * Extract the variable value from an already define Variable datasource (a VDEF)
+     * 
+     * @param sourceName Datasource name
+     * @return A combined time and value extracted calculated from the datasource
+     */
+    public Variable.Value getVariable(String sourceName) {
+        Source source = getSource(sourceName);
+        if( source instanceof VDef) {
+            return ((VDef) source).getValue();
+        }
+        else {
+            throw new IllegalArgumentException(String.format("%s is not a Variable source", source.getName()));
+        }
+    }
+
+    /**
+     * Returns single aggregated value for a single datasource.
+     * 
+     * @param sourceName Datasource name
+     * @param var variable that will generate value
+     * @return A combined time and value extracted calculated from the datasource
+     */
+    public Variable.Value getVariable(String sourceName, Variable var) {
+        Source source = getSource(sourceName);
+        var.calculate(source, tStart, tEnd);
+        return var.getValue();
+    }
+
+    /**
+     * This method is just an alias for {@link #getPercentile(String)} method.
+     *
+     * Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.<p>
+     *
+     * The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
+     * of source data is discarded. It is used as a measure of the peak value used when one discounts
+     * a fair amount for transitory spikes. This makes it markedly different from the average.<p>
+     *
+     * Read more about this topic at
+     * <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or
+     * <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
+     *
+     * @param sourceName Datasource name
+     * @return 95th percentile of fetched source values
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public double get95Percentile(String sourceName) {
+        return getPercentile(sourceName);
+    }
+
+    /**
+     * Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.<p>
+     *
+     * The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
+     * of source data is discarded. It is used as a measure of the peak value used when one discounts
+     * a fair amount for transitory spikes. This makes it markedly different from the average.<p>
+     *
+     * Read more about this topic at
+     * <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or
+     * <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
+     *
+     * @param sourceName Datasource name
+     * @return 95th percentile of fetched source values
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public double getPercentile(String sourceName) {
+        return getPercentile(sourceName, DEFAULT_PERCENTILE);
+    }
+
+    /**
+     * The same as {@link #getPercentile(String)} but with a possibility to define custom percentile boundary
+     * (different from 95).
+     *
+     * @param sourceName Datasource name.
+     * @param percentile Boundary percentile. Value of 95 (%) is suitable in most cases, but you are free
+     *                   to provide your own percentile boundary between zero and 100.
+     * @return Requested percentile of fetched source values
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public double getPercentile(String sourceName, double percentile) {
+        if (percentile <= 0.0 || percentile > 100.0) {
+            throw new IllegalArgumentException("Invalid percentile [" + percentile + "], should be between 0 and 100");
+        }
+        Source source = getSource(sourceName);
+        return source.getPercentile(tStart, tEnd, percentile);
+    }
+
+    /**
+     * Returns array of datasource names defined in this DataProcessor.
+     *
+     * @return array of datasource names
+     */
+    public String[] getSourceNames() {
+        return sources.keySet().toArray(new String[sources.keySet().size()]);
+    }
+
+    /**
+     * Returns an array of all datasource values for all datasources. Each row in this two-dimensional
+     * array represents an array of calculated values for a single datasource. The order of rows is the same
+     * as the order in which datasources were added to this DataProcessor object.
+     *
+     * @return All datasource values for all datasources. The first index is the index of the datasource,
+     *         the second index is the index of the datasource value. The number of datasource values is equal
+     *         to the number of timestamps returned with {@link #getTimestamps()}  method.
+     * @throws java.lang.IllegalArgumentException Thrown if invalid datasource name is specified,
+     *                                  or if datasource values are not yet calculated (method {@link #processData()}
+     *                                  was not called)
+     */
+    public double[][] getValues() {
+        String[] names = getSourceNames();
+        double[][] values = new double[names.length][];
+        for (int i = 0; i < names.length; i++) {
+            values[i] = getValues(names[i]);
+        }
+        return values;
+    }
+
+    Source getSource(String sourceName) {
+        Source source = sources.get(sourceName);
+        if (source != null) {
+            return source;
+        }
+        throw new IllegalArgumentException("Unknown source: " + sourceName);
+    }
+
+    /////////////////////////////////////////////////////////////////
+    // DATASOURCE DEFINITIONS
+    /////////////////////////////////////////////////////////////////
+
+    /**
+     * Adds a custom, {@link org.rrd4j.data.Plottable plottable} datasource (<b>PDEF</b>).
+     * The datapoints should be made available by a class extending
+     * {@link org.rrd4j.data.Plottable Plottable} class.
+     *
+     * @param name      source name.
+     * @param plottable class that extends Plottable class and is suited for graphing.
+     */
+    public void addDatasource(String name, Plottable plottable) {
+        PDef pDef = new PDef(name, plottable);
+        sources.put(name, pDef);
+    }
+
+    /**
+     * <p>Adds complex source (<b>CDEF</b>).
+     * Complex sources are evaluated using the supplied <code>RPN</code> expression.</p>
+     * Complex source <code>name</code> can be used:
+     * <ul>
+     * <li>To specify sources for line, area and stack plots.</li>
+     * <li>To define other complex sources.</li>
+     * </ul>
+     *
+     * The supported RPN functions, operators and constants are detailed at
+     * <a href="https://github.com/rrd4j/rrd4j/wiki/RPNFuncs" target="man">RRD4J's wiki</a>.
+     * <p>
+     * Rrd4j does not force you to specify at least one simple source name as RRDTool.
+     * <p>
+     * For more details on RPN see RRDTool's
+     * <a href="http://oss.oetiker.ch/rrdtool/doc/rrdgraph_rpn.en.html" target="man">
+     * rrdgraph man page</a>.
+     *
+     * @param name          source name.
+     * @param rpnExpression RPN expression containing comma delimited simple and complex
+     *                      source names, RPN constants, functions and operators.
+     */
+    public void addDatasource(String name, String rpnExpression) {
+        CDef cDef = new CDef(name, rpnExpression);
+        sources.put(name, cDef);
+    }
+
+    /**
+     * Adds static source (<b>SDEF</b>). Static sources are the result of a consolidation function applied
+     * to <em>any</em> other source that has been defined previously.
+     *
+     * @param name      source name.
+     * @param defName   Name of the datasource to calculate the value from.
+     * @param consolFun Consolidation function to use for value calculation
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void addDatasource(String name, String defName, ConsolFun consolFun) {
+        VDef sDef = new VDef(name, defName, consolFun.getVariable());
+        sources.put(name, sDef);
+    }
+
+    /**
+     * Creates a datasource that performs a percentile calculation on an
+     * another named datasource to yield a single value.
+     *
+     * Requires that the other datasource has already been defined; otherwise, it'll
+     * end up with no data
+     *
+     * @param name - the new virtual datasource name
+     * @param sourceName - the datasource from which to extract the percentile.  Must be a previously
+     *                     defined virtual datasource
+     * @param percentile - the percentile to extract from the source datasource
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void addDatasource(String name, String sourceName, double percentile) {
+        sources.put(name, new VDef(name, sourceName, new Variable.PERCENTILE(percentile)));
+    }
+
+    /**
+     * Creates a datasource that performs a variable calculation on an
+     * another named datasource to yield a single combined timestampe/value.
+     *
+     * Requires that the other datasource has already been defined; otherwise, it'll
+     * end up with no data
+     *
+     * @param name - the new virtual datasource name
+     * @param defName - the datasource from which to extract the percentile. Must be a previously
+     *                     defined virtual datasource
+     * @param var - a new instance of a Variable used to do the calculation
+     */
+    public void addDatasource(String name, String defName, Variable var) {
+        VDef sDef = new VDef(name, defName, var);
+        sources.put(name, sDef);
+    }
+
+    /**
+     * <p>Adds simple datasource (<b>DEF</b>). Simple source <code>name</code>
+     * can be used:</p>
+     * <ul>
+     * <li>To specify sources for line, area and stack plots.</li>
+     * <li>To define complex sources
+     * </ul>
+     *
+     * @param name       source name.
+     * @param file       Path to RRD file.
+     * @param dsName     Datasource name defined in the RRD file.
+     * @param consolFunc Consolidation function that will be used to extract data from the RRD
+     */
+    public void addDatasource(String name, String file, String dsName, ConsolFun consolFunc) {
+        Def def = new Def(name, file, dsName, consolFunc);
+        sources.put(name, def);
+    }
+
+    /**
+     * <p>Adds simple source (<b>DEF</b>). Source <code>name</code> can be used:</p>
+     * <ul>
+     * <li>To specify sources for line, area and stack plots.</li>
+     * <li>To define complex sources
+     * </ul>
+     *
+     * @param name       Source name.
+     * @param file       Path to RRD file.
+     * @param dsName     Data source name defined in the RRD file.
+     * @param consolFunc Consolidation function that will be used to extract data from the RRD
+     *                   file ("AVERAGE", "MIN", "MAX" or "LAST" - these string constants are conveniently defined
+     *                   in the {@link org.rrd4j.ConsolFun ConsolFun} class).
+     * @param backend    Name of the RrdBackendFactory that should be used for this RrdDb.
+     * @deprecated uses {@link #addDatasource(String, String, String, ConsolFun, RrdBackendFactory)} instead
+     */
+    @Deprecated
+    public void addDatasource(String name, String file, String dsName, ConsolFun consolFunc, String backend) {
+        Def def = new Def(name, file, dsName, consolFunc, RrdBackendFactory.getFactory(backend));
+        sources.put(name, def);
+    }
+
+    /**
+     * <p>Adds simple source (<b>DEF</b>). Source <code>name</code> can be used:</p>
+     * <ul>
+     * <li>To specify sources for line, area and stack plots.</li>
+     * <li>To define complex sources
+     * </ul>
+     *
+     * @param name       Source name.
+     * @param file       Path to RRD file.
+     * @param dsName     Data source name defined in the RRD file.
+     * @param consolFunc Consolidation function that will be used to extract data from the RRD
+     *                   file ("AVERAGE", "MIN", "MAX" or "LAST" - these string constants are conveniently defined
+     *                   in the {@link org.rrd4j.ConsolFun ConsolFun} class).
+     * @param backend    Name of the RrdBackendFactory that should be used for this RrdDb.
+     */
+    public void addDatasource(String name, String file, String dsName, ConsolFun consolFunc, RrdBackendFactory backend) {
+        Def def = new Def(name, file, dsName, consolFunc, backend);
+        sources.put(name, def);
+    }
+
+    /**
+     * Adds DEF datasource with datasource values already available in the FetchData object. This method is
+     * used internally by Rrd4j and probably has no purpose outside of it.
+     *
+     * @param name      Source name.
+     * @param fetchData Fetched data containing values for the given source name.
+     */
+    public void addDatasource(String name, FetchData fetchData) {
+        Def def = new Def(name, fetchData);
+        sources.put(name, def);
+    }
+
+    /**
+     * Adds DEF datasource with datasource values already available in the FetchData object. This method is
+     * used internally by Rrd4j and probably has no purpose outside of it.
+     * The values will be extracted from dsName in fetchData.
+     *
+     * @param name      Source name.
+     * @param dsName    Source name in the fetch data.
+     * @param fetchData Fetched data containing values for the given source name.
+     */
+    public void addDatasource(String name, String dsName, FetchData fetchData) {
+        Def def = new Def(name, dsName, fetchData);
+        sources.put(name, def);
+    }
+
+    /////////////////////////////////////////////////////////////////
+    // CALCULATIONS
+    /////////////////////////////////////////////////////////////////
+
+    /**
+     * Method that should be called once all datasources are defined. Data will be fetched from
+     * RRD files, RPN expressions will be calculated, etc.
+     *
+     * @throws java.io.IOException Thrown in case of I/O error (while fetching data from RRD files)
+     */
+    public void processData() throws IOException {
+        extractDefs();
+        fetchRrdData();
+        fixZeroEndingTimestamp();
+        chooseOptimalStep();
+        createTimestamps();
+        assignTimestampsToSources();
+        normalizeRrdValues();
+        calculateNonRrdSources();
+    }
+
+    /**
+     * Method used to calculate datasource values which should be presented on the graph
+     * based on the desired graph width. Each value returned represents a single pixel on the graph.
+     * Corresponding timestamp can be found in the array returned from {@link #getTimestampsPerPixel()}
+     * method.
+     *
+     * @param sourceName Datasource name
+     * @param pixelCount Graph width
+     * @return Per-pixel datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if datasource values are not yet calculated (method {@link #processData()}
+     *                                  was not called)
+     */
+    public double[] getValuesPerPixel(String sourceName, int pixelCount) {
+        setPixelCount(pixelCount);
+        return getValuesPerPixel(sourceName);
+    }
+
+    /**
+     * Method used to calculate datasource values which should be presented on the graph
+     * based on the graph width set with a {@link #setPixelCount(int)} method call.
+     * Each value returned represents a single pixel on the graph. Corresponding timestamp can be
+     * found in the array returned from {@link #getTimestampsPerPixel()} method.
+     *
+     * @param sourceName Datasource name
+     * @return Per-pixel datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if datasource values are not yet calculated (method {@link #processData()}
+     *                                  was not called)
+     */
+    public double[] getValuesPerPixel(String sourceName) {
+        double[] values = getValues(sourceName);
+        double[] pixelValues = new double[pixelCount];
+        Arrays.fill(pixelValues, Double.NaN);
+        long span = tEnd - tStart;
+        // this is the ugliest nested loop I have ever made
+        for (int pix = 0, ref = 0; pix < pixelCount; pix++) {
+            double t = tStart + (double) (span * pix) / (double) (pixelCount - 1);
+            while (ref < timestamps.length) {
+                if (t <= timestamps[ref] - step) {
+                    // too left, nothing to do, already NaN
+                    break;
+                }
+                else if (t <= timestamps[ref]) {
+                    // in brackets, get this value
+                    pixelValues[pix] = values[ref];
+                    break;
+                }
+                else {
+                    // too right
+                    ref++;
+                }
+            }
+        }
+        return pixelValues;
+    }
+
+    /**
+     * Calculates timestamps which correspond to individual pixels on the graph.
+     *
+     * @param pixelCount Graph width
+     * @return Array of timestamps
+     */
+    public long[] getTimestampsPerPixel(int pixelCount) {
+        setPixelCount(pixelCount);
+        return getTimestampsPerPixel();
+    }
+
+    /**
+     * Calculates timestamps which correspond to individual pixels on the graph
+     * based on the graph width set with a {@link #setPixelCount(int)} method call.
+     *
+     * @return Array of timestamps
+     */
+    public long[] getTimestampsPerPixel() {
+        long[] times = new long[pixelCount];
+        long span = tEnd - tStart;
+        for (int i = 0; i < pixelCount; i++) {
+            times[i] = Math.round(tStart + (double) (span * i) / (double) (pixelCount - 1));
+        }
+        return times;
+    }
+
+    /**
+     * Dumps timestamps and values of all datasources in a tabular form. Very useful for debugging.
+     *
+     * @return Dumped object content.
+     */
+    public String dump() {
+        String[] names = getSourceNames();
+        double[][] values = getValues();
+        StringBuilder buffer = new StringBuilder();
+        buffer.append(format("timestamp", 12));
+        for (String name : names) {
+            buffer.append(format(name, 20));
+        }
+        buffer.append("\n");
+        for (int i = 0; i < timestamps.length; i++) {
+            buffer.append(format(Long.toString(timestamps[i]), 12));
+            for (int j = 0; j < names.length; j++) {
+                buffer.append(format(Util.formatDouble(values[j][i]), 20));
+            }
+            buffer.append("\n");
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns time when last RRD archive was updated (all RRD files are considered).
+     *
+     * @return Last archive update time for all RRD files in this DataProcessor
+     */
+    public long getLastRrdArchiveUpdateTime() {
+        return lastRrdArchiveUpdateTime;
+    }
+
+    // PRIVATE METHODS
+
+    private void extractDefs() {
+        List<Def> defList = new ArrayList<Def>();
+        for (Source source : sources.values()) {
+            if (source instanceof Def) {
+                defList.add((Def) source);
+            }
+        }
+        defSources = defList.toArray(new Def[defList.size()]);
+    }
+
+    private void fetchRrdData() throws IOException {
+        long tEndFixed = (tEnd == 0) ? Util.getTime() : tEnd;
+        for (int i = 0; i < defSources.length; i++) {
+            if (!defSources[i].isLoaded()) {
+                // not fetched yet
+                Set<String> dsNames = new HashSet<String>();
+                dsNames.add(defSources[i].getDsName());
+                // look for all other datasources with the same path and the same consolidation function
+                for (int j = i + 1; j < defSources.length; j++) {
+                    if (defSources[i].isCompatibleWith(defSources[j])) {
+                        dsNames.add(defSources[j].getDsName());
+                    }
+                }
+                // now we have everything
+                try (RrdDb rrd = getRrd(defSources[i])){
+                    lastRrdArchiveUpdateTime = Math.max(lastRrdArchiveUpdateTime, rrd.getLastArchiveUpdateTime());
+                    FetchRequest req = rrd.createFetchRequest(defSources[i].getConsolFun(),
+                            tStart, tEndFixed, fetchRequestResolution);
+                    req.setFilter(dsNames);
+                    FetchData data = req.fetchData();
+                    defSources[i].setFetchData(data);
+                    for (int j = i + 1; j < defSources.length; j++) {
+                        if (defSources[i].isCompatibleWith(defSources[j])) {
+                            defSources[j].setFetchData(data);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void fixZeroEndingTimestamp() {
+        if (tEnd == 0) {
+            if (defSources.length == 0) {
+                throw new IllegalStateException("Could not adjust zero ending timestamp, no DEF source provided");
+            }
+            tEnd = defSources[0].getArchiveEndTime();
+            for (int i = 1; i < defSources.length; i++) {
+                tEnd = Math.min(tEnd, defSources[i].getArchiveEndTime());
+            }
+            if (tEnd <= tStart) {
+                throw new IllegalStateException("Could not resolve zero ending timestamp.");
+            }
+        }
+    }
+
+    // Tricky and ugly. Should be redesigned some time in the future
+    private void chooseOptimalStep() {
+        long newStep = Long.MAX_VALUE;
+        for (Def defSource : defSources) {
+            long fetchStep = defSource.getFetchStep(), tryStep = fetchStep;
+            if (step > 0) {
+                tryStep = Math.min(newStep, (((step - 1) / fetchStep) + 1) * fetchStep);
+            }
+            newStep = Math.min(newStep, tryStep);
+        }
+        if (newStep != Long.MAX_VALUE) {
+            // step resolved from a RRD file
+            step = newStep;
+        }
+        else {
+            // choose step based on the number of pixels (useful for plottable datasources)
+            step = Math.max((tEnd - tStart) / pixelCount, 1);
+        }
+    }
+
+    private void createTimestamps() {
+        long t1 = Util.normalize(tStart, step);
+        long t2 = Util.normalize(tEnd, step);
+        if (t2 < tEnd) {
+            t2 += step;
+        }
+        int count = (int) (((t2 - t1) / step) + 1);
+        timestamps = new long[count];
+        for (int i = 0; i < count; i++) {
+            timestamps[i] = t1;
+            t1 += step;
+        }
+    }
+
+    private void assignTimestampsToSources() {
+        for (Source src : sources.values()) {
+            src.setTimestamps(timestamps);
+        }
+    }
+
+    private void normalizeRrdValues() {
+        Normalizer normalizer = new Normalizer(timestamps);
+        for (Def def : defSources) {
+            long[] rrdTimestamps = def.getRrdTimestamps();
+            double[] rrdValues = def.getRrdValues();
+            def.setValues(normalizer.normalize(rrdTimestamps, rrdValues));
+        }
+    }
+
+    private void calculateNonRrdSources() {
+        for (Source source : sources.values()) {
+            if (source instanceof NonRrdSource) {
+                ((NonRrdSource)source).calculate(tStart, tEnd, this);
+            }
+        }
+    }
+
+    private RrdDb getRrd(Def def) throws IOException {
+        String path = def.getPath();
+        RrdBackendFactory backend = def.getBackend();
+        return RrdDb.getBuilder().setPath(path).setBackendFactory(backend).readOnly().setUsePool(poolUsed).setPool(pool).build();
+    }
+
+    private static String format(String s, int length) {
+        StringBuilder b = new StringBuilder(s);
+        for (int i = 0; i < length - s.length(); i++) {
+            b.append(' ');
+        }
+        return b.toString();
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/Def.java b/apps/jrobin/java/src/org/rrd4j/data/Def.java
new file mode 100644
index 0000000000000000000000000000000000000000..8123343d03cdc8d88cbfef27de638784060cd435
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/Def.java
@@ -0,0 +1,114 @@
+package org.rrd4j.data;
+
+import org.rrd4j.ConsolFun;
+import org.rrd4j.core.FetchData;
+import org.rrd4j.core.RrdBackendFactory;
+import org.rrd4j.core.Util;
+
+import java.io.IOException;
+
+class Def extends Source {
+    private final String path;
+    private final String dsName;
+    private final RrdBackendFactory backend;
+    private final ConsolFun consolFun;
+    private FetchData fetchData;
+
+    Def(String name, FetchData fetchData) {
+        this(name, name, fetchData);
+    }
+
+    Def(String name, String dsName, FetchData fetchData) {
+        this(name,
+                fetchData.getRequest().getParentDb().getPath(),
+                dsName, fetchData.getRequest().getConsolFun(),
+                fetchData.getRequest().getParentDb().getRrdBackend().getFactory()
+                );
+        this.fetchData = fetchData;
+    }
+
+    Def(String name, String path, String dsName, ConsolFun consolFunc) {
+        this(name, path, dsName, consolFunc, null);
+    }
+
+    Def(String name, String path, String dsName, ConsolFun consolFunc, RrdBackendFactory backend) {
+        super(name);
+        this.path = path;
+        this.dsName = dsName;
+        this.consolFun = consolFunc;
+        this.backend = backend;
+    }
+
+    String getPath() {
+        return path;
+    }
+
+    String getCanonicalPath() throws IOException {
+        return Util.getCanonicalPath(path);
+    }
+
+    String getDsName() {
+        return dsName;
+    }
+
+    ConsolFun getConsolFun() {
+        return consolFun;
+    }
+
+    RrdBackendFactory getBackend() {
+        return backend;
+    }
+
+    boolean isCompatibleWith(Def def) throws IOException {
+        return getCanonicalPath().equals(def.getCanonicalPath()) &&
+                getConsolFun() == def.consolFun &&
+                ((backend == null && def.backend == null) ||
+                        (backend != null && def.backend != null && backend.equals(def.backend)));
+    }
+
+    void setFetchData(FetchData fetchData) {
+        this.fetchData = fetchData;
+    }
+
+    long[] getRrdTimestamps() {
+        return fetchData.getTimestamps();
+    }
+
+    double[] getRrdValues() {
+        return fetchData.getValues(dsName);
+    }
+
+    long getArchiveEndTime() {
+        return fetchData.getArcEndTime();
+    }
+
+    long getFetchStep() {
+        return fetchData.getStep();
+    }
+
+    /* (non-Javadoc)
+     * @see org.rrd4j.data.Source#getAggregates(long, long)
+     */
+    @Override
+    @Deprecated
+    Aggregates getAggregates(long tStart, long tEnd) {
+        long[] t = getRrdTimestamps();
+        double[] v = getRrdValues();
+        return new Aggregator(t, v).getAggregates(tStart, tEnd);
+    }
+
+    /* (non-Javadoc)
+     * @see org.rrd4j.data.Source#getPercentile(long, long, double)
+     */
+    @Override
+    @Deprecated
+    double getPercentile(long tStart, long tEnd, double percentile) {
+        long[] t = getRrdTimestamps();
+        double[] v = getRrdValues();
+        return new Aggregator(t, v).getPercentile(tStart, tEnd, percentile);
+    }
+
+    boolean isLoaded() {
+        return fetchData != null;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/LinearInterpolator.java b/apps/jrobin/java/src/org/rrd4j/data/LinearInterpolator.java
new file mode 100644
index 0000000000000000000000000000000000000000..53eb262681020c111b097fed8f91af18e0251942
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/LinearInterpolator.java
@@ -0,0 +1,273 @@
+package org.rrd4j.data;
+
+import org.rrd4j.core.Util;
+
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * <p>Class used to interpolate datasource values from the collection of (timestamp, values)
+ * points. This class is suitable for linear interpolation only.</p>
+ *
+ * <p>Interpolation algorithm returns different values based on the value passed to
+ * {@link #setInterpolationMethod(Method) setInterpolationMethod()}. If not set, interpolation
+ * method defaults to standard linear interpolation ({@link org.rrd4j.data.LinearInterpolator.Method#LINEAR}).
+ * Interpolation method handles NaN datasource
+ * values gracefully.</p>
+ */
+public class LinearInterpolator extends Plottable {
+
+    /**
+     * constant used to specify LEFT interpolation.
+     * See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
+     */
+    public static final int INTERPOLATE_LEFT = 0;
+    /**
+     * constant used to specify RIGHT interpolation.
+     * See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
+     */
+    public static final int INTERPOLATE_RIGHT = 1;
+    /**
+     * constant used to specify LINEAR interpolation (default interpolation method).
+     * See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
+     */
+    public static final int INTERPOLATE_LINEAR = 2;
+    /**
+     * constant used to specify LINEAR REGRESSION as interpolation method.
+     * See {@link #setInterpolationMethod(int) setInterpolationMethod()} for explanation.
+     */
+    public static final int INTERPOLATE_REGRESSION = 3;
+
+    /**
+     * A enumeration of interpolation methods
+     * @author Fabrice Bacchella
+     *
+     */
+    public enum Method {
+        LEFT,
+        RIGHT,
+        LINEAR,
+        REGRESSION;
+    }
+
+    private int lastIndexUsed = 0;
+    private Method interpolationMethod = Method.LINEAR;
+
+    private long[] timestamps;
+    private double[] values;
+
+    // used only if INTERPOLATE_BESTFIT is specified
+    double b0 = Double.NaN;
+    double b1 = Double.NaN;
+
+    /**
+     * Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
+     *
+     * @param timestamps timestamps in seconds
+     * @param values     corresponding datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if supplied arrays do not contain at least two values, or if
+     *                                  timestamps are not ordered, or array lengths are not equal.
+     */
+    public LinearInterpolator(long[] timestamps, double[] values) {
+        this.timestamps = timestamps;
+        this.values = values;
+        validate();
+    }
+
+    /**
+     * Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
+     *
+     * @param dates  Array of Date objects
+     * @param values corresponding datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if supplied arrays do not contain at least two values, or if
+     *                                  timestamps are not ordered, or array lengths are not equal.
+     */
+    public LinearInterpolator(Date[] dates, double[] values) {
+        this.values = values;
+        timestamps = new long[dates.length];
+        for (int i = 0; i < dates.length; i++) {
+            timestamps[i] = Util.getTimestamp(dates[i]);
+        }
+        validate();
+    }
+
+    /**
+     * Creates LinearInterpolator from arrays of timestamps and corresponding datasource values.
+     *
+     * @param dates  array of GregorianCalendar objects
+     * @param values corresponding datasource values
+     * @throws java.lang.IllegalArgumentException Thrown if supplied arrays do not contain at least two values, or if
+     *                                  timestamps are not ordered, or array lengths are not equal.
+     */
+    public LinearInterpolator(Calendar[] dates, double[] values) {
+        this.values = values;
+        timestamps = new long[dates.length];
+        for (int i = 0; i < dates.length; i++) {
+            timestamps[i] = Util.getTimestamp(dates[i]);
+        }
+        validate();
+    }
+
+    private void validate() {
+        boolean ok = true;
+        if (timestamps.length != values.length || timestamps.length < 2) {
+            ok = false;
+        }
+        for (int i = 0; i < timestamps.length - 1 && ok; i++) {
+            if (timestamps[i] >= timestamps[i + 1]) {
+                ok = false;
+            }
+        }
+        if (!ok) {
+            throw new IllegalArgumentException("Invalid plottable data supplied");
+        }
+    }
+
+    /**
+     * <p>Sets interpolation method to be used. Suppose that we have two timestamp/value pairs:<br>
+     * <code>(t, 100)</code> and <code>(t + 100, 300)</code>. Here are the results interpolator
+     * returns for t + 50 seconds, for various <code>interpolationMethods</code>:</p>
+     *
+     * <ul>
+     * <li><code>INTERPOLATE_LEFT:   100</code>
+     * <li><code>INTERPOLATE_RIGHT:  300</code>
+     * <li><code>INTERPOLATE_LINEAR: 200</code>
+     * </ul>
+     * <p>If not set, interpolation method defaults to <code>INTERPOLATE_LINEAR</code>.</p>
+     * 
+     * <p>The fourth available interpolation method is INTERPOLATE_REGRESSION. This method uses
+     * simple linear regression to interpolate supplied data with a simple straight line which does not
+     * necessarily pass through all data points. The slope of the best-fit line will be chosen so that the
+     * total square distance of real data points from from the best-fit line is at minimum.</p>
+     * 
+     * <p>This method is deprecated, one should use {@link #setInterpolationMethod(Method) setInterpolationMethod()} instead.</p>
+     * 
+     * <p>The full explanation of this interpolation method can be found
+     * <a href="http://www.JerryDallal.com/LHSP/slr.htm">here</a>.</p>
+     *
+     * @param interpolationMethod Should be <code>INTERPOLATE_LEFT</code>,
+     *                            <code>INTERPOLATE_RIGHT</code>, <code>INTERPOLATE_LINEAR</code> or
+     *                            <code>INTERPOLATE_REGRESSION</code>. Any other value will be interpreted as
+     *                            INTERPOLATE_LINEAR (default).
+     * @deprecated uses {@link #setInterpolationMethod(Method)} instead.
+     */
+    @Deprecated
+    public void setInterpolationMethod(int interpolationMethod) {
+        if (interpolationMethod >= Method.values().length || interpolationMethod < 0) {
+            setInterpolationMethod(Method.LINEAR);
+        } else {
+            setInterpolationMethod(Method.values()[interpolationMethod]);
+        }
+    }
+
+    /**
+     * <p>Sets interpolation method to be used. Suppose that we have two timestamp/value pairs:<br>
+     * <code>(t, 100)</code> and <code>(t + 100, 300)</code>. Here are the results interpolator
+     * returns for t + 50 seconds, for various <code>interpolationMethods</code>:</p>
+     *
+     * <ul>
+     * <li><code>LEFT:   100</code>
+     * <li><code>RIGHT:  300</code>
+     * <li><code>LINEAR: 200</code>
+     * </ul>
+     * <p>If not set, interpolation method defaults to <code>INTERPOLATE_LINEAR</code>.</p>
+     * 
+     * <p>The fourth available interpolation method is REGRESSION. This method uses
+     * simple linear regression to interpolate supplied data with a simple straight line which does not
+     * necessarily pass through all data points. The slope of the best-fit line will be chosen so that the
+     * total square distance of real data points from from the best-fit line is at minimum.</p>
+     * 
+     * <p>The full explanation of this interpolation method can be found
+     * <a href="http://www.JerryDallal.com/LHSP/slr.htm">here</a>.</p>
+     *
+     * @param interpolationMethod a method from {@link org.rrd4j.data.LinearInterpolator.Method}.
+     */
+    public void setInterpolationMethod(Method interpolationMethod) {
+        if (interpolationMethod == Method.REGRESSION) {
+            calculateBestFitLine();
+        }
+        this.interpolationMethod = interpolationMethod;
+    }
+
+    private void calculateBestFitLine() {
+        int count = timestamps.length;
+        int validCount = 0;
+        double ts = 0.0;
+        double vs = 0.0;
+
+        for (int i = 0; i < count; i++) {
+            if (!Double.isNaN(values[i])) {
+                ts += timestamps[i];
+                vs += values[i];
+                validCount++;
+            }
+        }
+        if (validCount <= 1) {
+            // just one not-NaN point
+            b0 = b1 = Double.NaN;
+            return;
+        } else {
+            ts /= validCount;
+            vs /= validCount;
+        }
+        double s1 = 0;
+        double s2 = 0;
+        for (int i = 0; i < count; i++) {
+            if (!Double.isNaN(values[i])) {
+                double dt = timestamps[i] - ts;
+                double dv = values[i] - vs;
+                s1 += dt * dv;
+                s2 += dt * dt;
+            }
+        }
+        b1 = s2 != 0 ? s1 / s2 : Double.NaN;
+        b0 = vs - b1 * ts;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Method overridden from the base class. This method will be called by the framework. Call
+     * this method only if you need interpolated values in your code.
+     */
+    @Override
+    public double getValue(long timestamp) {
+        if (interpolationMethod == Method.REGRESSION) {
+            return b0 + b1 * timestamp;
+        }
+        int count = timestamps.length;
+        // check if out of range
+        if (timestamp < timestamps[0] || timestamp > timestamps[count - 1]) {
+            return Double.NaN;
+        }
+        // find matching segment
+        int startIndex = lastIndexUsed;
+        if (timestamp < timestamps[lastIndexUsed]) {
+            // backward reading, shift to the first timestamp
+            startIndex = 0;
+        }
+        for (int i = startIndex; i < count; i++) {
+            if (timestamps[i] == timestamp) {
+                return values[i];
+            }
+            if (i < count - 1 && timestamps[i] < timestamp && timestamp < timestamps[i + 1]) {
+                // matching segment found
+                lastIndexUsed = i;
+                switch (interpolationMethod) {
+                case LEFT:
+                    return values[i];
+                case RIGHT:
+                    return values[i + 1];
+                case LINEAR:
+                    double slope = (values[i + 1] - values[i]) /
+                    (timestamps[i + 1] - timestamps[i]);
+                    return values[i] + slope * (timestamp - timestamps[i]);
+                default:
+                    return Double.NaN;
+                }
+            }
+        }
+        // should not be here ever, but let's satisfy the compiler
+        return Double.NaN;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/NonRrdSource.java b/apps/jrobin/java/src/org/rrd4j/data/NonRrdSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..9b61636a0303d59d054c935404f05262553b0bc4
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/NonRrdSource.java
@@ -0,0 +1,12 @@
+package org.rrd4j.data;
+
+interface NonRrdSource {
+    /**
+     * <p>calculate.</p>
+     *
+     * @param tStart a long.
+     * @param tEnd a long.
+     * @param dataProcessor a {@link org.rrd4j.data.DataProcessor} object.
+     */
+    void calculate(long tStart, long tEnd, DataProcessor dataProcessor);
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/Normalizer.java b/apps/jrobin/java/src/org/rrd4j/data/Normalizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ed014faa1e6234ad35f3c9f851660fda4104322
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/Normalizer.java
@@ -0,0 +1,67 @@
+package org.rrd4j.data;
+
+import org.rrd4j.core.Util;
+
+import java.util.Arrays;
+
+class Normalizer {
+    private final long[] timestamps;
+    final int count;
+    final long step;
+
+    Normalizer(long[] timestamps) {
+        this.timestamps = timestamps;
+        this.step = timestamps[1] - timestamps[0];
+        this.count = timestamps.length;
+    }
+
+    double[] normalize(long[] rawTimestamps, double[] rawValues) {
+        int rawCount = rawTimestamps.length;
+        long rawStep = rawTimestamps[1] - rawTimestamps[0];
+        // check if we have a simple match
+        if (rawCount == count && rawStep == step && rawTimestamps[0] == timestamps[0]) {
+            return getCopyOf(rawValues);
+        }
+        // reset all normalized values to NaN
+        double[] values = new double[count];
+        Arrays.fill(values, Double.NaN);
+        double[] weights = new double[count];
+        Arrays.fill(weights, Double.NaN);
+        for (int rawSeg = 0, seg = 0; rawSeg < rawCount && seg < count; rawSeg++) {
+            double rawValue = rawValues[rawSeg];
+            if (!Double.isNaN(rawValue)) {
+                long rawLeft = rawTimestamps[rawSeg] - rawStep;
+                while (seg < count && rawLeft >= timestamps[seg]) {
+                    seg++;
+                }
+                boolean overlap = true;
+                for (int fillSeg = seg; overlap && fillSeg < count; fillSeg++) {
+                    long left = timestamps[fillSeg] - step;
+                    long t1 = Math.max(rawLeft, left);
+                    long t2 = Math.min(rawTimestamps[rawSeg], timestamps[fillSeg]);
+                    if (t1 < t2) {
+                        values[fillSeg] = Util.sum(values[fillSeg], (t2 - t1) * rawValues[rawSeg]);
+                        weights[fillSeg] = Util.sum(weights[fillSeg], (double)(t2 - t1));
+                    }
+                    else {
+                        overlap = false;
+                    }
+                }
+            }
+        }
+        for (int seg = 0; seg < count; seg++) {
+            if (!Double.isNaN(weights[seg])) {
+                values[seg] /= weights[seg];
+            }
+        }
+        return values;
+    }
+
+    private static double[] getCopyOf(double[] rawValues) {
+        int n = rawValues.length;
+        double[] values = new double[n];
+        System.arraycopy(rawValues, 0, values, 0, n);
+        return values;
+    }
+}
+
diff --git a/apps/jrobin/java/src/org/rrd4j/data/PDef.java b/apps/jrobin/java/src/org/rrd4j/data/PDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc215b22055ca298858f8ca7792587d378b334fd
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/PDef.java
@@ -0,0 +1,20 @@
+package org.rrd4j.data;
+
+class PDef extends Source implements NonRrdSource  {
+    private final Plottable plottable;
+
+    PDef(String name, Plottable plottable) {
+        super(name);
+        this.plottable = plottable;
+    }
+
+    /** {@inheritDoc} */
+    public void calculate(long tStart, long tEnd, DataProcessor dataProcessor) {
+        long[] times = getTimestamps();
+        double[] vals = new double[times.length];
+        for (int i = 0; i < times.length; i++) {
+            vals[i] = plottable.getValue(times[i]);
+        }
+        setValues(vals);
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/Plottable.java b/apps/jrobin/java/src/org/rrd4j/data/Plottable.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8fb3a9c6cf7d930dbda13a4ce4dc3e86cc9f680
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/Plottable.java
@@ -0,0 +1,20 @@
+package org.rrd4j.data;
+
+/**
+ * Abstract class to be used for custom datasources.
+ *
+ * <p>If you wish to use a custom datasource in a graph, you should create a class implementing this interface
+ * that represents that datasource, and then pass this class on to the RrdGraphDef.</p>
+ */
+public abstract class Plottable {
+    /**
+     * Retrieves datapoint value based on a given timestamp.
+     * Use this method if you only have one series of data in this class.
+     *
+     * @param timestamp Timestamp in seconds for the datapoint.
+     * @return Double value of the datapoint.
+     */
+    public double getValue(long timestamp) {
+        return Double.NaN;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/RpnCalculator.java b/apps/jrobin/java/src/org/rrd4j/data/RpnCalculator.java
new file mode 100644
index 0000000000000000000000000000000000000000..24946c9c68314e99a5af26c60dc252beedd72186
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/RpnCalculator.java
@@ -0,0 +1,837 @@
+package org.rrd4j.data;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.rrd4j.core.Util;
+
+import com.tomgibara.crinch.hashing.PerfectStringHash;
+
+class RpnCalculator {
+    private enum Token_Symbol {
+        TKN_VAR("") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(s.token.values[s.slot]);
+                s.token_rpi = s.rpi;
+            }
+        },
+        TKN_NUM("") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(s.token.number);
+            }
+        },
+
+        // Arithmetics
+        TKN_PLUS("+") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.pop() + c.pop());
+            }
+        },
+        TKN_ADDNAN("ADDNAN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x1 = c.pop();
+                double x2 = c.pop();
+                c.push(Double.isNaN(x1) ? x2 : (Double.isNaN(x2) ? x1 : x1 + x2));
+            }
+        },
+        TKN_MINUS("-") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 - x2);
+            }
+        },
+        TKN_MULT("*") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.pop() * c.pop());
+            }
+        },
+        TKN_DIV("/") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 / x2);
+            }
+        },
+        TKN_MOD("%") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 % x2);
+            }
+        },
+
+        TKN_SIN("SIN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.sin(c.pop()));
+            }
+        },
+        TKN_COS("COS") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.cos(c.pop()));
+            }
+        },
+        TKN_LOG("LOG") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.log(c.pop()));
+            }
+        },
+        TKN_EXP("EXP") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.exp(c.pop()));
+            }
+        },
+        TKN_SQRT("SQRT") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.sqrt(c.pop()));
+            }
+        },
+        TKN_ATAN("ATAN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.atan(c.pop()));
+            }
+        },
+        TKN_ATAN2("ATAN2") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(Math.atan2(x1, x2));
+            }
+        },
+
+        TKN_FLOOR("FLOOR") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.floor(c.pop()));
+            }
+        },
+        TKN_CEIL("CEIL") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.ceil(c.pop()));
+            }
+        },
+
+        TKN_DEG2RAD("DEG2RAD") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.toRadians(c.pop()));
+            }
+        },
+        TKN_RAD2DEG("RAD2DEG") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.toDegrees(c.pop()));
+            }
+        },
+        TKN_ROUND("ROUND") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.round(c.pop()));
+            }
+        },
+        TKN_POW("POW") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(Math.pow(x1, x2));
+            }
+        },
+        TKN_ABS("ABS") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.abs(c.pop()));
+            }
+        },
+        TKN_RANDOM("RANDOM") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.random());
+            }
+        },
+        TKN_RND("RND") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.floor(c.pop() * Math.random()));
+            }
+        },
+
+        // Boolean operators
+        TKN_UN("UN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Double.isNaN(c.pop()) ? 1 : 0);
+            }
+        },
+        TKN_ISINF("ISINF") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Double.isInfinite(c.pop()) ? 1 : 0);
+            }            
+        },
+        TKN_LT("LT") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 < x2 ? 1 : 0);
+            }
+        },
+        TKN_LE("LE") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 <= x2 ? 1 : 0);
+            }
+        },
+        TKN_GT("GT") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 > x2 ? 1 : 0);
+            }
+        },
+        TKN_GE("GE") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 >= x2 ? 1 : 0);
+            }
+        },
+        TKN_EQ("EQ") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 == x2 ? 1 : 0);
+            }
+        },
+        TKN_NE("NE") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 != x2 ? 1 : 0);
+            }
+        },
+        TKN_IF("IF") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x3 = c.pop();
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 != 0 ? x2 : x3);
+            }
+        },
+
+        // Comparing values
+        TKN_MIN("MIN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.min(c.pop(), c.pop()));
+            }
+        },
+        TKN_MAX("MAX") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.max(c.pop(), c.pop()));
+            }
+        },
+        TKN_MINNAN("MINNAN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x1 = c.pop();
+                double x2 = c.pop();
+                c.push(Double.isNaN(x1) ? x2 : (Double.isNaN(x2) ? x1 : Math.min(x1, x2)));
+            }
+        },
+        TKN_MAXNAN("MAXNAN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x1 = c.pop();
+                double x2 = c.pop();
+                c.push(Double.isNaN(x1) ? x2 : (Double.isNaN(x2) ? x1 : Math.max(x1, x2)));
+            }
+        },
+        TKN_LIMIT("LIMIT") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x3 = c.pop();
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x1 < x2 || x1 > x3 ? Double.NaN : x1);
+            }
+        },
+
+        // Processing the stack directly
+        TKN_DUP("DUP") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.peek());
+            }
+        },
+        TKN_EXC("EXC") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(x2);
+                c.push(x1);
+            }
+        },
+        TKN_POP("POP") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.pop();
+            }
+        },
+
+        // Special values
+        TKN_UNKN("UNKN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Double.NaN);
+            }
+        },
+        TKN_PI("PI") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.PI);
+            }
+        },
+        TKN_E("E") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Math.E);
+            }
+        },
+        TKN_INF("INF") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Double.POSITIVE_INFINITY);
+            }
+        },
+        TKN_NEGINF("NEGINF") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Double.NEGATIVE_INFINITY);
+            }
+        },
+
+        // Logical operator
+        TKN_AND("AND") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push((x1 != 0 && x2 != 0) ? 1 : 0);
+            }
+        },
+        TKN_OR("OR") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push((x1 != 0 || x2 != 0) ? 1 : 0);
+            }
+        },
+        TKN_XOR("XOR") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x2 = c.pop();
+                double x1 = c.pop();
+                c.push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0)) ? 1 : 0);
+            }
+        },
+
+        TKN_PREV("PREV") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push((s.slot == 0) ? Double.NaN : s.token.values[s.slot - 1]);
+            }
+        },
+
+        //Time and date operator
+        TKN_STEP("STEP") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.timeStep);
+            }
+        },
+        TKN_NOW("NOW") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(Util.getTime());
+            }
+        },
+        TKN_TIME("TIME") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.timestamps[s.slot]);
+            }
+        },
+        TKN_LTIME("LTIME") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                TimeZone tz = s.getTimeZone();
+                c.push((double)(c.timestamps[s.slot] + ((long) tz.getOffset(c.timestamps[s.slot]) / 1000D)));
+            }
+        },
+        TKN_YEAR("YEAR") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.getCalendarField(c.pop(), Calendar.YEAR));
+            }
+        },
+        TKN_MONTH("MONTH") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.getCalendarField(c.pop(), Calendar.MONTH) + 1);
+            }
+        },
+        TKN_DATE("DATE") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.getCalendarField(c.pop(), Calendar.DAY_OF_MONTH));
+            }
+        },
+        TKN_HOUR("HOUR") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.getCalendarField(c.pop(), Calendar.HOUR_OF_DAY));
+            }
+        },
+        TKN_MINUTE("MINUTE") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.getCalendarField(c.pop(), Calendar.MINUTE));
+            }
+        },
+        TKN_SECOND("SECOND") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.getCalendarField(c.pop(), Calendar.SECOND));
+            }
+        },
+        TKN_WEEK("WEEK") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(c.getCalendarField(c.pop(), Calendar.WEEK_OF_YEAR));
+            }
+        },
+        TKN_SIGN("SIGN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                double x1 = c.pop();
+                c.push(Double.isNaN(x1) ? Double.NaN : x1 > 0 ? +1 : x1 < 0 ? -1 : 0);
+            }
+        },
+        TKN_SORT("SORT") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                int n = (int) c.pop();
+                double[] array = new double[n];
+                for(int i = 0; i < n; i++) {
+                    array[i] = c.pop();
+                }
+                Arrays.sort(array);
+                for (int i = 0; i < n; i++) {
+                    c.push(array[i]);
+                }
+            }
+        },
+        TKN_REV("REV") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                int n = (int) c.pop();
+                double[] array = new double[n];
+                for(int i = 0; i < n; i++) {
+                    array[i] = c.pop();
+                }
+                for (int i = 0; i < n; i++) {
+                    c.push(array[i]);
+                }
+            }
+        },
+        TKN_AVG("AVG"){
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                int count = 0;
+                int n = (int) c.pop();
+                double sum = 0.0;
+                while (n > 0) {
+                    double x1 = c.pop();
+                    n--;
+
+                    if (Double.isNaN(x1)) {
+                        continue;
+                    }
+                    sum += x1;
+                    count++;
+                }
+                if (count > 0) {
+                    c.push(sum / count);
+                } else {
+                    c.push(Double.NaN);
+                }
+            }
+        },
+        TKN_COUNT("COUNT") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.push(s.slot + 1.0);
+            }
+        },
+        TKN_TREND("TREND") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                int dur = (int) c.pop();
+                c.pop();
+                /*
+                 * OK, so to match the output from rrdtool, we have to go *forward* 2 timeperiods.
+                 * So at t[59] we use the average of t[1]..t[61]
+                 *
+                 */
+
+                if ((s.slot+1) < Math.ceil(dur / c.timeStep)) {
+                    c.push(Double.NaN);
+                } else {
+                    double[] vals = c.dataProcessor.getValues(c.tokens[s.token_rpi].variable);
+                    boolean ignorenan = s.token.id == TKN_TRENDNAN;
+                    double accum = 0.0;
+                    int count = 0;
+
+                    int start = (int) (Math.ceil(dur / c.timeStep));
+                    int row = 2;
+                    while ((s.slot + row) > vals.length) {
+                        row --;
+                    }
+
+                    for(; start > 0; start--) {
+                        double val = vals[s.slot + row - start];
+                        if (ignorenan || !Double.isNaN(val)) {
+                            accum = Util.sum(accum, val);
+                            ++count;
+                        }
+                    }
+                    c.push((count == 0) ? Double.NaN : (accum / count));
+                }
+            }
+        },
+        TKN_TRENDNAN("TRENDNAN") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                TKN_TREND.do_method(c, s);
+            }
+        },
+        TKN_PREDICT("PREDICT") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                c.pop(); // Clear the value of our variable
+
+                /* the local averaging window (similar to trend, but better here, as we get better statistics thru numbers)*/
+                int locstepsize = (int) c.pop();
+                /* the number of shifts and range-checking*/
+                int num_shifts = (int) c.pop();
+                double[] multipliers;
+
+                // handle negative shifts special
+                if (num_shifts < 0) {
+                    multipliers = new double[1];
+                    multipliers[0] = c.pop();
+                } else {
+                    multipliers = new double[num_shifts];
+                    for(int i = 0; i < num_shifts; i++) {
+                        multipliers[i] = c.pop();
+                    }
+                }
+
+                /* the real calculation */
+                double val = Double.NaN;
+
+                /* the info on the datasource */
+                double[] vals = c.dataProcessor.getValues(c.tokens[s.rpi-1].variable);
+
+                int locstep = (int) Math.ceil((float) locstepsize / (float) c.timeStep);
+
+                /* the sums */
+                double sum = 0;
+                double sum2 = 0;
+                int count = 0;
+
+                /* now loop for each position */
+                int doshifts = Math.abs(num_shifts);
+                for (int loop = 0; loop < doshifts; loop++) {
+                    /* calculate shift step */
+                    int shiftstep;
+                    if (num_shifts < 0) {
+                        shiftstep = loop * (int) multipliers[0];
+                    } else {
+                        shiftstep = (int) multipliers[loop];
+                    }
+                    if (shiftstep < 0) {
+                        throw new RuntimeException("negative shift step not allowed: " + shiftstep);
+                    }
+                    shiftstep = (int) Math.ceil((float) shiftstep / (float) c.timeStep);
+                    /* loop all local shifts */
+                    for (int i = 0; i <= locstep; i++) {
+                        int offset = shiftstep + i;
+                        if ((offset >= 0) && (offset < s.slot)) {
+                            /* get the value */
+                            val = vals[s.slot - offset];
+
+                            /* and handle the non NAN case only*/
+                            if (!Double.isNaN(val)) {
+                                sum = Util.sum(sum, val);
+                                sum2 = Util.sum(sum2, val * val);
+                                count++;
+                            }
+                        }
+                    }
+                }
+                /* do the final calculations */
+                val = Double.NaN;
+                if (s.token.id == TKN_PREDICT) {  /* the average */
+                    if (count > 0) {
+                        val = sum / (double) count;
+                    }
+                } else {
+                    if (count > 1) { /* the sigma case */
+                        val = count * sum2 - sum * sum;
+                        if (val < 0) {
+                            val = Double.NaN;
+                        } else {
+                            val = Math.sqrt(val / (count * (count - 1.0)));
+                        }
+                    }
+                }
+                c.push(val);
+            }
+
+        },
+        TKN_PREDICTSIGMA("PREDICTSIGMA") {
+            @Override
+            void do_method(RpnCalculator c, State s) {
+                TKN_PREDICT.do_method(c, s);
+            }
+        };
+
+        public final String token_string;
+        Token_Symbol(String token_string) {
+            this.token_string = token_string;
+        }
+        abstract void do_method(RpnCalculator c, State s);
+    }
+
+    private static final Token_Symbol[] symbols;
+    private static final PerfectStringHash perfect;
+    static
+    {
+        List<String> tokenStrings = new ArrayList<String>(Token_Symbol.values().length);
+        for(Token_Symbol s: Token_Symbol.values()) {
+            if(! s.token_string.isEmpty()) {
+                tokenStrings.add(s.token_string);
+            }
+        }
+
+        String[] array = tokenStrings.toArray(new String[tokenStrings.size()]);
+        perfect = new PerfectStringHash(array);
+        symbols = new Token_Symbol[tokenStrings.size()];
+        for(Token_Symbol s: Token_Symbol.values()) {
+            int hash = perfect.hashAsInt(s.token_string);
+            if(hash >= 0) {
+                symbols[hash] = s;
+            }
+        }
+    }
+
+    private final String rpnExpression;
+    private final String sourceName;
+    private final DataProcessor dataProcessor;
+
+    private final Token[] tokens;
+    private final RpnStack stack = new RpnStack();
+    private final double[] calculatedValues;
+    private final long[] timestamps;
+    private final double timeStep;
+    private final List<String> sourcesNames;
+
+    RpnCalculator(String rpnExpression, String sourceName, DataProcessor dataProcessor) {
+        this.rpnExpression = rpnExpression;
+        this.sourceName = sourceName;
+        this.dataProcessor = dataProcessor;
+        this.timestamps = dataProcessor.getTimestamps();
+        this.timeStep = (double)(timestamps[1] - timestamps[0]);
+        this.calculatedValues = new double[timestamps.length];
+        this.sourcesNames = Arrays.asList(dataProcessor.getSourceNames());
+        String[] tokensString = rpnExpression.split(" *, *");
+        tokens = new Token[tokensString.length];
+        for (int i = 0; i < tokensString.length; i++) {
+            tokens[i] = createToken(tokensString[i].trim());
+        }
+    }
+
+    private Token createToken(String parsedText) {
+        Token token;
+        int hash = perfect.hashAsInt(parsedText);
+        if (hash >= 0 ){
+            token = new Token(symbols[hash]);
+        }
+        else if (parsedText.equals("PREV")) {
+            token = new Token(Token_Symbol.TKN_PREV, sourceName, calculatedValues);
+        }
+        else if (parsedText.startsWith("PREV(") && parsedText.endsWith(")")) {
+            String variable = parsedText.substring(5, parsedText.length() - 1);
+            token = new Token(Token_Symbol.TKN_PREV, variable, dataProcessor.getValues(variable));
+        }
+        else if (Util.isDouble(parsedText)) {
+            token = new Token(Token_Symbol.TKN_NUM, Util.parseDouble(parsedText));
+        }
+        else if (sourcesNames.contains(parsedText)){
+            token = new Token(Token_Symbol.TKN_VAR, parsedText, dataProcessor.getValues(parsedText));
+        }
+        else {
+            throw new IllegalArgumentException("Unexpected RPN token encountered: " +  parsedText);
+        }
+        return token;
+    }
+
+    double[] calculateValues() {
+        State s = new State();
+        for (int slot = 0; slot < timestamps.length; slot++) {
+            resetStack();
+            s.rpi = 0;
+            s.token_rpi = -1;
+            for (Token token: tokens) {
+                s.token = token;
+                s.slot = slot;
+                token.id.do_method(this, s);
+                s.rpi++;
+            }
+            calculatedValues[slot] = pop();
+            // check if stack is empty only on the first try
+            if (slot == 0 && !isStackEmpty()) {
+                throw new IllegalArgumentException("Stack not empty at the end of calculation. " +
+                        "Probably bad RPN expression [" + rpnExpression + "]");
+            }
+        }
+        return calculatedValues;
+    }
+
+    private double getCalendarField(double timestamp, int field) {
+        Calendar calendar = Util.getCalendar((long) (timestamp));
+        return calendar.get(field);
+    }
+
+    private void push(final double x) {
+        stack.push(x);
+    }
+
+    private double pop() {
+        return stack.pop();
+    }
+
+    private double peek() {
+        return stack.peek();
+    }
+
+    private void resetStack() {
+        stack.reset();
+    }
+
+    private boolean isStackEmpty() {
+        return stack.isEmpty();
+    }
+
+    private static final class RpnStack {
+        private static final int MAX_STACK_SIZE = 1000;
+        private double[] stack = new double[MAX_STACK_SIZE];
+        private int pos = 0;
+
+        void push(double x) {
+            if (pos >= MAX_STACK_SIZE) {
+                throw new IllegalArgumentException("PUSH failed, RPN stack full [" + MAX_STACK_SIZE + "]");
+            }
+            stack[pos++] = x;
+        }
+
+        double pop() {
+            if (pos <= 0) {
+                throw new IllegalArgumentException("POP failed, RPN stack is empty");
+            }
+            return stack[--pos];
+        }
+
+        double peek() {
+            if (pos <= 0) {
+                throw new IllegalArgumentException("PEEK failed, RPN stack is empty");
+            }
+            return stack[pos - 1];
+        }
+
+        void reset() {
+            pos = 0;
+        }
+
+        boolean isEmpty() {
+            return pos <= 0;
+        }
+    }
+
+    private final class State {
+        private int token_rpi;
+        private int rpi;
+        Token token;
+        int slot;
+        TimeZone getTimeZone() {
+            return RpnCalculator.this.dataProcessor.getTimeZone();
+        }
+    }
+
+    private static final class Token {
+        final Token_Symbol id;
+        final double number;
+        final String variable;
+        final double[] values;
+        Token(Token_Symbol id) {
+            this.id = id;
+            this.values = null;
+            this.variable = "";
+            this.number = Double.NaN;
+        }
+        Token(Token_Symbol id, String variable, double[] values) {
+            this.id = id;
+            this.variable = variable;
+            this.values = values;
+            this.number = Double.NaN;
+        }
+        Token(Token_Symbol id, double number) {
+            this.id = id;
+            this.values = null;
+            this.variable = "";
+            this.number = number;
+        }
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/Source.java b/apps/jrobin/java/src/org/rrd4j/data/Source.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a79c6c5c00d54d80c08eb6be2ab5bbb94374c82
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/Source.java
@@ -0,0 +1,59 @@
+package org.rrd4j.data;
+
+abstract class Source {
+    private final String name;
+
+    protected double[] values;
+    protected long[] timestamps;
+
+    Source(String name) {
+        this.name = name;
+    }
+
+    String getName() {
+        return name;
+    }
+
+    void setValues(double[] values) {
+        this.values = values;
+    }
+
+    void setTimestamps(long[] timestamps) {
+        this.timestamps = timestamps;
+    }
+
+    double[] getValues() {
+        return values;
+    }
+
+    long[] getTimestamps() {
+        return timestamps;
+    }
+
+    /**
+     * @param tStart
+     * @param tEnd
+     * @return
+     * @deprecated This method is deprecated. Uses instance of {@link org.rrd4j.data.Variable}, used with {@link org.rrd4j.data.DataProcessor.addDatasource(String, String, Variable)}
+     */
+    @Deprecated
+    Aggregates getAggregates(long tStart, long tEnd) {
+        Aggregator agg = new Aggregator(timestamps, values);
+        return agg.getAggregates(tStart, tEnd);
+    }
+
+    /**
+     * @param tStart
+     * @param tEnd
+     * @param percentile
+     * @return
+     * @deprecated This method is deprecated. Uses instance of {@link org.rrd4j.data.Variable.PERCENTILE}, used with {@link org.rrd4j.data.DataProcessor.addDatasource(String, String, Variable)}
+     */
+    @Deprecated
+    double getPercentile(long tStart, long tEnd, double percentile) {
+        Variable vpercent = new Variable.PERCENTILE((float) percentile);
+        vpercent.calculate(this, tStart, tEnd);
+        return vpercent.getValue().value;
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/VDef.java b/apps/jrobin/java/src/org/rrd4j/data/VDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a5632999922443508f56580f4acf1a6e78ae956
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/VDef.java
@@ -0,0 +1,41 @@
+package org.rrd4j.data;
+
+import java.util.Arrays;
+
+class VDef extends Source implements NonRrdSource  {
+    private final String defName;
+    private final Variable var;
+
+    VDef(String name, String defName, Variable aggr) {
+        super(name);
+        this.defName = defName;
+        this.var = aggr;
+    }
+
+    String getDefName() {
+        return defName;
+    }
+
+    /** {@inheritDoc} */
+    public void calculate(long tStart, long tEnd, DataProcessor dataProcessor) {
+        String defName = getDefName();
+        Source source = dataProcessor.getSource(defName);
+        var.calculate(source, tStart, tEnd);
+    }
+
+    public Variable.Value getValue() {
+        return var.getValue();
+    }
+
+    /* (non-Javadoc)
+     * @see org.rrd4j.data.Source#getValues()
+     */
+    @Override
+    double[] getValues() {
+        int count = getTimestamps().length;
+        double[] values = new double[count];
+        Arrays.fill(values, var.getValue().value);
+        return values;
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/Variable.java b/apps/jrobin/java/src/org/rrd4j/data/Variable.java
new file mode 100644
index 0000000000000000000000000000000000000000..2668032cfc5bd6a919efee5489c3c17931d7525b
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/Variable.java
@@ -0,0 +1,506 @@
+package org.rrd4j.data;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.rrd4j.core.Util;
+
+/**
+ *  An abstract class to help extract single value from a set of value (VDEF in rrdtool)
+ *  
+ *  It can be used to add new fancy statistical calculation with rrd values 
+ *
+ */
+public abstract class Variable {
+
+    /**
+     * This class store both the value and the time stamp
+     * It will be used by graph rendering legend
+     */
+    public static final class Value {
+        public final double value;
+        public final long timestamp;
+        Value(long timestamp, double value) {
+            this.value = value;
+            this.timestamp = timestamp;
+        }
+    };
+
+    public static final Value INVALIDVALUE = new Value(0, Double.NaN);
+
+    private Value val = null;
+
+    /**
+     * Used to calculate the needed value from a source, this method call fill.
+     * @param s
+     * @param start
+     * @param end
+     */
+    void calculate(Source s, long start, long end) {
+        long step = s.timestamps[1] - s.timestamps[0];
+        int first = -1;
+        int last = -1;
+        // Iterate over array, stop then end cursor reach start or when both start and end has been found
+        // It also stop if cursor cross other side boundary
+        for(int i = 0, j = s.timestamps.length - 1 ; ( last == -1 && j > first ) || ( first == -1 && ( last == -1 || i < last )  ) ; i++, j--) {
+            if(first == -1) {
+                long leftdown = Math.max(s.timestamps[i] - step, start);
+                long rightdown = Math.min(s.timestamps[i], end);
+                if(rightdown > leftdown) {
+                    first = i;
+                }
+            }
+
+            if(last == -1) {
+                long leftup = Math.max(s.timestamps[j] - step, start);
+                long rightup = Math.min(s.timestamps[j], end);
+                if(rightup > leftup ) {
+                    last = j;
+                }
+            }
+        }
+        if( first == -1 || last == -1) {
+            throw new RuntimeException("Invalid range");
+        }
+        if(s instanceof VDef) {
+            // Already a variable, just check if it fits
+            Value v = ((VDef) s).getValue();
+            // No time stamp, or not time stamped value, keep it
+            if(v.timestamp == 0) {
+                val = v;
+            }
+            else {
+                if(v.timestamp < end && v.timestamp > start) {
+                    val = v;
+                }
+                else {
+                    val = new Value(0, Double.NaN);
+                }
+            }
+        }
+        else {
+            long[] timestamps = new long[ last - first + 1];
+            System.arraycopy(s.timestamps, first, timestamps, 0, timestamps.length);
+            double[] values = new double[ last - first + 1];
+            System.arraycopy(s.getValues(), first, values, 0, values.length);
+            val = fill(timestamps, values, start, end);
+        }
+    }
+
+    public Value getValue() {
+        assert val != null : "Used before calculation";
+        return val;
+    }
+
+    /**
+     * This method is call with the needed values, extracted from the datasource to do the calculation.
+     * 
+     * Value is to be filled with both the double value and a possible timestamp, when it's used to find
+     * a specific point
+     * 
+     * @param timestamps the timestamps for the value
+     * @param values the actual values
+     * @param start the start of the period
+     * @param end the end of the period
+     * @return a filled Value object
+     */
+    protected abstract Value fill(long timestamps[], double[] values, long start, long end);
+
+    /**
+     * Find the first valid data point and it's timestamp
+     *
+     */
+    public static class FIRST extends Variable {
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            for(int i = 0; i < values.length; i++) {
+                if( timestamps[i] > start && timestamps[i] < end && ! Double.isNaN(values[i])) {
+                    return new Value(timestamps[i], values[i]);
+                }
+            }
+            return new Value(0, Double.NaN);
+        }
+    }
+
+    /**
+     * Find the first last valid point and it's timestamp
+     *
+     */
+    public static class LAST extends Variable {
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            for(int i = values.length - 1 ; i >=0 ; i--) {
+                if( ! Double.isNaN(values[i]) ) {
+                    return new Value(timestamps[i], values[i]);
+                }
+            }
+            return new Value(0, Double.NaN);
+        }
+    }
+
+    /**
+     * The smallest of the data points and it's time stamp (the first one) is stored.
+     *
+     */
+    public static class MIN extends Variable {
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            long timestamp = 0;
+            double value = Double.NaN;
+            for(int i = values.length -1 ; i >=0 ; i--) {
+                if(! Double.isNaN(values[i]) && Double.isNaN(value)) {
+                    timestamp = timestamps[i];
+                    value = values[i];
+                } else if( ! Double.isNaN(values[i]) && value > values[i]) {
+                    timestamp = timestamps[i];
+                    value = values[i];
+                }
+            }
+            return new Value(timestamp, value);
+        }
+    }
+
+    /**
+     * The biggest of the data points and it's time stamp (the first one) is stored.
+     *
+     */
+    public static class MAX extends Variable {
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            long timestamp = 0;
+            double value = Double.NaN;
+            for(int i = values.length -1 ; i >=0 ; i--) {
+                if(! Double.isNaN(values[i]) && Double.isNaN(value)) {
+                    timestamp = timestamps[i];
+                    value = values[i];
+                } else if(!Double.isNaN(values[i]) && value < values[i]) {
+                    timestamp = timestamps[i];
+                    value = values[i];
+                }
+            }
+            return new Value(timestamp, value);
+        }
+    }
+
+    /**
+     * Calculate the sum of the data points.
+     *
+     */
+    public static class TOTAL extends Variable {
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            double value = Double.NaN;
+
+            for(double tempVal: values) {
+                value = Util.sum(value, tempVal);
+            }
+            return new Value(0, value * (timestamps[1] - timestamps[0]) );
+        }
+    }
+
+    /**
+     * Calculate the average of the data points.
+     *
+     */
+    public static class AVERAGE extends Variable {
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            double value = 0;
+            int count = 0;
+            for (int i = values.length - 1 ; i >= 0 ; i--) {
+                if ( !Double.isNaN(values[i]) ) {
+                    count++;
+                    value = Double.isNaN(value) ?  values[i] : values[i] + value;
+                }
+            }
+            if (! Double.isNaN(value) && count > 0) {
+                value = value / count;
+            }
+            else {
+                value = Double.NaN;
+            }
+            return new Value(0, value);
+        }
+    }
+
+    /**
+     * Calculate the standard deviation for the data point.
+     *
+     */
+    public static class STDDEV extends Variable {
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            double value = Double.NaN;
+            int count = 0;
+            double M = 0.0;
+            double S = 0.0;
+            // See Knuth TAOCP vol 2, 3rd edition, page 232 and http://www.johndcook.com/standard_deviation.html
+            for(double cursVal: values) {
+                if(Double.isNaN(cursVal))
+                    continue;
+                count++;
+                if(count == 1) {
+                    M = cursVal;
+                    S = 0;
+                }
+                else {
+                    double dM = cursVal - M;
+                    M += dM/count;
+                    S += dM * (cursVal - M);
+                }
+            }
+            if(count > 1) {
+                value = Math.sqrt( S/(count - 1) );
+            }
+            return new Value(0, value);
+        }
+    }
+
+    /**
+     * Store all the informations about a datasource point, for predictive and consistent sorting
+     *
+     */
+    static final class PercentElem {
+        long timestamp;
+        double value;
+
+        PercentElem(int pos, long timestamp, double value) {
+            this.timestamp = timestamp;
+            this.value = value;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            PercentElem other = (PercentElem) obj;
+            if (timestamp != other.timestamp)
+                return false;
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return Long.valueOf(timestamp).hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return String.format("[%d, %f]", timestamp, value);
+        }
+    }
+
+    /**
+     * The sort used by rrdtool for percent, where NaN &lt; -INF &lt; finite values &lt; INF
+     *
+     */
+    static final class ComparPercentElemen implements Comparator<PercentElem>, Serializable {
+        @Override
+        public final int compare(PercentElem arg0, PercentElem arg1) {
+            if(Double.isNaN(arg0.value) && Double.isNaN(arg1.value))
+                return Long.signum(arg0.timestamp - arg1.timestamp);
+            else if(Double.isNaN(arg0.value))
+                return -1;
+            else if (Double.isNaN(arg1.value))
+                return +1;
+            else  {
+                int compared = Double.compare(arg0.value, arg1.value);
+                if (compared == 0) {
+                    compared = Long.signum(arg0.timestamp - arg1.timestamp);
+                }
+                return compared;
+            }
+        }
+    }
+
+    /**
+     * Find the point at the n-th percentile.
+     *
+     */
+    public static class PERCENTILE extends Variable {
+        private final float percentile;
+        private final boolean withNaN;
+
+        protected PERCENTILE(float percentile, boolean withNaN) {
+            this.percentile = percentile;
+            this.withNaN = withNaN;
+        }
+
+        public PERCENTILE(double percentile) {
+            this((float) percentile, true);
+        }
+
+        public PERCENTILE(float percentile) {
+            this(percentile, true);
+        }
+
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            // valuesSet will be a set with NaN packet at the start
+            SortedSet<PercentElem> valuesSet = new TreeSet<PercentElem>(new ComparPercentElemen());
+            for(int i = 0 ; i < values.length ; i++) {
+                valuesSet.add(new PercentElem(i, timestamps[i], values[i]));
+            }
+
+            //If not with nan, just drop all nan (inferior to min value)
+            if( ! withNaN) {
+                valuesSet = valuesSet.tailSet(new PercentElem(0, 0, Double.NEGATIVE_INFINITY ));
+            }
+
+            PercentElem[] element = (PercentElem[]) valuesSet.toArray(new PercentElem[valuesSet.size()]);
+            int pos = Math.round(percentile * (element.length - 1) / 100);
+            // if we have anything left...
+            if (pos >= 0) {
+                double value = element[pos].value;
+                long timestamp = element[pos].timestamp;
+                return new Value(timestamp, value);
+            }
+            return new Value(0, Double.NaN);
+        }
+    }
+
+    public static class PERCENTILENAN extends PERCENTILE {
+
+        public PERCENTILENAN(float percentile) {
+            super(percentile, false);
+        }
+
+        public PERCENTILENAN(double percentile) {
+            super((float)percentile, false);
+        }
+    }
+
+    /**
+     * Calculate the slop of the least squares line.
+     *
+     */
+    public static class LSLSLOPE extends Variable {
+
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            int cnt = 0;
+            int lslstep = 0;
+            double SUMx = 0.0;
+            double SUMy = 0.0;
+            double SUMxy = 0.0;
+            double SUMxx = 0.0;
+            double lslslope;
+
+            for (int i = 0; i < values.length; i++) {
+                double value = values[i];
+
+                if (!Double.isNaN(value)) {
+                    cnt++;
+
+                    SUMx += lslstep;
+                    SUMxx += lslstep * lslstep;
+                    SUMy  += value;
+                    SUMxy += lslstep * value;
+
+                }
+                lslstep++;
+            }
+            double divisor = (SUMx * SUMx - cnt * SUMxx);
+            if (divisor != 0) {
+                /* Bestfit line by linear least squares method */
+                lslslope = (SUMx * SUMy - cnt * SUMxy) / divisor;
+                return new Value(0, lslslope);
+            } else {
+                return new Value(0, Double.NaN);
+            }
+        }
+
+    }
+
+    /**
+     * Calculate the y-intercept of the least squares line.
+     *
+     */
+    public static class LSLINT extends Variable {
+
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            int cnt = 0;
+            int lslstep = 0;
+            double SUMx = 0.0;
+            double SUMy = 0.0;
+            double SUMxy = 0.0;
+            double SUMxx = 0.0;
+            double lslslope;
+            double lslint;
+
+            for(int i = 0; i < values.length; i++) {
+                double value = values[i];
+
+                if (!Double.isNaN(value)) {
+                    cnt++;
+
+                    SUMx += lslstep;
+                    SUMxx += lslstep * lslstep;
+                    SUMy  += value;
+                    SUMxy += lslstep * value;
+                }
+                lslstep++;
+            }
+            double divisor = (SUMx * SUMx - cnt * SUMxx);
+            if(cnt > 0 && divisor != 0) {
+                /* Bestfit line by linear least squares method */
+                lslslope = (SUMx * SUMy - cnt * SUMxy) / divisor;
+                lslint = (SUMy - lslslope * SUMx) / cnt;
+                return new Value(0, lslint);
+            } else {
+                return new Value(0, Double.NaN);
+            }
+        }
+
+    }
+
+    /**
+     * Calculate the correlation coefficient of the least squares line.
+     *
+     */
+    public static class LSLCORREL extends Variable {
+
+        @Override
+        protected Value fill(long[] timestamps, double[] values, long start, long end) {
+            int cnt = 0;
+            int lslstep = 0;
+            double SUMx = 0.0;
+            double SUMy = 0.0;
+            double SUMxy = 0.0;
+            double SUMxx = 0.0;
+            double SUMyy = 0.0;
+            double lslcorrel;
+
+            for(int i = 0; i < values.length; i++) {
+                double value = values[i];
+
+                if (!Double.isNaN(value)) {
+                    cnt++;
+
+                    SUMx += lslstep;
+                    SUMxx += lslstep * lslstep;
+                    SUMy  += value;
+                    SUMxy += lslstep * value;
+                    SUMyy += value * value;
+                }
+                lslstep++;
+            }
+            if(cnt > 0) {
+                /* Bestfit line by linear least squares method */
+                lslcorrel =
+                        (SUMxy - (SUMx * SUMy) / cnt) /
+                        Math.sqrt((SUMxx - (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt));
+                return new Value(0, lslcorrel);
+            }
+            return new Value(0, Double.NaN);
+        }
+
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/data/package-info.java b/apps/jrobin/java/src/org/rrd4j/data/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..37ec9d37ade777ff8a7bfac0a8b19190ea537148
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/data/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * RRD4J data management.
+ */
+package org.rrd4j.data;
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Area.java b/apps/jrobin/java/src/org/rrd4j/graph/Area.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8a169a2f17c877d8b0a2dd8debc480ed9c3c4fb
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Area.java
@@ -0,0 +1,9 @@
+package org.rrd4j.graph;
+
+import java.awt.*;
+
+class Area extends SourcedPlotElement {
+    Area(String srcName, Paint color, SourcedPlotElement parent) {
+        super(srcName, color, parent);
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Axis.java b/apps/jrobin/java/src/org/rrd4j/graph/Axis.java
new file mode 100644
index 0000000000000000000000000000000000000000..71f506849e8ad0b1020cb4ce59692cfb17d904e6
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Axis.java
@@ -0,0 +1,7 @@
+package org.rrd4j.graph;
+
+public abstract class Axis implements RrdGraphConstants {
+
+    abstract boolean draw();
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/CDef.java b/apps/jrobin/java/src/org/rrd4j/graph/CDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..380d78b9840410af616dae3514af846d474e69f9
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/CDef.java
@@ -0,0 +1,16 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.data.DataProcessor;
+
+class CDef extends Source {
+    private final String rpnExpression;
+
+    CDef(String name, String rpnExpression) {
+        super(name);
+        this.rpnExpression = rpnExpression;
+    }
+
+    void requestData(DataProcessor dproc) {
+        dproc.addDatasource(name, rpnExpression);
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/CommentText.java b/apps/jrobin/java/src/org/rrd4j/graph/CommentText.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef0c34828a3580d5c14b8448c30936a11daa3b86
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/CommentText.java
@@ -0,0 +1,49 @@
+package org.rrd4j.graph;
+
+import java.util.Locale;
+
+import org.rrd4j.data.DataProcessor;
+
+class CommentText implements RrdGraphConstants {
+    protected final String text; // original text
+
+    String resolvedText; // resolved text
+    Markers marker; // end-of-text marker
+    boolean enabled; // hrule and vrule comments can be disabled at runtime
+    int x, y; // coordinates, evaluated in LegendComposer
+
+    CommentText(String text) {
+        this.text = text;
+    }
+
+    void resolveText(Locale l, DataProcessor dproc, ValueScaler valueScaler) {
+        resolvedText = text;
+        marker = null;
+        if (resolvedText != null) {
+            for (Markers m : Markers.values()) {
+                String tryMarker = m.marker;
+                if (resolvedText.endsWith(tryMarker)) {
+                    marker = m;
+                    resolvedText = resolvedText.substring(0, resolvedText.length() - tryMarker.length());
+                    trimIfGlue();
+                    break;
+                }
+            }
+        }
+        enabled = resolvedText != null;
+    }
+
+    void trimIfGlue() {
+        if (Markers.GLUE_MARKER == marker) {
+            resolvedText = resolvedText.replaceFirst("\\s+$", "");
+        }
+    }
+
+    boolean isPrint() {
+        return false;
+    }
+
+    boolean isValidGraphElement() {
+        return !isPrint() && enabled;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ConstantArea.java b/apps/jrobin/java/src/org/rrd4j/graph/ConstantArea.java
new file mode 100644
index 0000000000000000000000000000000000000000..87b272ddb33f14aaeffeee7f826de1e4a956cadc
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ConstantArea.java
@@ -0,0 +1,29 @@
+package org.rrd4j.graph;
+
+import java.awt.Paint;
+import java.util.Arrays;
+
+import org.rrd4j.data.DataProcessor;
+
+public class ConstantArea extends Area {
+    private final double value;
+
+    ConstantArea(double value, Paint color, SourcedPlotElement parent) {
+        super(Double.toString(value), color, parent);
+        this.value = value;
+    }
+
+    void assignValues(DataProcessor dproc) {
+        values = new double[dproc.getTimestamps().length];
+        Arrays.fill(values, value);
+        if(parent != null) {
+            double[] parentValues = parent.getValues();
+            for (int i = 0; i < values.length; i++) {
+                if (! Double.isNaN(parentValues[i])){
+                    values[i] += parentValues[i];
+                }
+            }
+        }
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ConstantLine.java b/apps/jrobin/java/src/org/rrd4j/graph/ConstantLine.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4749d9da70f9135594af41172b578d1a38ba209
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ConstantLine.java
@@ -0,0 +1,30 @@
+package org.rrd4j.graph;
+
+import java.awt.BasicStroke;
+import java.awt.Paint;
+import java.util.Arrays;
+
+import org.rrd4j.data.DataProcessor;
+
+public class ConstantLine extends Line {
+    private final double value;
+
+    ConstantLine(double value, Paint color, BasicStroke stroke, SourcedPlotElement parent) {
+        super(Double.toString(value), color, stroke, parent);
+        this.value = value;
+    }
+
+    void assignValues(DataProcessor dproc) {
+        values = new double[dproc.getTimestamps().length];
+        Arrays.fill(values, value);
+        if(parent != null) {
+            double[] parentValues = parent.getValues();
+            for (int i = 0; i < values.length; i++) {
+                if (! Double.isNaN(parentValues[i])){
+                    values[i] += parentValues[i];
+                }
+            }
+        }
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Def.java b/apps/jrobin/java/src/org/rrd4j/graph/Def.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5a2bef5373bca0c9767b8732c4b31042d2a3765
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Def.java
@@ -0,0 +1,42 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.data.DataProcessor;
+import org.rrd4j.ConsolFun;
+import org.rrd4j.core.RrdBackendFactory;
+
+class Def extends Source {
+    private final String rrdPath, dsName;
+    private final RrdBackendFactory backend;
+
+    private final ConsolFun consolFun;
+
+    Def(String name, String rrdPath, String dsName, ConsolFun consolFun) {
+        this(name, rrdPath, dsName, consolFun, (RrdBackendFactory)null);
+    }
+
+    @Deprecated
+    Def(String name, String rrdPath, String dsName, ConsolFun consolFun, String backend) {
+        super(name);
+        this.rrdPath = rrdPath;
+        this.dsName = dsName;
+        this.consolFun = consolFun;
+        this.backend = RrdBackendFactory.getFactory(backend);
+    }
+
+    Def(String name, String rrdPath, String dsName, ConsolFun consolFun, RrdBackendFactory backend) {
+        super(name);
+        this.rrdPath = rrdPath;
+        this.dsName = dsName;
+        this.consolFun = consolFun;
+        this.backend = backend;
+    }
+
+    void requestData(DataProcessor dproc) {
+        if (backend == null) {
+            dproc.addDatasource(name, rrdPath, dsName, consolFun);
+        }
+        else {
+            dproc.addDatasource(name, rrdPath, dsName, consolFun, backend);
+        }
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/DownSampler.java b/apps/jrobin/java/src/org/rrd4j/graph/DownSampler.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ab396161dab2d05d103472368bbd62d2d94ddad
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/DownSampler.java
@@ -0,0 +1,30 @@
+package org.rrd4j.graph;
+
+import java.util.Arrays;
+
+/**
+ * 
+ * A class that implement a downsampler, used to reduce the number of point to display.
+ * 
+ * @author Fabrice Bacchella
+ *
+ */
+public interface DownSampler {
+
+    public class DataSet {
+        public final long[] timestamps;
+        public final double[] values;
+        public DataSet(long[] timestamps, double[] values) {
+            this.timestamps = timestamps;
+            this.values = values;
+        }
+        @Override
+        public String toString() {
+            return "{\n  " + Arrays.toString(timestamps) + ",\n  " + Arrays.toString(values) + "}\n";
+        }
+
+    }
+
+    public DataSet downsize(long[] timestamps, double[] values);
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ElementsNames.java b/apps/jrobin/java/src/org/rrd4j/graph/ElementsNames.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6a2a3cd1958d1d6ca6f16daa5115e9758597a53
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ElementsNames.java
@@ -0,0 +1,54 @@
+package org.rrd4j.graph;
+
+/**
+ * The elements of the graph. For use in {@link RrdGraphDef#setColor(ElementsNames, java.awt.Paint)} method.
+ * 
+ * @author Fabrice Bacchella
+ *
+ */
+public enum ElementsNames {
+    /**
+     * The canvas color
+     */
+    canvas,
+    /**
+     * The background color
+     */
+    back,
+    /**
+     * The top-left graph shade color
+     */
+    shadea,
+    /**
+     * The bottom-right graph shade color
+     */
+    shadeb,
+    /**
+     * The minor grid color
+     */
+    grid,
+    /**
+     * The major grid color
+     */
+    mgrid,
+    /**
+     * The font color
+     */
+    font,
+    /**
+     * The frame color
+     */
+    frame,
+    /**
+     * The arrow color
+     */
+    arrow,
+    /**
+     * The x-axis color
+     */
+    xaxis,
+    /**
+     * The y-axis color
+     */
+    yaxis;
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/HRule.java b/apps/jrobin/java/src/org/rrd4j/graph/HRule.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fd66c015840bb4a47aab293a00960a8c501a5e5
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/HRule.java
@@ -0,0 +1,17 @@
+package org.rrd4j.graph;
+
+import java.awt.BasicStroke;
+import java.awt.Paint;
+
+class HRule extends Rule {
+    final double value;
+
+    HRule(double value, Paint color, LegendText legend, BasicStroke stroke) {
+        super(color, legend, stroke);
+        this.value = value;
+    }
+
+    void setLegendVisibility(double minval, double maxval, boolean forceLegend) {
+        legend.enabled &= (forceLegend || (value >= minval && value <= maxval));
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/HSpan.java b/apps/jrobin/java/src/org/rrd4j/graph/HSpan.java
new file mode 100644
index 0000000000000000000000000000000000000000..3166776aa88d6a4b08e102456ba440e4e666dfe2
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/HSpan.java
@@ -0,0 +1,25 @@
+package org.rrd4j.graph;
+
+import java.awt.Paint;
+
+class HSpan extends Span {
+    final double start;
+    final double end;
+
+    HSpan(double start, double end, Paint color, LegendText legend) {
+        super(color, legend);
+        this.start = start;
+        this.end = end;
+        assert(start < end);
+    }
+
+    private boolean checkRange(double v, double min, double max) {
+        return v >= min && v <= max;
+    }
+
+    void setLegendVisibility(double min, double max, boolean forceLegend) {
+        legend.enabled = legend.enabled && (forceLegend
+                || checkRange(start, min, max)
+                || checkRange(end, min, max));
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ImageParameters.java b/apps/jrobin/java/src/org/rrd4j/graph/ImageParameters.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d7135f3c11f23d93f52b66321e0b7e763a7fa56
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ImageParameters.java
@@ -0,0 +1,22 @@
+package org.rrd4j.graph;
+
+class ImageParameters {
+    long start, end;
+    double minval, maxval;
+    int unitsexponent;
+    double base;
+    double magfact;
+    char symbol;
+    double ygridstep;
+    int ylabfact;
+    double decimals;
+    int quadrant;
+    double scaledstep;
+    int xsize;
+    int ysize;
+    int xorigin;
+    int yorigin;
+    int unitslength;
+    int xgif, ygif;
+    String unit;
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ImageWorker.java b/apps/jrobin/java/src/org/rrd4j/graph/ImageWorker.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3814a5ff9d079b6af074aedf186855e1f1404aa
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ImageWorker.java
@@ -0,0 +1,239 @@
+package org.rrd4j.graph;
+
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Stroke;
+import java.awt.TexturePaint;
+import java.awt.font.LineMetrics;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Paths;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.imageio.stream.ImageOutputStream;
+
+class ImageWorker {
+    private static final String DUMMY_TEXT = "Dummy";
+
+    static final int IMG_BUFFER_CAPACITY = 10000; // bytes
+
+    private BufferedImage img;
+    private Graphics2D g2d;
+    private int imgWidth, imgHeight;
+    private AffineTransform initialAffineTransform;
+
+    ImageWorker(int width, int height) {
+        resize(width, height);
+    }
+
+    void resize(int width, int height) {
+        if (g2d != null) {
+            dispose();
+        }
+
+        imgWidth = width;
+        imgHeight = height;
+        img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+
+        g2d = img.createGraphics();
+        initialAffineTransform = g2d.getTransform();
+
+        setAntiAliasing(false);
+        setTextAntiAliasing(false);
+
+        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+    }
+
+    void clip(int x, int y, int width, int height) {
+        g2d.setClip(x, y, width, height);
+    }
+
+    void transform(int x, int y, double angle) {
+        g2d.translate(x, y);
+        g2d.rotate(angle);
+    }
+
+    void reset() {
+        g2d.setTransform(initialAffineTransform);
+        g2d.setClip(0, 0, imgWidth, imgHeight);
+    }
+
+    void fillRect(int x, int y, int width, int height, Paint paint) {
+        g2d.setPaint(paint);
+        g2d.fillRect(x, y, width, height);
+    }
+
+    void fillPolygon(int[] x, int[] y, Paint paint) {
+        g2d.setPaint(paint);
+        g2d.fillPolygon(x, y, x.length);
+    }
+
+    void fillPolygon(double[] x, double yBottom, double[] yTop, Paint paint) {
+        g2d.setPaint(paint);
+        PathIterator path = new PathIterator(yTop);
+        for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
+            int start = pos[0], end = pos[1], n = end - start;
+            int[] xDev = new int[n + 2], yDev = new int[n + 2];
+            for (int i = start; i < end; i++) {
+                xDev[i - start] = (int) x[i];
+                yDev[i - start] = (int) yTop[i];
+            }
+            xDev[n] = xDev[n - 1];
+            xDev[n + 1] = xDev[0];
+            yDev[n] = yDev[n + 1] = (int) yBottom;
+            g2d.fillPolygon(xDev, yDev, xDev.length);
+            g2d.drawPolygon(xDev, yDev, xDev.length);
+        }
+    }
+
+    void fillPolygon(double[] x, double[] yBottom, double[] yTop, Paint paint) {
+        g2d.setPaint(paint);
+        PathIterator path = new PathIterator(yTop);
+        for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
+            int start = pos[0], end = pos[1], n = end - start;
+            int[] xDev = new int[n * 2], yDev = new int[n * 2];
+            for (int i = start; i < end; i++) {
+                int ix1 = i - start, ix2 = n * 2 - 1 - i + start;
+                xDev[ix1] = xDev[ix2] = (int) x[i];
+                yDev[ix1] = (int) yTop[i];
+                yDev[ix2] = (int) yBottom[i];
+            }
+            g2d.fillPolygon(xDev, yDev, xDev.length);
+        }
+    }
+
+    void drawLine(int x1, int y1, int x2, int y2, Paint paint, Stroke stroke) {
+        g2d.setStroke(stroke);
+        g2d.setPaint(paint);
+        g2d.drawLine(x1, y1, x2, y2);
+    }
+
+    void drawPolyline(int[] x, int[] y, Paint paint, Stroke stroke) {
+        g2d.setStroke(stroke);
+        g2d.setPaint(paint);
+        g2d.drawPolyline(x, y, x.length);
+    }
+
+    void drawPolyline(double[] x, double[] y, Paint paint, Stroke stroke) {
+        g2d.setPaint(paint);
+        g2d.setStroke(stroke);
+        PathIterator path = new PathIterator(y);
+        for (int[] pos = path.getNextPath(); pos != null; pos = path.getNextPath()) {
+            int start = pos[0], end = pos[1];
+            int[] xDev = new int[end - start], yDev = new int[end - start];
+            for (int i = start; i < end; i++) {
+                xDev[i - start] = (int) x[i];
+                yDev[i - start] = (int) y[i];
+            }
+            g2d.drawPolyline(xDev, yDev, xDev.length);
+        }
+    }
+
+    void drawString(String text, int x, int y, Font font, Paint paint) {
+        g2d.setFont(font);
+        g2d.setPaint(paint);
+        g2d.drawString(text, x, y);
+    }
+
+    double getFontAscent(Font font) {
+        LineMetrics lm = font.getLineMetrics(DUMMY_TEXT, g2d.getFontRenderContext());
+        return lm.getAscent();
+    }
+
+    double getFontHeight(Font font) {
+        LineMetrics lm = font.getLineMetrics(DUMMY_TEXT, g2d.getFontRenderContext());
+        return lm.getAscent() + lm.getDescent();
+    }
+
+    double getStringWidth(String text, Font font) {
+        return font.getStringBounds(text, 0, text.length(), g2d.getFontRenderContext()).getBounds().getWidth();
+    }
+
+    void setAntiAliasing(boolean enable) {
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                enable ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
+    }
+
+    void setTextAntiAliasing(boolean enable) {
+        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+                enable ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
+    }
+
+    void dispose() {
+        g2d.dispose();
+    }
+
+    void makeImage(Object stream, ImageWriter writer, ImageWriteParam iwp) throws IOException {
+        BufferedImage outputImage = img; 
+
+        ImageWriterSpi imgProvider = writer.getOriginatingProvider();
+
+        img.coerceData(false);
+
+        // Some format can't manage 16M colors images
+        // JPEG don't like transparency
+        if (! imgProvider.canEncodeImage(outputImage) || "image/jpeg".equals(imgProvider.getMIMETypes()[0].toLowerCase())) {
+            int w = img.getWidth();
+            int h = img.getHeight();
+            outputImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
+            outputImage.getGraphics().drawImage(img, 0, 0, w, h, null);
+            if (! imgProvider.canEncodeImage(outputImage)) {
+                throw new RuntimeException("Invalid image type");
+            }
+        }
+
+        if (! imgProvider.canEncodeImage(outputImage)) {
+            throw new RuntimeException("Invalid image type");
+        }
+
+        try (ImageOutputStream imageStream = ImageIO.createImageOutputStream(stream)) {
+            writer.setOutput(imageStream);
+            writer.write(null, new IIOImage(outputImage, null, null), iwp);
+            imageStream.flush();
+        } catch (IOException e) {
+            writer.abort();
+            throw e;
+        } finally {
+            writer.dispose();
+        }
+    }
+
+    InputStream saveImage(String path, ImageWriter writer, ImageWriteParam iwp) throws IOException {
+        makeImage(Paths.get(path).toFile(), writer, iwp);
+        return new BufferedInputStream(new FileInputStream(path));
+    }
+
+    InputStream getImageBytes(ImageWriter writer, ImageWriteParam iwp) throws IOException {
+        try (ByteArrayOutputStream stream = new ByteArrayOutputStream(IMG_BUFFER_CAPACITY)){
+            makeImage(stream, writer, iwp);
+            return new ByteArrayInputStream(stream.toByteArray());
+        }
+    }
+
+    /**
+     * <p>loadImage.</p>
+     *
+     * @param imageFile a {@link java.lang.String} object.
+     * @throws java.io.IOException if any.
+     */
+    public void loadImage(String imageFile) throws IOException {
+        BufferedImage wpImage = ImageIO.read(new File(imageFile));
+        TexturePaint paint = new TexturePaint(wpImage, new Rectangle(0, 0, wpImage.getWidth(), wpImage.getHeight()));
+        g2d.setPaint(paint);
+        g2d.fillRect(0, 0, wpImage.getWidth(), wpImage.getHeight());
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/LegendComposer.java b/apps/jrobin/java/src/org/rrd4j/graph/LegendComposer.java
new file mode 100644
index 0000000000000000000000000000000000000000..f26e1985ee41e081eb7fd9a7f29205453848a6ed
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/LegendComposer.java
@@ -0,0 +1,157 @@
+package org.rrd4j.graph;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class LegendComposer implements RrdGraphConstants {
+    private final RrdGraphDef gdef;
+    private final ImageWorker worker;
+    private int legX, legY;
+    private final int legWidth;
+
+    private double interLegendSpace;
+    private double leading;
+    private double smallLeading;
+    private double boxSpace;
+
+    LegendComposer(RrdGraph rrdGraph, int legX, int legY, int legWidth) {
+        this.gdef = rrdGraph.gdef;
+        this.worker = rrdGraph.worker;
+        this.legX = legX;
+        this.legY = legY;
+        this.legWidth = legWidth;
+        interLegendSpace = rrdGraph.getInterlegendSpace();
+        leading = rrdGraph.getLeading();
+        smallLeading = rrdGraph.getSmallLeading();
+        boxSpace = rrdGraph.getBoxSpace();
+    }
+
+    int placeComments() {
+        Line line = new Line();
+        for (CommentText comment : gdef.comments) {
+            if (comment.isValidGraphElement()) {
+                if (!line.canAccommodate(comment)) {
+                    line.layoutAndAdvance(false);
+                    line.clear();
+                }
+                line.add(comment);
+            }
+        }
+        line.layoutAndAdvance(true);
+        return legY;
+    }
+
+    class Line {
+        private Markers lastMarker;
+        private double width;
+        private int spaceCount;
+        private boolean noJustification;
+        private List<CommentText> comments = new ArrayList<CommentText>();
+
+        Line() {
+            clear();
+        }
+
+        void clear() {
+            lastMarker = null;
+            width = 0;
+            spaceCount = 0;
+            noJustification = false;
+            comments.clear();
+        }
+
+        boolean canAccommodate(CommentText comment) {
+            // always accommodate if empty
+            if (comments.size() == 0) {
+                return true;
+            }
+            // cannot accommodate if the last marker was \j, \l, \r, \c, \s
+            if (lastMarker == Markers.ALIGN_LEFT_MARKER || lastMarker == Markers.ALIGN_LEFTNONL_MARKER || lastMarker == Markers.ALIGN_CENTER_MARKER ||
+                    lastMarker == Markers.ALIGN_RIGHT_MARKER || lastMarker == Markers.ALIGN_JUSTIFIED_MARKER ||
+                    lastMarker == Markers.VERTICAL_SPACING_MARKER) {
+                return false;
+            }
+            // cannot accommodate if line would be too long
+            double commentWidth = getCommentWidth(comment);
+            if (lastMarker != Markers.GLUE_MARKER) {
+                commentWidth += interLegendSpace;
+            }
+            return width + commentWidth <= legWidth;
+        }
+
+        void add(CommentText comment) {
+            double commentWidth = getCommentWidth(comment);
+            if (comments.size() > 0 && lastMarker != Markers.GLUE_MARKER) {
+                commentWidth += interLegendSpace;
+                spaceCount++;
+            }
+            width += commentWidth;
+            lastMarker = comment.marker;
+            noJustification |= lastMarker == Markers.NO_JUSTIFICATION_MARKER || lastMarker == null;
+            comments.add(comment);
+        }
+
+        void layoutAndAdvance(boolean isLastLine) {
+            if (comments.size() > 0) {
+                if (lastMarker == Markers.ALIGN_LEFT_MARKER || lastMarker == Markers.ALIGN_LEFTNONL_MARKER) {
+                    placeComments(legX, interLegendSpace);
+                }
+                else if (lastMarker == Markers.ALIGN_RIGHT_MARKER) {
+                    placeComments(legX + legWidth - width, interLegendSpace);
+                }
+                else if (lastMarker == Markers.ALIGN_CENTER_MARKER) {
+                    placeComments(legX + (legWidth - width) / 2.0, interLegendSpace);
+                }
+                else if (lastMarker == Markers.ALIGN_JUSTIFIED_MARKER) {
+                    // anything to justify?
+                    if (spaceCount > 0) {
+                        placeComments(legX, (legWidth - width) / spaceCount + interLegendSpace);
+                    }
+                    else {
+                        placeComments(legX, interLegendSpace);
+                    }
+                }
+                else if (lastMarker == Markers.VERTICAL_SPACING_MARKER) {
+                    placeComments(legX, interLegendSpace);
+                }
+                else {
+                    // nothing specified, align with respect to '\J'
+                    if (noJustification || isLastLine) {
+                        placeComments(legX, (legWidth - width) / spaceCount + interLegendSpace);
+                    }
+                    else {
+                        placeComments(legX, interLegendSpace);
+                    }
+                }
+                if (!(lastMarker == Markers.ALIGN_LEFTNONL_MARKER)) {
+                    if (lastMarker == Markers.VERTICAL_SPACING_MARKER) {
+                        legY += smallLeading;
+                    }
+                    else {
+                        legY += leading;
+                    }
+                }
+            }
+        }
+
+        private double getCommentWidth(CommentText comment) {
+            double commentWidth = worker.getStringWidth(comment.resolvedText, gdef.getFont(FONTTAG_LEGEND));
+            if (comment instanceof LegendText) {
+                commentWidth += boxSpace;
+            }
+            return commentWidth;
+        }
+
+        private void placeComments(double xStart, double space) {
+            double x = xStart;
+            for (CommentText comment : comments) {
+                comment.x = (int) x;
+                comment.y = legY;
+                x += getCommentWidth(comment);
+                if (comment.marker != Markers.GLUE_MARKER) {
+                    x += space;
+                }
+            }
+        }
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/LegendText.java b/apps/jrobin/java/src/org/rrd4j/graph/LegendText.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f19f6b19b698091f6a4ce3ab566a623cbd98805
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/LegendText.java
@@ -0,0 +1,12 @@
+package org.rrd4j.graph;
+
+import java.awt.*;
+
+class LegendText extends CommentText {
+    final Paint legendColor;
+
+    LegendText(Paint legendColor, String text) {
+        super(text);
+        this.legendColor = legendColor;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Line.java b/apps/jrobin/java/src/org/rrd4j/graph/Line.java
new file mode 100644
index 0000000000000000000000000000000000000000..83bdd9f14015a5a3932a8a9018f70c92676c49c2
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Line.java
@@ -0,0 +1,14 @@
+package org.rrd4j.graph;
+
+import java.awt.BasicStroke;
+import java.awt.Paint;
+
+class Line extends SourcedPlotElement {
+    final BasicStroke stroke;
+
+    Line(String srcName, Paint color, BasicStroke stroke, SourcedPlotElement parent) {
+        super(srcName, color, parent);
+        this.stroke = stroke;
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Mapper.java b/apps/jrobin/java/src/org/rrd4j/graph/Mapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d168d41937b8037b8293972b4d7e79048d4e04d
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Mapper.java
@@ -0,0 +1,66 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.graph.ImageParameters;
+import org.rrd4j.graph.RrdGraphDef;
+
+class Mapper {
+    private final RrdGraphDef gdef;
+    private final ImageParameters im;
+    private final double pixieX, pixieY;
+
+    Mapper(RrdGraph rrdGraph) {
+        this.gdef = rrdGraph.gdef;
+        this.im = rrdGraph.im;
+        pixieX = (double) im.xsize / (double) (im.end - im.start);
+        if (!gdef.logarithmic) {
+            pixieY = (double) im.ysize / (im.maxval - im.minval);
+        }
+        else {
+            pixieY = (double) im.ysize / (ValueAxisLogarithmic.log10(im.maxval) - ValueAxisLogarithmic.log10(im.minval));
+        }
+    }
+
+    Mapper(RrdGraphDef gdef, ImageParameters im) {
+        this.gdef = gdef;
+        this.im = im;
+        pixieX = (double) im.xsize / (double) (im.end - im.start);
+        if (!gdef.logarithmic) {
+            pixieY = (double) im.ysize / (im.maxval - im.minval);
+        }
+        else {
+            pixieY = (double) im.ysize / (Math.log10(im.maxval) - Math.log10(im.minval));
+        }
+    }
+
+    int xtr(double mytime) {
+        return (int) ((double) im.xorigin + pixieX * (mytime - im.start));
+    }
+
+    int ytr(double value) {
+        double yval;
+        if (!gdef.logarithmic) {
+            yval = im.yorigin - pixieY * (value - im.minval) + 0.5;
+        }
+        else {
+            if (value < im.minval) {
+                yval = im.yorigin;
+            }
+            else {
+                yval = im.yorigin - pixieY * (ValueAxisLogarithmic.log10(value) - ValueAxisLogarithmic.log10(im.minval)) + 0.5;
+            }
+        }
+        if (!gdef.rigid) {
+            return (int) yval;
+        }
+        else if ((int) yval > im.yorigin) {
+            return im.yorigin + 2;
+        }
+        else if ((int) yval < im.yorigin - im.ysize) {
+            return im.yorigin - im.ysize - 2;
+        }
+        else {
+            return (int) yval;
+        }
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Markers.java b/apps/jrobin/java/src/org/rrd4j/graph/Markers.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c014ff4b26f18c9d16da03054443e89cd32053f
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Markers.java
@@ -0,0 +1,46 @@
+package org.rrd4j.graph;
+
+enum Markers {
+    /**
+     * Constant to represent left alignment marker
+     */
+    ALIGN_LEFT_MARKER("\\l"),
+    /**
+     * Constant to represent left alignment marker, without new line
+     */
+    ALIGN_LEFTNONL_MARKER("\\L"),
+    /**
+     * Constant to represent centered alignment marker
+     */
+    ALIGN_CENTER_MARKER("\\c"),
+    /**
+     * Constant to represent right alignment marker
+     */
+    ALIGN_RIGHT_MARKER("\\r"),
+    /**
+     * Constant to represent justified alignment marker
+     */
+    ALIGN_JUSTIFIED_MARKER("\\j"),
+    /**
+     * Constant to represent "glue" marker
+     */
+    GLUE_MARKER("\\g"),
+    /**
+     * Constant to represent vertical spacing marker
+     */
+    VERTICAL_SPACING_MARKER("\\s"),
+    /**
+     * Constant to represent no justification markers
+     */
+    NO_JUSTIFICATION_MARKER("\\J");
+
+    final String marker;
+    Markers(String marker) {
+        this.marker = marker;
+    }
+
+    boolean check(String mark) {
+        return marker.equals(mark);
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/PDef.java b/apps/jrobin/java/src/org/rrd4j/graph/PDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..88fe3b0dd8ab8b9bd5cdc858ba4bbeec0c73d408
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/PDef.java
@@ -0,0 +1,17 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.data.DataProcessor;
+import org.rrd4j.data.Plottable;
+
+class PDef extends Source {
+    private Plottable plottable;
+
+    PDef(String name, Plottable plottable) {
+        super(name);
+        this.plottable = plottable;
+    }
+
+    void requestData(DataProcessor dproc) {
+        dproc.addDatasource(name, plottable);
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/PathIterator.java b/apps/jrobin/java/src/org/rrd4j/graph/PathIterator.java
new file mode 100644
index 0000000000000000000000000000000000000000..9b789cf15106b40659a7678aae42bd7ca18263da
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/PathIterator.java
@@ -0,0 +1,30 @@
+package org.rrd4j.graph;
+
+class PathIterator {
+    private double[] y;
+    private int pos = 0;
+
+    PathIterator(double[] y) {
+        this.y = y;
+    }
+
+    int[] getNextPath() {
+        while (pos < y.length) {
+            if (Double.isNaN(y[pos])) {
+                pos++;
+            }
+            else {
+                int endPos = pos + 1;
+                while (endPos < y.length && !Double.isNaN(y[endPos])) {
+                    endPos++;
+                }
+                int[] result = {pos, endPos};
+                pos = endPos;
+                if (result[1] - result[0] >= 2) {
+                    return result;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/PlotElement.java b/apps/jrobin/java/src/org/rrd4j/graph/PlotElement.java
new file mode 100644
index 0000000000000000000000000000000000000000..173ff1d30133fdbcb5a1067ba788c6d8f5090640
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/PlotElement.java
@@ -0,0 +1,11 @@
+package org.rrd4j.graph;
+
+import java.awt.*;
+
+class PlotElement {
+    final Paint color;
+
+    PlotElement(Paint color) {
+        this.color = color;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/PrintText.java b/apps/jrobin/java/src/org/rrd4j/graph/PrintText.java
new file mode 100644
index 0000000000000000000000000000000000000000..f247d68720b27593fdac7b9800d97715b423dcb5
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/PrintText.java
@@ -0,0 +1,72 @@
+package org.rrd4j.graph;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.rrd4j.core.Util;
+import org.rrd4j.data.DataProcessor;
+import org.rrd4j.data.Variable;
+import org.rrd4j.data.Variable.Value;
+
+class PrintText extends CommentText {
+    static final String UNIT_MARKER = "([^%]?)%(s|S)";
+    static final Pattern UNIT_PATTERN = Pattern.compile(UNIT_MARKER);
+
+    private final String srcName;
+    private final boolean includedInGraph;
+    private final boolean strftime;
+
+    PrintText(String srcName, String text, boolean includedInGraph, boolean strftime) {
+        super(text);
+        this.srcName = srcName;
+        this.includedInGraph = includedInGraph;
+        this.strftime = strftime;
+    }
+
+    boolean isPrint() {
+        return !includedInGraph;
+    }
+
+    void resolveText(Locale l, DataProcessor dproc, ValueScaler valueScaler) {
+        super.resolveText(l, dproc, valueScaler);
+        Value v = dproc.getVariable(srcName);
+        if(resolvedText == null) {
+            return;
+        }
+        else if(strftime) {
+            if(v != Variable.INVALIDVALUE) {
+                long time = v.timestamp;
+                try {
+                    Calendar c = new GregorianCalendar(dproc.getTimeZone(), l);
+                    c.setTimeInMillis(time * 1000);
+                    resolvedText = String.format(l, resolvedText, c);
+                } catch (Exception e) {
+                    throw new RuntimeException("can't format '" + resolvedText + "'", e);
+                }                
+            }
+            else {
+                resolvedText = "-";
+            }
+        }
+        else {
+            double value = v.value;
+            Matcher matcher = UNIT_PATTERN.matcher(resolvedText);
+            if (matcher.find()) {
+                // unit specified
+                ValueScaler.Scaled scaled = valueScaler.scale(value, matcher.group(2).equals("s"));
+                resolvedText = resolvedText.substring(0, matcher.start()) +
+                        matcher.group(1) + scaled.unit + resolvedText.substring(matcher.end());
+                value = scaled.value;
+            }
+            try {
+                resolvedText = Util.sprintf(l, resolvedText, value);
+            } catch (Exception e) {
+                throw new RuntimeException("can't format '" + resolvedText + "'", e);
+            }
+        }
+        trimIfGlue();
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/RrdGraph.java b/apps/jrobin/java/src/org/rrd4j/graph/RrdGraph.java
new file mode 100644
index 0000000000000000000000000000000000000000..071bfbce93e835b39444125c2717ce5a29cd52dd
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/RrdGraph.java
@@ -0,0 +1,781 @@
+package org.rrd4j.graph;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.swing.ImageIcon;
+
+import org.rrd4j.core.Util;
+import org.rrd4j.data.DataProcessor;
+import org.rrd4j.graph.DownSampler.DataSet;
+
+/**
+ * Class which actually creates Rrd4j graphs (does the hard work).
+ */
+public class RrdGraph implements RrdGraphConstants {
+    private static final double[] SENSIBLE_VALUES = {
+            1000.0, 900.0, 800.0, 750.0, 700.0, 600.0, 500.0, 400.0, 300.0, 250.0, 200.0, 125.0, 100.0,
+            90.0, 80.0, 75.0, 70.0, 60.0, 50.0, 40.0, 30.0, 25.0, 20.0, 10.0,
+            9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.5, 3.0, 2.5, 2.0, 1.8, 1.5, 1.2, 1.0,
+            0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, -1
+    };
+
+    private static final char[] SYMBOLS = {'a', 'f', 'p', 'n', 'µ', 'm', ' ', 'k', 'M', 'G', 'T', 'P', 'E'};
+
+    final RrdGraphDef gdef;
+    final ImageParameters im;
+    private DataProcessor dproc;
+    ImageWorker worker;
+    Mapper mapper;
+    private final RrdGraphInfo info = new RrdGraphInfo();
+    private final String signature;
+    private final ImageWriter writer;
+    private final ImageWriteParam param;
+
+    /**
+     * Creates graph from the corresponding {@link org.rrd4j.graph.RrdGraphDef} object.
+     *
+     * @param gdef Graph definition
+     * @throws java.io.IOException Thrown in case of I/O error
+     */
+    public RrdGraph(RrdGraphDef gdef) throws IOException {
+        this.gdef = gdef;
+        signature = gdef.getSignature();
+        im = new ImageParameters();
+        worker = new ImageWorker(1, 1); // Dummy worker, just to start with something
+        writer = ImageIO.getImageWritersByFormatName(gdef.imageFormat).next();
+        param = getImageParams();
+        try {
+            createGraph();
+        }
+        finally {
+            worker.dispose();
+            worker = null;
+            dproc = null;
+        }
+    }
+
+    /**
+     * <p>Creates graph from the corresponding {@link org.rrd4j.graph.RrdGraphDef} object.</p>
+     * <p>The graph will be created using customs {@link javax.imageio.ImageWriter} and {@link javax.imageio.ImageWriteParam} given.</p>
+     * <p>The ImageWriter type and ImageWriteParam settings have priority other the RrdGraphDef settings.
+
+     * @param gdef Graph definition
+     * @param writer
+     * @param param
+     * @throws IOException Thrown in case of I/O error
+     * @since 3.5
+     */
+    public RrdGraph(RrdGraphDef gdef, ImageWriter writer, ImageWriteParam param) throws IOException {
+        this.gdef = gdef;
+        signature = gdef.getSignature();
+        im = new ImageParameters();
+        worker = new ImageWorker(1, 1); // Dummy worker, just to start with something
+        this.writer = writer;
+        this.param = param;
+        try {
+            createGraph();
+        }
+        finally {
+            worker.dispose();
+            worker = null;
+            dproc = null;
+        }
+    }
+
+    /**
+     * Returns complete graph information in a single object.
+     *
+     * @return Graph information (width, height, filename, image bytes, etc...)
+     */
+    public RrdGraphInfo getRrdGraphInfo() {
+        return info;
+    }
+
+    private ImageWriteParam getImageParams() {
+        ImageWriteParam iwp = writer.getDefaultWriteParam();
+        ImageWriterSpi imgProvider = writer.getOriginatingProvider();
+        //If lossy compression, use the quality
+        if (! imgProvider.isFormatLossless()) {
+            iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+            iwp.setCompressionQuality(gdef.imageQuality);
+        }
+
+        if (iwp.canWriteProgressive()) {
+            iwp.setProgressiveMode(gdef.interlaced ? ImageWriteParam.MODE_DEFAULT:ImageWriteParam.MODE_DISABLED);
+        }
+        return iwp;
+    }
+
+    private void createGraph() throws IOException {
+        boolean lazy = lazyCheck();
+        if (!lazy || gdef.printStatementCount() != 0) {
+            fetchData();
+            resolveTextElements();
+            if (gdef.shouldPlot() && !lazy) {
+                calculatePlotValues();
+                findMinMaxValues();
+                identifySiUnit();
+                expandValueRange();
+                removeOutOfRangeRules();
+                removeOutOfRangeSpans();
+                initializeLimits();
+                placeLegends();
+                createImageWorker();
+                drawBackground();
+                drawData();
+                drawGrid();
+                drawAxis();
+                drawText();
+                drawLegend();
+                drawRules();
+                drawSpans();
+                gator();
+                drawOverlay();
+                saveImage();
+            }
+        }
+        collectInfo();
+    }
+
+    private void collectInfo() {
+        info.filename = gdef.filename;
+        info.width = im.xgif;
+        info.height = im.ygif;
+        for (CommentText comment : gdef.comments) {
+            if (comment instanceof PrintText) {
+                PrintText pt = (PrintText) comment;
+                if (pt.isPrint()) {
+                    info.addPrintLine(pt.resolvedText);
+                }
+            }
+        }
+        if (gdef.imageInfo != null) {
+            info.imgInfo = Util.sprintf(gdef.locale, gdef.imageInfo, gdef.filename, im.xgif, im.ygif);
+        }
+    }
+
+    private void saveImage() throws IOException {
+        if (! RrdGraphConstants.IN_MEMORY_IMAGE.equals(gdef.filename)) {
+            info.stream = worker.saveImage(gdef.filename, writer, param);
+        }
+        else {
+            info.stream = worker.getImageBytes(writer, param);
+        }
+    }
+
+    private void drawOverlay() throws IOException {
+        if (gdef.overlayImage != null) {
+            worker.loadImage(gdef.overlayImage);
+        }
+    }
+
+    private void gator() {
+        if (!gdef.onlyGraph && gdef.showSignature) {
+            worker.setTextAntiAliasing(gdef.textAntiAliasing);
+            Font font = gdef.getFont(FONTTAG_WATERMARK);
+            int x = (int) (im.xgif - 2 - worker.getFontAscent(font));
+            int y = 4;
+            worker.transform(x, y, Math.PI / 2);
+            worker.drawString(signature, 0, 0, font, Color.LIGHT_GRAY);
+            worker.reset();
+            worker.setTextAntiAliasing(false);
+        }
+    }
+
+    private void drawRules() {
+        worker.clip(im.xorigin + 1, im.yorigin - gdef.height - 1, gdef.width - 1, gdef.height + 2);
+        for (PlotElement pe : gdef.plotElements) {
+            if (pe instanceof HRule) {
+                HRule hr = (HRule) pe;
+                if (hr.value >= im.minval && hr.value <= im.maxval) {
+                    int y = mapper.ytr(hr.value);
+                    worker.drawLine(im.xorigin, y, im.xorigin + im.xsize, y, hr.color, hr.stroke);
+                }
+            }
+            else if (pe instanceof VRule) {
+                VRule vr = (VRule) pe;
+                if (vr.timestamp >= im.start && vr.timestamp <= im.end) {
+                    int x = mapper.xtr(vr.timestamp);
+                    worker.drawLine(x, im.yorigin, x, im.yorigin - im.ysize, vr.color, vr.stroke);
+                }
+            }
+        }
+        worker.reset();
+    }
+
+    private void drawSpans() {
+        worker.clip(im.xorigin + 1, im.yorigin - gdef.height - 1, gdef.width - 1, gdef.height + 2);
+        for (PlotElement pe : gdef.plotElements) {
+            if (pe instanceof HSpan) {
+                HSpan hr = (HSpan) pe;
+                int ys = mapper.ytr(hr.start);
+                int ye = mapper.ytr(hr.end);
+                int height = ys - ye;
+                worker.fillRect(im.xorigin, ys - height, im.xsize, height, hr.color);
+            }
+            else if (pe instanceof VSpan) {
+                VSpan vr = (VSpan) pe;
+                int xs = mapper.xtr(vr.start);
+                int xe = mapper.xtr(vr.end);
+                worker.fillRect(xs, im.yorigin - im.ysize, xe - xs, im.ysize, vr.color);
+            }
+        }
+        worker.reset();
+    }
+
+    private void drawText() {
+        if (!gdef.onlyGraph) {
+            worker.setTextAntiAliasing(gdef.textAntiAliasing);
+            if (gdef.title != null) {
+                int x = im.xgif / 2 - (int) (worker.getStringWidth(gdef.title, gdef.getFont(FONTTAG_TITLE)) / 2);
+                int y = PADDING_TOP + (int) worker.getFontAscent(gdef.getFont(FONTTAG_TITLE));
+                worker.drawString(gdef.title, x, y, gdef.getFont(FONTTAG_TITLE), gdef.getColor(ElementsNames.font));
+            }
+            if (gdef.verticalLabel != null) {
+                int x = PADDING_LEFT;
+                int y = im.yorigin - im.ysize / 2 + (int) worker.getStringWidth(gdef.verticalLabel, gdef.getFont(FONTTAG_UNIT)) / 2;
+                int ascent = (int) worker.getFontAscent(gdef.getFont(FONTTAG_UNIT));
+                worker.transform(x, y, -Math.PI / 2);
+                worker.drawString(gdef.verticalLabel, 0, ascent, gdef.getFont(FONTTAG_UNIT), gdef.getColor(ElementsNames.font));
+                worker.reset();
+            }
+            worker.setTextAntiAliasing(false);
+        }
+    }
+
+    private void drawGrid() {
+        if (!gdef.onlyGraph) {
+            worker.setTextAntiAliasing(gdef.textAntiAliasing);
+            Paint shade1 = gdef.getColor(ElementsNames.shadea);
+            Paint shade2 = gdef.getColor(ElementsNames.shadeb);
+            Stroke borderStroke = new BasicStroke(1);
+            worker.drawLine(0, 0, im.xgif - 1, 0, shade1, borderStroke);
+            worker.drawLine(1, 1, im.xgif - 2, 1, shade1, borderStroke);
+            worker.drawLine(0, 0, 0, im.ygif - 1, shade1, borderStroke);
+            worker.drawLine(1, 1, 1, im.ygif - 2, shade1, borderStroke);
+            worker.drawLine(im.xgif - 1, 0, im.xgif - 1, im.ygif - 1, shade2, borderStroke);
+            worker.drawLine(0, im.ygif - 1, im.xgif - 1, im.ygif - 1, shade2, borderStroke);
+            worker.drawLine(im.xgif - 2, 1, im.xgif - 2, im.ygif - 2, shade2, borderStroke);
+            worker.drawLine(1, im.ygif - 2, im.xgif - 2, im.ygif - 2, shade2, borderStroke);
+            if (gdef.drawXGrid) {
+                new TimeAxis(this).draw();
+            }
+            if (gdef.drawYGrid) {
+                boolean ok;
+                if (gdef.altYMrtg) {
+                    ok = new ValueAxisMrtg(this).draw();
+                }
+                else if (gdef.logarithmic) {
+                    ok = new ValueAxisLogarithmic(this).draw();
+                }
+                else {
+                    ok = new ValueAxis(this).draw();
+                }
+                if (!ok) {
+                    String msg = "No Data Found";
+                    worker.drawString(msg,
+                            im.xgif / 2 - (int) worker.getStringWidth(msg, gdef.getFont(FONTTAG_TITLE)) / 2,
+                            (2 * im.yorigin - im.ysize) / 2,
+                            gdef.getFont(FONTTAG_TITLE), gdef.getColor(ElementsNames.font));
+                }
+            }
+            worker.setTextAntiAliasing(false);
+        }
+    }
+
+    private void drawData() {
+        worker.setAntiAliasing(gdef.antiAliasing);
+        worker.clip(im.xorigin, im.yorigin - gdef.height - 1, gdef.width, gdef.height + 2);
+        double areazero = mapper.ytr((im.minval > 0.0) ? im.minval : (im.maxval < 0.0) ? im.maxval : 0.0);
+        double[] x = gdef.downsampler == null ? xtr(dproc.getTimestamps()) : null;
+        double[] lastY = null;
+        // draw line, area and stack
+        for (PlotElement plotElement : gdef.plotElements) {
+            if (plotElement instanceof SourcedPlotElement) {
+                SourcedPlotElement source = (SourcedPlotElement) plotElement;
+                double[] y;
+                if (gdef.downsampler != null) {
+                    DataSet set = gdef.downsampler.downsize(dproc.getTimestamps(), source.getValues());
+                    x = xtr(set.timestamps);
+                    y = ytr(set.values);
+                } else {
+                    y = ytr(source.getValues());
+                }
+                if (Line.class.isAssignableFrom(source.getClass())) {
+                    worker.drawPolyline(x, y, source.color, ((Line)source).stroke );
+                }
+                else if (Area.class.isAssignableFrom(source.getClass())) {
+                    if(source.parent == null) {
+                        worker.fillPolygon(x, areazero, y, source.color);
+                    }
+                    else {
+                        worker.fillPolygon(x, lastY, y, source.color);
+                        worker.drawPolyline(x, lastY, source.getParentColor(), new BasicStroke(0));
+                    }
+                }
+                else if (source instanceof Stack) {
+                    Stack stack = (Stack) source;
+                    float width = stack.getParentLineWidth();
+                    if (width >= 0F) {
+                        // line
+                        worker.drawPolyline(x, y, stack.color, new BasicStroke(width));
+                    }
+                    else {
+                        // area
+                        worker.fillPolygon(x, lastY, y, stack.color);
+                        worker.drawPolyline(x, lastY, stack.getParentColor(), new BasicStroke(0));
+                    }
+                }
+                else {
+                    // should not be here
+                    throw new IllegalStateException("Unknown plot source: " + source.getClass().getName());
+                }
+                lastY = y;
+            }
+        }
+        worker.reset();
+        worker.setAntiAliasing(false);
+    }
+
+    private void drawAxis() {
+        if (!gdef.onlyGraph) {
+            Paint gridColor = gdef.getColor(ElementsNames.grid);
+            Paint xaxisColor = gdef.getColor(ElementsNames.xaxis);
+            Paint yaxisColor = gdef.getColor(ElementsNames.yaxis);
+            Paint arrowColor = gdef.getColor(ElementsNames.arrow);
+            Stroke stroke = new BasicStroke(1);
+            worker.drawLine(im.xorigin + im.xsize, im.yorigin, im.xorigin + im.xsize, im.yorigin - im.ysize,
+                    gridColor, stroke);
+            worker.drawLine(im.xorigin, im.yorigin - im.ysize, im.xorigin + im.xsize, im.yorigin - im.ysize,
+                    gridColor, stroke);
+            worker.drawLine(im.xorigin - 4, im.yorigin, im.xorigin + im.xsize + 4, im.yorigin,
+                    xaxisColor, stroke);
+            worker.drawLine(im.xorigin, im.yorigin + 4, im.xorigin, im.yorigin - im.ysize - 4,
+                    yaxisColor, stroke);
+            //Do X axis arrow
+            double[] Xarrow_x = {
+                    im.xorigin + im.xsize + 4,
+                    im.xorigin + im.xsize + 9,
+                    im.xorigin + im.xsize + 4,
+            };
+            double[] Xarrow_y = {
+                    im.yorigin - 3,
+                    im.yorigin + 0,
+                    im.yorigin + 3,
+            };
+            worker.fillPolygon(Xarrow_x, im.yorigin + 3.0, Xarrow_y, arrowColor);
+
+            //Do y axis arrow
+            double[] Yarrow_x = {
+                    im.xorigin - 3,
+                    im.xorigin,
+                    im.xorigin + 3,
+            };
+            double[] Yarrow_y = {
+                    im.yorigin - im.ysize - 4,
+                    im.yorigin - im.ysize - 9,
+                    im.yorigin - im.ysize - 4,
+            };
+            worker.fillPolygon(Yarrow_x, im.yorigin - im.ysize - 4.0, Yarrow_y, arrowColor);
+        }
+    }
+
+    private void drawBackground() throws IOException {
+        worker.fillRect(0, 0, im.xgif, im.ygif, gdef.getColor(ElementsNames.back));
+        if (gdef.backgroundImage != null) {
+            worker.loadImage(gdef.backgroundImage);
+        }
+        worker.fillRect(im.xorigin, im.yorigin - im.ysize, im.xsize, im.ysize, gdef.getColor(ElementsNames.canvas));
+    }
+
+    private void createImageWorker() {
+        worker.resize(im.xgif, im.ygif);
+    }
+
+    private void placeLegends() {
+        if (!gdef.noLegend && !gdef.onlyGraph) {
+            int border = (int) (getFontCharWidth(FontTag.LEGEND) * PADDING_LEGEND);
+            LegendComposer lc = new LegendComposer(this, border, im.ygif, im.xgif - 2 * border);
+            im.ygif = lc.placeComments() + PADDING_BOTTOM;
+        }
+    }
+
+    private void initializeLimits() {
+        im.xsize = gdef.width;
+        im.ysize = gdef.height;
+        im.unitslength = gdef.unitsLength;
+
+        if (gdef.onlyGraph) {
+            im.xorigin = 0;
+        }
+        else {
+            im.xorigin = (int) (PADDING_LEFT + im.unitslength * getFontCharWidth(FontTag.UNIT));
+        }
+
+        if (!gdef.onlyGraph && gdef.verticalLabel != null) {
+            im.xorigin += getFontHeight(FONTTAG_UNIT);
+        }
+
+        if (gdef.onlyGraph) {
+            im.yorigin = im.ysize;
+        }
+        else {
+            im.yorigin = PADDING_TOP + im.ysize;
+        }
+
+        mapper = new Mapper(this);
+
+        if (!gdef.onlyGraph && gdef.title != null) {
+            im.yorigin += getFontHeight(FONTTAG_TITLE) + PADDING_TITLE;
+        }
+
+        if (gdef.onlyGraph) {
+            im.xgif = im.xsize;
+            im.ygif = im.yorigin;
+        }
+        else {
+            im.xgif = PADDING_RIGHT + im.xsize + im.xorigin;
+            im.ygif = im.yorigin + (int) (PADDING_PLOT * getFontHeight(FONTTAG_DEFAULT));
+        }
+    }
+
+    private void removeOutOfRangeRules() {
+        for (PlotElement plotElement : gdef.plotElements) {
+            if (plotElement instanceof HRule) {
+                ((HRule) plotElement).setLegendVisibility(im.minval, im.maxval, gdef.forceRulesLegend);
+            }
+            else if (plotElement instanceof VRule) {
+                ((VRule) plotElement).setLegendVisibility(im.start, im.end, gdef.forceRulesLegend);
+            }
+        }
+    }
+
+    private void removeOutOfRangeSpans() {
+        for (PlotElement plotElement : gdef.plotElements) {
+            if (plotElement instanceof HSpan) {
+                ((HSpan) plotElement).setLegendVisibility(im.minval, im.maxval, gdef.forceRulesLegend);
+            }
+            else if (plotElement instanceof VSpan) {
+                ((VSpan) plotElement).setLegendVisibility(im.start, im.end, gdef.forceRulesLegend);
+            }
+        }
+    }
+
+    private void expandValueRange() {
+        im.ygridstep = (gdef.valueAxisSetting != null) ? gdef.valueAxisSetting.gridStep : Double.NaN;
+        im.ylabfact = (gdef.valueAxisSetting != null) ? gdef.valueAxisSetting.labelFactor : 0;
+        if (!gdef.rigid && !gdef.logarithmic) {
+            double scaled_min, scaled_max, adj;
+            if (Double.isNaN(im.ygridstep)) {
+                if (gdef.altYMrtg) { /* mrtg */
+                    im.decimals = Math.ceil(Math.log10(Math.max(Math.abs(im.maxval), Math.abs(im.minval))));
+                    im.quadrant = 0;
+                    if (im.minval < 0) {
+                        im.quadrant = 2;
+                        if (im.maxval <= 0) {
+                            im.quadrant = 4;
+                        }
+                    }
+                    switch (im.quadrant) {
+                    case 2:
+                        im.scaledstep = Math.ceil(50 * Math.pow(10, -(im.decimals)) * Math.max(Math.abs(im.maxval),
+                                Math.abs(im.minval))) * Math.pow(10, im.decimals - 2);
+                        scaled_min = -2 * im.scaledstep;
+                        scaled_max = 2 * im.scaledstep;
+                        break;
+                    case 4:
+                        im.scaledstep = Math.ceil(25 * Math.pow(10,
+                                -(im.decimals)) * Math.abs(im.minval)) * Math.pow(10, im.decimals - 2);
+                        scaled_min = -4 * im.scaledstep;
+                        scaled_max = 0;
+                        break;
+                    default: /* quadrant 0 */
+                        im.scaledstep = Math.ceil(25 * Math.pow(10, -(im.decimals)) * im.maxval) *
+                        Math.pow(10, im.decimals - 2);
+                        scaled_min = 0;
+                        scaled_max = 4 * im.scaledstep;
+                        break;
+                    }
+                    im.minval = scaled_min;
+                    im.maxval = scaled_max;
+                }
+                else if (gdef.altAutoscale || (gdef.altAutoscaleMin && gdef.altAutoscaleMax)) {
+                    /* measure the amplitude of the function. Make sure that
+                            graph boundaries are slightly higher then max/min vals
+                            so we can see amplitude on the graph */
+                    double delt, fact;
+
+                    delt = im.maxval - im.minval;
+                    adj = delt * 0.1;
+                    fact = 2.0 * Math.pow(10.0,
+                            Math.floor(Math.log10(Math.max(Math.abs(im.minval), Math.abs(im.maxval)))) - 2);
+                    if (delt < fact) {
+                        adj = (fact - delt) * 0.55;
+                    }
+                    im.minval -= adj;
+                    im.maxval += adj;
+                }
+                else if (gdef.altAutoscaleMin) {
+                    /* measure the amplitude of the function. Make sure that
+                            graph boundaries are slightly lower than min vals
+                            so we can see amplitude on the graph */
+                    adj = (im.maxval - im.minval) * 0.1;
+                    im.minval -= adj;
+                }
+                else if (gdef.altAutoscaleMax) {
+                    /* measure the amplitude of the function. Make sure that
+                            graph boundaries are slightly higher than max vals
+                            so we can see amplitude on the graph */
+                    adj = (im.maxval - im.minval) * 0.1;
+                    im.maxval += adj;
+                }
+                else {
+                    scaled_min = im.minval / im.magfact;
+                    scaled_max = im.maxval / im.magfact;
+                    for (int i = 1; SENSIBLE_VALUES[i] > 0; i++) {
+                        if (SENSIBLE_VALUES[i - 1] >= scaled_min && SENSIBLE_VALUES[i] <= scaled_min) {
+                            im.minval = SENSIBLE_VALUES[i] * im.magfact;
+                        }
+                        if (-SENSIBLE_VALUES[i - 1] <= scaled_min && -SENSIBLE_VALUES[i] >= scaled_min) {
+                            im.minval = -SENSIBLE_VALUES[i - 1] * im.magfact;
+                        }
+                        if (SENSIBLE_VALUES[i - 1] >= scaled_max && SENSIBLE_VALUES[i] <= scaled_max) {
+                            im.maxval = SENSIBLE_VALUES[i - 1] * im.magfact;
+                        }
+                        if (-SENSIBLE_VALUES[i - 1] <= scaled_max && -SENSIBLE_VALUES[i] >= scaled_max) {
+                            im.maxval = -SENSIBLE_VALUES[i] * im.magfact;
+                        }
+                    }
+                }
+            }
+            else {
+                im.minval = (double) im.ylabfact * im.ygridstep *
+                        Math.floor(im.minval / ((double) im.ylabfact * im.ygridstep));
+                im.maxval = (double) im.ylabfact * im.ygridstep *
+                        Math.ceil(im.maxval / ((double) im.ylabfact * im.ygridstep));
+            }
+
+        }
+    }
+
+    private void identifySiUnit() {
+        im.unitsexponent = gdef.unitsExponent;
+        im.base = gdef.base;
+        if (!gdef.logarithmic) {
+            int symbcenter = 6;
+            double digits;
+            if (im.unitsexponent != Integer.MAX_VALUE) {
+                digits = Math.floor(im.unitsexponent / 3.0);
+            }
+            else {
+                digits = Math.floor(Math.log(Math.max(Math.abs(im.minval), Math.abs(im.maxval))) / Math.log(im.base));
+            }
+            im.magfact = Math.pow(im.base, digits);
+            if (((digits + symbcenter) < SYMBOLS.length) && ((digits + symbcenter) >= 0)) {
+                im.symbol = SYMBOLS[(int) digits + symbcenter];
+            }
+            else {
+                im.symbol = '?';
+            }
+        }
+    }
+
+    private void findMinMaxValues() {
+        double minval = Double.NaN, maxval = Double.NaN;
+        for (PlotElement pe : gdef.plotElements) {
+            if (pe instanceof SourcedPlotElement) {
+                minval = Util.min(((SourcedPlotElement) pe).getMinValue(), minval);
+                maxval = Util.max(((SourcedPlotElement) pe).getMaxValue(), maxval);
+            }
+        }
+        if (Double.isNaN(minval)) {
+            minval = 0D;
+        }
+        if (Double.isNaN(maxval)) {
+            maxval = 1D;
+        }
+        im.minval = gdef.minValue;
+        im.maxval = gdef.maxValue;
+        /* adjust min and max values */
+        if (Double.isNaN(im.minval) || ((!gdef.logarithmic && !gdef.rigid) && im.minval > minval)) {
+            im.minval = minval;
+        }
+        if (Double.isNaN(im.maxval) || (!gdef.rigid && im.maxval < maxval)) {
+            if (gdef.logarithmic) {
+                im.maxval = maxval * 1.1;
+            }
+            else {
+                im.maxval = maxval;
+            }
+        }
+        /* make sure min is smaller than max */
+        if (im.minval > im.maxval) {
+            im.minval = 0.99 * im.maxval;
+        }
+        /* make sure min and max are not equal */
+        if (Math.abs(im.minval - im.maxval) < .0000001)  {
+            im.maxval *= 1.01;
+            if (!gdef.logarithmic) {
+                im.minval *= 0.99;
+            }
+            /* make sure min and max are not both zero */
+            if (im.maxval == 0.0) {
+                im.maxval = 1.0;
+            }
+        }
+    }
+
+    private void calculatePlotValues() {
+        for (PlotElement pe : gdef.plotElements) {
+            if (pe instanceof SourcedPlotElement) {
+                ((SourcedPlotElement) pe).assignValues(dproc);
+            }
+        }
+    }
+
+    private void resolveTextElements() {
+        ValueScaler valueScaler = new ValueScaler(gdef.base);
+        for (CommentText comment : gdef.comments) {
+            comment.resolveText(gdef.locale, dproc, valueScaler);
+        }
+    }
+
+    private void fetchData() throws IOException {
+        dproc = new DataProcessor(gdef.startTime, gdef.endTime);
+        dproc.setPoolUsed(gdef.poolUsed);
+        dproc.setTimeZone(gdef.tz);
+        if (gdef.step > 0) {
+            dproc.setStep(gdef.step);
+            dproc.setFetchRequestResolution(gdef.step); 
+        }
+        for (Source src : gdef.sources) {
+            src.requestData(dproc);
+        }
+        dproc.processData();
+        im.start = gdef.startTime;
+        im.end = gdef.endTime;
+    }
+
+    private boolean lazyCheck() {
+        // redraw if lazy option is not set or file does not exist
+        if (!gdef.lazy || !Util.fileExists(gdef.filename)) {
+            return false; // 'false' means 'redraw'
+        }
+        // redraw if not enough time has passed
+        long secPerPixel = (gdef.endTime - gdef.startTime) / gdef.width;
+        long elapsed = Util.getTimestamp() - Util.getLastModified(gdef.filename);
+        return elapsed <= secPerPixel;
+    }
+
+    private void drawLegend() {
+        if (!gdef.onlyGraph && !gdef.noLegend) {
+            worker.setTextAntiAliasing(gdef.textAntiAliasing);
+            int ascent = (int) worker.getFontAscent(gdef.getFont(FONTTAG_LEGEND));
+            int box = (int) getBox(), boxSpace = (int) (getBoxSpace());
+            for (CommentText c : gdef.comments) {
+                if (c.isValidGraphElement()) {
+                    int x = c.x, y = c.y + ascent;
+                    if (c instanceof LegendText) {
+                        // draw with BOX
+                        worker.fillRect(x, y - box, box, box, gdef.getColor(ElementsNames.frame));
+                        worker.fillRect(x + 1, y - box + 1, box - 2, box - 2, gdef.getColor(ElementsNames.canvas));
+                        worker.fillRect(x + 1, y - box + 1, box - 2, box - 2, gdef.getColor(ElementsNames.back));
+                        worker.fillRect(x + 1, y - box + 1, box - 2, box - 2, ((LegendText) c).legendColor);
+                        worker.drawString(c.resolvedText, x + boxSpace, y, gdef.getFont(FONTTAG_LEGEND), gdef.getColor(ElementsNames.font));
+                    }
+                    else {
+                        worker.drawString(c.resolvedText, x, y, gdef.getFont(FONTTAG_LEGEND), gdef.getColor(ElementsNames.font));
+                    }
+                }
+            }
+            worker.setTextAntiAliasing(false);
+        }
+    }
+
+    // helper methods
+
+    double getFontHeight(FontTag fonttag) {
+        return worker.getFontHeight(gdef.getFont(fonttag));
+    }
+
+    double getFontCharWidth(FontTag fonttag) {
+        return worker.getStringWidth("a", gdef.getFont(fonttag));
+    }
+
+    @Deprecated
+    double getSmallFontHeight() {
+        return getFontHeight(FONTTAG_LEGEND);
+    }
+
+    double getTitleFontHeight() {
+        return getFontHeight(FONTTAG_TITLE);
+    }
+
+    double getInterlegendSpace() {
+        return getFontCharWidth(FONTTAG_LEGEND) * LEGEND_INTERSPACING;
+    }
+
+    double getLeading() {
+        return getFontHeight(FONTTAG_LEGEND) * LEGEND_LEADING;
+    }
+
+    double getSmallLeading() {
+        return getFontHeight(FONTTAG_LEGEND) * LEGEND_LEADING_SMALL;
+    }
+
+    double getBoxSpace() {
+        return Math.ceil(getFontHeight(FONTTAG_LEGEND) * LEGEND_BOX_SPACE);
+    }
+
+    private double getBox() {
+        return getFontHeight(FONTTAG_LEGEND) * LEGEND_BOX;
+    }
+
+    private double[] xtr(long[] timestamps) {
+        double[] timestampsDev = new double[2 * timestamps.length - 1];
+        for (int i = 0, j = 0; i < timestamps.length; i += 1, j += 2) {
+            timestampsDev[j] = mapper.xtr(timestamps[i]);
+            if (i < timestamps.length - 1) {
+                timestampsDev[j + 1] = timestampsDev[j];
+            }
+        }
+        return timestampsDev;
+    }
+
+    private double[] ytr(double[] values) {
+        double[] valuesDev = new double[2 * values.length - 1];
+        for (int i = 0, j = 0; i < values.length; i += 1, j += 2) {
+            if (Double.isNaN(values[i])) {
+                valuesDev[j] = Double.NaN;
+            }
+            else {
+                valuesDev[j] = mapper.ytr(values[i]);
+            }
+            if (j > 0) {
+                valuesDev[j - 1] = valuesDev[j];
+            }
+        }
+        return valuesDev;
+    }
+
+    /**
+     * Renders this graph onto graphing device
+     *
+     * @param g Graphics handle
+     */
+    public void render(Graphics g) {
+        byte[] imageData = getRrdGraphInfo().getBytes();
+        ImageIcon image = new ImageIcon(imageData);
+        image.paintIcon(null, g, 0, 0);
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphConstants.java b/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..04d088ddf6da66c5996aad7ad15f06f8a5a2fa10
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphConstants.java
@@ -0,0 +1,420 @@
+package org.rrd4j.graph;
+
+import java.awt.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Class to represent various constants used for graphing. No methods are specified.
+ */
+public interface RrdGraphConstants {
+    /**
+     * Default graph starting time
+     */
+    String DEFAULT_START = "end-1d";
+    /**
+     * Default graph ending time
+     */
+    String DEFAULT_END = "now";
+
+    /**
+     * HH:mm time format
+     */
+    String HH_MM = "HH:mm";
+
+    /**
+     * Constant to represent second
+     */
+    int SECOND = Calendar.SECOND;
+    /**
+     * Constant to represent minute
+     */
+    int MINUTE = Calendar.MINUTE;
+    /**
+     * Constant to represent hour
+     */
+    int HOUR = Calendar.HOUR_OF_DAY;
+    /**
+     * Constant to represent day
+     */
+    int DAY = Calendar.DAY_OF_MONTH;
+    /**
+     * Constant to represent week
+     */
+    int WEEK = Calendar.WEEK_OF_YEAR;
+    /**
+     * Constant to represent month
+     */
+    int MONTH = Calendar.MONTH;
+    /**
+     * Constant to represent year
+     */
+    int YEAR = Calendar.YEAR;
+
+    /**
+     * Constant to represent Monday
+     */
+    int MONDAY = Calendar.MONDAY;
+    /**
+     * Constant to represent Tuesday
+     */
+    int TUESDAY = Calendar.TUESDAY;
+    /**
+     * Constant to represent Wednesday
+     */
+    int WEDNESDAY = Calendar.WEDNESDAY;
+    /**
+     * Constant to represent Thursday
+     */
+    int THURSDAY = Calendar.THURSDAY;
+    /**
+     * Constant to represent Friday
+     */
+    int FRIDAY = Calendar.FRIDAY;
+    /**
+     * Constant to represent Saturday
+     */
+    int SATURDAY = Calendar.SATURDAY;
+    /**
+     * Constant to represent Sunday
+     */
+    int SUNDAY = Calendar.SUNDAY;
+
+    /**
+     * Index of the canvas color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_CANVAS = 0;
+    /**
+     * Index of the background color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_BACK = 1;
+    /**
+     * Index of the top-left graph shade color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_SHADEA = 2;
+    /**
+     * Index of the bottom-right graph shade color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_SHADEB = 3;
+    /**
+     * Index of the minor grid color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_GRID = 4;
+    /**
+     * Index of the major grid color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_MGRID = 5;
+    /**
+     * Index of the font color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_FONT = 6;
+    /**
+     * Index of the frame color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_FRAME = 7;
+    /**
+     * Index of the arrow color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_ARROW = 8;
+    /**
+     * Index of the x-axis color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_XAXIS = 9;
+    /**
+     * Index of the yaxis color. Used in {@link RrdGraphDef#setColor(int, java.awt.Paint)}
+     */
+    @Deprecated
+    int COLOR_YAXIS = 10;
+
+    /**
+     * Default first day of the week (obtained from the default locale)
+     */
+    int FIRST_DAY_OF_WEEK = Calendar.getInstance(Locale.getDefault()).getFirstDayOfWeek();
+
+    /**
+     * Default graph canvas color
+     */
+    Color DEFAULT_CANVAS_COLOR = Color.WHITE;
+    /**
+     * Default graph background color
+     */
+    Color DEFAULT_BACK_COLOR = new Color(245, 245, 245);
+    /**
+     * Default top-left graph shade color
+     */
+    Color DEFAULT_SHADEA_COLOR = new Color(200, 200, 200);
+    /**
+     * Default bottom-right graph shade color
+     */
+    Color DEFAULT_SHADEB_COLOR = new Color(150, 150, 150);
+    /**
+     * Default minor grid color
+     */
+    Color DEFAULT_GRID_COLOR = new Color(171, 171, 171, 95);
+    /**
+     * Default major grid color
+     */
+    Color DEFAULT_MGRID_COLOR = new Color(255, 91, 91, 95);
+    /**
+     * Default font color
+     */
+    Color DEFAULT_FONT_COLOR = Color.BLACK;
+    /**
+     * Default frame color
+     */
+    Color DEFAULT_FRAME_COLOR = Color.BLACK;
+    /**
+     * Default arrow color
+     */
+    Color DEFAULT_ARROW_COLOR = new Color(128, 31, 31);
+    /**
+     * Default x-axis color
+     */
+    Color DEFAULT_XAXIS_COLOR = Color.BLACK;
+    /**
+     * Default x-axis color
+     */
+    Color DEFAULT_YAXIS_COLOR = Color.BLACK;
+
+    /**
+     * An transparent color
+     */
+    Color BLIND_COLOR = new Color(0, 0, 0, 0);
+
+    /**
+     * Constant to represent left alignment marker
+     */
+    @Deprecated
+    String ALIGN_LEFT_MARKER = Markers.ALIGN_LEFT_MARKER.marker;
+    /**
+     * Constant to represent left alignment marker, without new line
+     */
+    @Deprecated
+    String ALIGN_LEFTNONL_MARKER = Markers.ALIGN_LEFTNONL_MARKER.marker;
+    /**
+     * Constant to represent centered alignment marker
+     */
+    @Deprecated
+    String ALIGN_CENTER_MARKER = Markers.ALIGN_CENTER_MARKER.marker;
+    /**
+     * Constant to represent right alignment marker
+     */
+    @Deprecated
+    String ALIGN_RIGHT_MARKER = Markers.ALIGN_RIGHT_MARKER.marker;
+    /**
+     * Constant to represent justified alignment marker
+     */
+    @Deprecated
+    String ALIGN_JUSTIFIED_MARKER = Markers.ALIGN_JUSTIFIED_MARKER.marker;
+    /**
+     * Constant to represent "glue" marker
+     */
+    @Deprecated
+    String GLUE_MARKER = Markers.GLUE_MARKER.marker;
+    /**
+     * Constant to represent vertical spacing marker
+     */
+    @Deprecated
+    String VERTICAL_SPACING_MARKER = Markers.VERTICAL_SPACING_MARKER.marker;
+    /**
+     * Constant to represent no justification markers
+     */
+    @Deprecated
+    String NO_JUSTIFICATION_MARKER = Markers.NO_JUSTIFICATION_MARKER.marker;
+
+    /**
+     * Constant to represent in-memory image name
+     */
+    String IN_MEMORY_IMAGE = "-";
+
+    /**
+     * Default units length
+     */
+    int DEFAULT_UNITS_LENGTH = 9;
+    /**
+     * Default graph width
+     */
+    int DEFAULT_WIDTH = 400;
+    /**
+     * Default graph height
+     */
+    int DEFAULT_HEIGHT = 100;
+    /**
+     * Default image format
+     */
+    String DEFAULT_IMAGE_FORMAT = "gif";
+    /**
+     * Default image quality, used only for jpeg graphs
+     */
+    float DEFAULT_IMAGE_QUALITY = 0.8F; // only for jpegs, not used for png/gif
+    /**
+     * Default value base
+     */
+    double DEFAULT_BASE = 1000;
+
+    /**
+     * Font constructor, to use embedded fonts
+     */
+    static class FontConstructor {
+        private FontConstructor() {}
+
+        /**
+         * Return the default RRD4J's default font for the given strength
+         * @param type {@link java.awt.Font#BOLD} for a bold fond, any other value return plain style.
+         * @param size the size for the new Font
+         * @return a new {@link java.awt.Font} instance
+         */
+        public static Font getFont(int type, int size) {
+            String fontPath;
+            if (type == Font.BOLD)
+                fontPath = "/DejaVuSansMono-Bold.ttf";
+            else
+                fontPath = "/DejaVuSansMono.ttf";
+
+            try (InputStream fontstream = RrdGraphConstants.class.getResourceAsStream(fontPath)) {
+                return Font.createFont(Font.TRUETYPE_FONT, fontstream).deriveFont(type == Font.BOLD ? Font.BOLD : Font.PLAIN, size);
+            } catch (FontFormatException | IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Default graph small font
+     */
+    static final Font DEFAULT_SMALL_FONT = FontConstructor.getFont(Font.PLAIN, 10);
+    /**
+     * Default graph large font
+     */
+    static final Font DEFAULT_LARGE_FONT = FontConstructor.getFont(Font.BOLD, 12);
+    /**
+     * Font for the Gator
+     */
+    static final Font GATOR_FONT = FontConstructor.getFont(Font.PLAIN, 9);
+    /**
+     * Used internally
+     */
+    double LEGEND_LEADING = 1.2; // chars
+    /**
+     * Used internally
+     */
+    double LEGEND_LEADING_SMALL = 0.7; // chars
+    /**
+     * Used internally
+     */
+    double LEGEND_BOX_SPACE = 1.2; // chars
+    /**
+     * Used internally
+     */
+    double LEGEND_BOX = 0.9; // chars
+    /**
+     * Used internally
+     */
+    int LEGEND_INTERSPACING = 2; // chars
+    /**
+     * Used internally
+     */
+    int PADDING_LEFT = 10; // pix
+    /**
+     * Used internally
+     */
+    int PADDING_TOP = 12; // pix
+    /**
+     * Used internally
+     */
+    int PADDING_TITLE = 6; // pix
+    /**
+     * Used internally
+     */
+    int PADDING_RIGHT = 16; // pix
+    /**
+     * Used internally
+     */
+    int PADDING_PLOT = 2; //chars
+    /**
+     * Used internally
+     */
+    int PADDING_LEGEND = 2; // chars
+    /**
+     * Used internally
+     */
+    int PADDING_BOTTOM = 6; // pix
+    /**
+     * Used internally
+     */
+    int PADDING_VLABEL = 7; // pix
+
+    /**
+     * Stroke used to draw grid
+     */
+    Stroke GRID_STROKE = new BasicStroke(1);
+
+    /**
+     * Stroke used to draw ticks
+     */
+    Stroke TICK_STROKE = new BasicStroke(1);
+
+    /**
+     * Allowed font tag names which can be used in {@link org.rrd4j.graph.RrdGraphDef#setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, java.awt.Font)} method
+     */
+    public enum FontTag  {
+        /**
+         * Index of the default font. Used in {@link org.rrd4j.graph.RrdGraphDef#setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, java.awt.Font)}
+         */
+        DEFAULT,
+        /**
+         * Index of the title font. Used in {@link org.rrd4j.graph.RrdGraphDef#setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, java.awt.Font)}
+         */
+        TITLE,
+        /**
+         * Index of the axis label font. Used in {@link org.rrd4j.graph.RrdGraphDef#setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, java.awt.Font)}
+         */
+        AXIS,
+        /**
+         * Index of the vertical unit label font. Used in {@link org.rrd4j.graph.RrdGraphDef#setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, java.awt.Font)}
+         */
+        UNIT,
+        /**
+         * Index of the graph legend font. Used in {@link org.rrd4j.graph.RrdGraphDef#setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, java.awt.Font)}
+         */
+        LEGEND,
+        /**
+         * Index of the edge watermark font. Used in {@link org.rrd4j.graph.RrdGraphDef#setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, java.awt.Font)}
+         */
+        WATERMARK;
+
+        public void set(Font f, Font[] fonts) {
+            fonts[this.ordinal()] = f;
+        }
+
+        public Font get(Font f, Font[] fonts) {
+            return fonts[this.ordinal()];
+        }
+
+    }
+
+    FontTag FONTTAG_DEFAULT   = FontTag.DEFAULT;
+
+    FontTag FONTTAG_TITLE     = FontTag.TITLE;
+
+    FontTag FONTTAG_AXIS      = FontTag.AXIS;
+
+    FontTag FONTTAG_UNIT      = FontTag.AXIS;
+
+    FontTag FONTTAG_LEGEND    = FontTag.LEGEND;
+
+    FontTag FONTTAG_WATERMARK = FontTag.WATERMARK;
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphDef.java b/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e8446e822c09ffae0c839caf0fcdb2aeedac515
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphDef.java
@@ -0,0 +1,1668 @@
+package org.rrd4j.graph;
+
+import java.awt.BasicStroke;
+import java.awt.Font;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.rrd4j.ConsolFun;
+import org.rrd4j.core.FetchData;
+import org.rrd4j.core.RrdBackendFactory;
+import org.rrd4j.core.Util;
+import org.rrd4j.data.DataProcessor;
+import org.rrd4j.data.Plottable;
+import org.rrd4j.data.Variable;
+
+/**
+ * <p>Class which should be used to define new Rrd4j graph. Once constructed and populated with data
+ * object of this class should be passed to the constructor of the {@link org.rrd4j.graph.RrdGraph} class which
+ * will actually create the graph.</p>
+ * 
+ * <p>The text printed below the actual graph can be formated by appending
+ * special escaped characters at the end of a text. When ever such a
+ * character occurs, all pending text is pushed onto the graph according to
+ * the character specified.</p>
+ * 
+ * <p>Valid markers are: \j for justified, \l for left aligned, \r for right
+ * aligned and \c for centered.</p>
+ * 
+ * <p>Normally there are two space characters inserted between every two
+ * items printed into the graph. The space following a string can be
+ * suppressed by putting a \g at the end of the string. The \g also squashes
+ * any space inside the string if it is at the very end of the string.
+ * This can be used in connection with %s to suppress empty unit strings.</p>
+ * 
+ * <p>A special case is COMMENT:\s this inserts some additional vertical
+ * space before placing the next row of legends.</p>
+ * 
+ * <p>When text has to be formated without special instructions from your
+ * side, RRDTool will automatically justify the text as soon as one string
+ * goes over the right edge. If you want to prevent the justification
+ * without forcing a newline, you can use the special tag \J at the end of
+ * the string to disable the auto justification.</p>
+ */
+public class RrdGraphDef implements RrdGraphConstants {
+    boolean poolUsed = false; // ok
+    boolean antiAliasing = false; // ok
+    boolean textAntiAliasing = false; // ok
+    String filename = RrdGraphConstants.IN_MEMORY_IMAGE; // ok
+    long startTime, endTime; // ok
+    TimeAxisSetting timeAxisSetting = null; // ok
+    TimeLabelFormat timeLabelFormat = null; // ok
+    ValueAxisSetting valueAxisSetting = null; // ok
+    boolean altYGrid = false; // ok
+    boolean noMinorGrid = false; // ok
+    boolean altYMrtg = false; // ok
+    boolean altAutoscale = false; // ok
+    boolean altAutoscaleMin = false; // ok
+    boolean altAutoscaleMax = false; // ok
+    int unitsExponent = Integer.MAX_VALUE; // ok
+    int unitsLength = DEFAULT_UNITS_LENGTH; // ok
+    String verticalLabel = null; // ok
+    int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT; // ok
+    boolean interlaced = false; // ok
+    String imageInfo = null; // ok
+    String imageFormat = DEFAULT_IMAGE_FORMAT; // ok
+    float imageQuality = DEFAULT_IMAGE_QUALITY; // ok
+    String backgroundImage = null; // ok
+    String overlayImage = null; // ok
+    String unit = null; // ok
+    boolean lazy = false; // ok
+    double minValue = Double.NaN; // ok
+    double maxValue = Double.NaN; // ok
+    boolean rigid = false; // ok
+    double base = DEFAULT_BASE;  // ok
+    boolean logarithmic = false; // ok
+    private final Paint[] colors = new Paint[]{
+            // ok
+            DEFAULT_CANVAS_COLOR,
+            DEFAULT_BACK_COLOR,
+            DEFAULT_SHADEA_COLOR,
+            DEFAULT_SHADEB_COLOR,
+            DEFAULT_GRID_COLOR,
+            DEFAULT_MGRID_COLOR,
+            DEFAULT_FONT_COLOR,
+            DEFAULT_FRAME_COLOR,
+            DEFAULT_ARROW_COLOR,
+            DEFAULT_XAXIS_COLOR,
+            DEFAULT_YAXIS_COLOR
+    };
+    boolean noLegend = false; // ok
+    boolean onlyGraph = false; // ok
+    boolean forceRulesLegend = false; // ok
+    String title = null; // ok
+    long step = 0; // ok
+    Font[] fonts = new Font[] {
+            DEFAULT_SMALL_FONT,    // FONTTAG_DEFAULT
+            DEFAULT_LARGE_FONT,    // FONTTAG_TITLE
+            DEFAULT_SMALL_FONT,    // FONTTAG_AXIS
+            DEFAULT_SMALL_FONT,    // FONTTAG_UNIT
+            DEFAULT_SMALL_FONT,    // FONTTAG_LEGEND
+            GATOR_FONT             // FONTTAG_WATERMARK
+    };
+    boolean drawXGrid = true; // ok
+    boolean drawYGrid = true; // ok
+    int firstDayOfWeek = FIRST_DAY_OF_WEEK; // ok
+    Locale locale = Locale.getDefault();
+    TimeZone tz = TimeZone.getDefault();
+    String signature = "Generated by RRD4J";
+    boolean showSignature = true;
+    Stroke gridStroke = GRID_STROKE;
+    Stroke tickStroke = TICK_STROKE;
+    DownSampler downsampler = null;
+
+    final List<Source> sources = new ArrayList<Source>();
+    final List<CommentText> comments = new ArrayList<CommentText>();
+    final List<PlotElement> plotElements = new ArrayList<PlotElement>();
+
+    /**
+     * Creates RrdGraphDef object and sets default time span (default ending time is 'now',
+     * default starting time is 'end-1day'.
+     */
+    public RrdGraphDef() {
+        setTimeSpan(Util.getTimestamps(DEFAULT_START, DEFAULT_END));
+    }
+
+    /**
+     * Sets the time when the graph should begin. Time in seconds since epoch
+     * (1970-01-01) is required. Negative numbers are relative to the current time.
+     *
+     * @param time Starting time for the graph in seconds since epoch
+     */
+    public void setStartTime(long time) {
+        this.startTime = time;
+        if (time <= 0) {
+            this.startTime += Util.getTime();
+        }
+    }
+
+    /**
+     * Sets the time when the graph should end. Time in seconds since epoch
+     * (1970-01-01) is required. Negative numbers are relative to the current time.
+     *
+     * @param time Ending time for the graph in seconds since epoch
+     */
+    public void setEndTime(long time) {
+        this.endTime = time;
+        if (time <= 0) {
+            this.endTime += Util.getTime();
+        }
+    }
+
+    /**
+     * Sets starting and ending time for the for the graph. Timestamps in seconds since epoch are
+     * required. Negative numbers are relative to the current time.
+     *
+     * @param startTime Starting time in seconds since epoch
+     * @param endTime   Ending time in seconds since epoch
+     */
+    public void setTimeSpan(long startTime, long endTime) {
+        setStartTime(startTime);
+        setEndTime(endTime);
+    }
+
+    /**
+     * Sets starting and ending time for the for the graph. Timestamps in seconds since epoch are
+     * required.
+     *
+     * @param timestamps Array of timestamps. The first array item will be chosen for the starting
+     *                   timestamp. The last array item will be chosen for the ending timestamp.
+     */
+    public void setTimeSpan(long[] timestamps) {
+        setTimeSpan(timestamps[0], timestamps[timestamps.length - 1]);
+    }
+
+    /**
+     * Sets RrdDbPool usage policy (defaults to true). If set to true,
+     * {@link org.rrd4j.core.RrdDbPool RrdDbPool} will be used to
+     * access individual RRD files. If set to false, RRD files will be accessed directly.
+     *
+     * @param poolUsed true, if RrdDbPool class should be used. False otherwise.
+     */
+    public void setPoolUsed(boolean poolUsed) {
+        this.poolUsed = poolUsed;
+    }
+
+    /**
+     * Sets the name of the graph to generate. Since Rrd4j outputs GIFs, PNGs,
+     * and JPEGs it's recommended that the filename end in either .gif,
+     * .png or .jpg. Rrd4j does not enforce this, however. If the filename is
+     * set to '-' the image will be created only in memory (no file will be created).
+     * PNG and GIF formats are recommended but JPEGs should be avoided.
+     *
+     * @param filename Path to the image file
+     */
+    public void setFilename(String filename) {
+        this.filename = filename;
+    }
+
+    /**
+     * <p>Configures x-axis grid and labels. The x-axis label is quite complex to configure.
+     * So if you don't have very special needs, you can rely on the autoconfiguration to
+     * get this right.</p>
+     * 
+     * <p>Otherwise, you have to configure three elements making up the x-axis labels
+     * and grid. The base grid, the major grid and the labels.
+     * The configuration is based on the idea that you first specify a well
+     * known amount of time and then say how many times
+     * it has to pass between each minor/major grid line or label. For the label
+     * you have to define two additional items: The precision of the label
+     * in seconds and the format used to generate the text
+     * of the label.</p>
+     * 
+     * <p>For example, if you wanted a graph with a base grid every 10 minutes and a major
+     * one every hour, with labels every hour you would use the following
+     * x-axis definition.</p>
+     * 
+     * <pre>
+     * setTimeAxis(RrdGraphConstants.MINUTE, 10,
+     *             RrdGraphConstants.HOUR, 1,
+     *             RrdGraphConstants.HOUR, 1,
+     *             0, "%H:%M")
+     * </pre>
+     * 
+     * <p>The precision in this example is 0 because the %X format is exact.
+     * If the label was the name of the day, we would have had a precision
+     * of 24 hours, because when you say something like 'Monday' you mean
+     * the whole day and not Monday morning 00:00. Thus the label should
+     * be positioned at noon. By defining a precision of 24 hours or
+     * rather 86400 seconds, you make sure that this happens.</p>
+     *
+     * @param minorUnit        Minor grid unit. Minor grid, major grid and label units
+     *                         can be one of the following constants defined in
+     *                         {@link org.rrd4j.graph.RrdGraphConstants}: {@link org.rrd4j.graph.RrdGraphConstants#SECOND SECOND},
+     *                         {@link org.rrd4j.graph.RrdGraphConstants#MINUTE MINUTE}, {@link org.rrd4j.graph.RrdGraphConstants#HOUR HOUR},
+     *                         {@link org.rrd4j.graph.RrdGraphConstants#DAY DAY}, {@link org.rrd4j.graph.RrdGraphConstants#WEEK WEEK},
+     *                         {@link org.rrd4j.graph.RrdGraphConstants#MONTH MONTH}, {@link org.rrd4j.graph.RrdGraphConstants#YEAR YEAR}.
+     * @param minorUnitCount   Number of minor grid units between minor grid lines.
+     * @param majorUnit        Major grid unit.
+     * @param majorUnitCount   Number of major grid units between major grid lines.
+     * @param labelUnit        Label unit.
+     * @param labelUnitCount   Number of label units between labels.
+     * @param labelSpan        Label precision
+     * @param simpleDateFormat Date format (SimpleDateFormat pattern of strftime-like pattern)
+     */
+    public void setTimeAxis(int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
+            int labelUnit, int labelUnitCount, int labelSpan, String simpleDateFormat) {
+        timeAxisSetting = new TimeAxisSetting(minorUnit, minorUnitCount, majorUnit, majorUnitCount,
+                labelUnit, labelUnitCount, labelSpan, new SimpleTimeLabelFormat(simpleDateFormat));
+    }
+
+    /**
+     * It configure the x-axis grid in the same way than {@link #setTimeAxis(int, int, int, int, int, int, int, String)}, but it allows
+     * to use a {@link org.rrd4j.graph.TimeLabelFormat} to format the date label.
+     * 
+     * @param minorUnit
+     * @param minorUnitCount
+     * @param majorUnit
+     * @param majorUnitCount
+     * @param labelUnit
+     * @param labelUnitCount
+     * @param labelSpan
+     * @param format
+     */
+    public void setTimeAxis(int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
+            int labelUnit, int labelUnitCount, int labelSpan, TimeLabelFormat format) {
+        timeAxisSetting = new TimeAxisSetting(minorUnit, minorUnitCount, majorUnit, majorUnitCount,
+                labelUnit, labelUnitCount, labelSpan, format);
+    }
+
+    /**
+     * This allows to keep the default major and minor grid unit, but with changing only the label formatting,
+     * using a {@link org.rrd4j.graph.TimeLabelFormat}
+     * 
+     * @param format a custom dynamic time label format
+     */
+    public void setTimeLabelFormat(TimeLabelFormat format) {
+        timeLabelFormat = format;
+    }
+
+    /**
+     * Sets vertical axis grid and labels. Makes vertical grid lines appear
+     * at gridStep interval. Every labelFactor*gridStep, a major grid line is printed,
+     * along with label showing the value of the grid line.
+     *
+     * @param gridStep    Minor grid step
+     * @param labelFactor Specifies how many minor minor grid steps will appear between labels
+     *                    (major grid lines)
+     */
+    public void setValueAxis(double gridStep, int labelFactor) {
+        valueAxisSetting = new ValueAxisSetting(gridStep, labelFactor);
+    }
+
+    /**
+     * Places Y grid dynamically based on graph Y range. Algorithm ensures
+     * that you always have grid, that there are enough but not too many
+     * grid lines and the grid is metric. That is grid lines are placed
+     * every 1, 2, 5 or 10 units.
+     *
+     * @param altYGrid true, if Y grid should be calculated dynamically (defaults to false)
+     */
+    public void setAltYGrid(boolean altYGrid) {
+        this.altYGrid = altYGrid;
+    }
+
+    /**
+     * Use this method to turn off minor grid lines (printed by default)
+     *
+     * @param noMinorGrid true, to turn off, false to turn on (default)
+     */
+    public void setNoMinorGrid(boolean noMinorGrid) {
+        this.noMinorGrid = noMinorGrid;
+    }
+
+    /**
+     * Use this method to request MRTG-like graph (false by default)
+     *
+     * @param altYMrtg true, to create MRTG-like graph, false otherwise (default)
+     */
+    public void setAltYMrtg(boolean altYMrtg) {
+        this.altYMrtg = altYMrtg;
+    }
+
+    /**
+     * Computes Y range based on function absolute minimum and maximum
+     * values. Default algorithm uses predefined set of ranges.  This is
+     * good in many cases but it fails miserably when you need to graph
+     * something like 260 + 0.001 * sin(x). Default algorithm will use Y
+     * range from 250 to 300 and on the graph you will see almost straight
+     * line. With --alt-autoscale Y range will be from slightly less the
+     * 260 - 0.001 to slightly more then 260 + 0.001 and periodic behavior
+     * will be seen.
+     *
+     * @param altAutoscale true to request alternative autoscaling, false otherwise
+     *                     (default).
+     */
+    public void setAltAutoscale(boolean altAutoscale) {
+        this.altAutoscale = altAutoscale;
+    }
+
+    /**
+     * Computes Y range based on function absolute minimum and maximum
+     * values. Where setAltAutoscale(true) will modify both the absolute maximum AND
+     * minimum values, this option will only affect the maximum value. The
+     * minimum value, if not defined elsewhere, will be 0. This
+     * option can be useful when graphing router traffic when the WAN line
+     * uses compression, and thus the throughput may be higher than the
+     * WAN line speed.
+     *
+     * @param altAutoscaleMin true to request alternative autoscaling, false
+     *                        otherwise (default)
+     */
+    public void setAltAutoscaleMin(boolean altAutoscaleMin) {
+        this.altAutoscaleMin = altAutoscaleMin;
+    }
+
+    /**
+     * Computes Y range based on function absolute minimum and maximum
+     * values. Where setAltAutoscale(true) will modify both the absolute maximum AND
+     * minimum values, this option will only affect the maximum value. The
+     * minimum value, if not defined elsewhere, will be 0. This
+     * option can be useful when graphing router traffic when the WAN line
+     * uses compression, and thus the throughput may be higher than the
+     * WAN line speed.
+     *
+     * @param altAutoscaleMax true to request alternative autoscaling, false
+     *                        otherwise (default)
+     */
+    public void setAltAutoscaleMax(boolean altAutoscaleMax) {
+        this.altAutoscaleMax = altAutoscaleMax;
+    }
+
+    /**
+     * Sets the 10**unitsExponent scaling of the y-axis values. Normally
+     * values will be scaled to the appropriate units (k, M, etc.). However
+     * you may wish to display units always in k (Kilo, 10e3) even if
+     * the data is in the M (Mega, 10e6) range for instance.  Value should
+     * be an integer which is a multiple of 3 between -18 and 18, inclusive.
+     * It is the exponent on the units you which to use.  For example,
+     * use 3 to display the y-axis values in k (Kilo, 10e3, thousands),
+     * use -6 to display the y-axis values in µ (Micro, 10e-6,
+     * millionths). Use a value of 0 to prevent any scaling of the y-axis
+     * values.
+     *
+     * @param unitsExponent the 10**unitsExponent value for scaling y-axis values.
+     */
+    public void setUnitsExponent(int unitsExponent) {
+        this.unitsExponent = unitsExponent;
+    }
+
+    /**
+     * Sets the character width on the left side of the graph for
+     * y-axis values.
+     *
+     * @param unitsLength Number of characters on the left side of the graphs
+     *                    reserved for vertical axis labels.
+     */
+    public void setUnitsLength(int unitsLength) {
+        this.unitsLength = unitsLength;
+    }
+
+    /**
+     * Sets vertical label on the left side of the graph. This is normally used
+     * to specify the units used.
+     *
+     * @param verticalLabel Vertical axis label
+     */
+    public void setVerticalLabel(String verticalLabel) {
+        this.verticalLabel = verticalLabel;
+    }
+
+    /**
+     * Sets width of the drawing area within the graph. This affects the total
+     * size of the image.
+     *
+     * @param width Width of the drawing area.
+     */
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    /**
+     * Sets height of the drawing area within the graph. This affects the total
+     * size of the image.
+     *
+     * @param height Height of the drawing area.
+     */
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    /**
+     * Creates interlaced or progressive mode image.
+     *
+     * @param interlaced true, if GIF image should be interlaced.
+     */
+    public void setInterlaced(boolean interlaced) {
+        this.interlaced = interlaced;
+    }
+
+    /**
+     * <p>Creates additional image information.
+     * After the image has been created, the graph function uses imageInfo
+     * format string (printf-like) to create output similar to
+     * the {@link #print(String, ConsolFun, String)} function.
+     * The format string is supplied with the following parameters:
+     * filename, xsize and ysize (in that particular order).</p>
+     * 
+     * <p>For example, in order to generate an IMG tag
+     * suitable for including the graph into a web page, the command
+     * would look like this:</p>
+     * <pre>
+     * setImageInfo(&quot;&lt;IMG SRC='/img/%s' WIDTH='%d' HEIGHT='%d' ALT='Demo'&gt;&quot;);
+     * </pre>
+     *
+     * @param imageInfo Image info format. Use %s placeholder for filename, %d placeholder for
+     *                  image width and height.
+     */
+    public void setImageInfo(String imageInfo) {
+        this.imageInfo = imageInfo;
+    }
+
+    /**
+     * Sets image format.
+     *
+     * @param imageFormat Any value as return by {@link javax.imageio.ImageIO#getReaderFormatNames}
+     */
+    public void setImageFormat(String imageFormat) {
+        this.imageFormat = imageFormat;
+    }
+
+    /**
+     * Sets background image - currently, only PNG images can be used as background.
+     *
+     * @param backgroundImage Path to background image
+     */
+    public void setBackgroundImage(String backgroundImage) {
+        this.backgroundImage = backgroundImage;
+    }
+
+    /**
+     * Sets overlay image - currently, only PNG images can be used as overlay. Overlay image is
+     * printed on the top of the image, once it is completely created.
+     *
+     * @param overlayImage Path to overlay image
+     */
+    public void setOverlayImage(String overlayImage) {
+        this.overlayImage = overlayImage;
+    }
+
+    /**
+     * Sets unit to be displayed on y axis. It is wise to use only short units on graph, however.
+     *
+     * @param unit Unit description
+     */
+    public void setUnit(String unit) {
+        this.unit = unit;
+    }
+
+    /**
+     * Creates graph only if the current graph is out of date or not existent.
+     *
+     * @param lazy true, if graph should be 'lazy', false otherwise (default)
+     */
+    public void setLazy(boolean lazy) {
+        this.lazy = lazy;
+    }
+
+    /**
+     * Sets the lower limit of a graph. But rather, this is the
+     * maximum lower bound of a graph. For example, the value -100 will
+     * result in a graph that has a lower limit of -100 or less.  Use this
+     * method to expand graphs down.
+     *
+     * @param minValue Minimal value displayed on the graph
+     */
+    public void setMinValue(double minValue) {
+        this.minValue = minValue;
+    }
+
+    /**
+     * <p>Defines the value normally located at the upper border of the
+     * graph. If the graph contains higher values, the upper border will
+     * move upwards to accommodate these values as well.</p>
+     * 
+     * <p>If you want to define an upper-limit which will not move in any
+     * event you have to use {@link #setRigid(boolean)} method as well.</p>
+     *
+     * @param maxValue Maximal value displayed on the graph.
+     */
+    public void setMaxValue(double maxValue) {
+        this.maxValue = maxValue;
+    }
+
+    /**
+     * Sets rigid boundaries mode. Normally Rrd4j will automatically expand
+     * the lower and upper limit if the graph contains a value outside the
+     * valid range. With the <code>true</code> argument you can disable this behavior.
+     *
+     * @param rigid true if upper and lower limits should not be expanded to accommodate
+     *              values outside of the specified range. False otherwise (default).
+     */
+    public void setRigid(boolean rigid) {
+        this.rigid = rigid;
+    }
+
+    /**
+     * Sets default base for magnitude scaling. If you are graphing memory
+     * (and NOT network traffic) this switch should be set to 1024 so that 1Kb is 1024 byte.
+     * For traffic measurement, 1 kb/s is 1000 b/s.
+     *
+     * @param base Base value (defaults to 1000.0)
+     */
+    public void setBase(double base) {
+        this.base = base;
+    }
+
+    /**
+     * Sets logarithmic y-axis scaling.
+     *
+     * @param logarithmic true, for logarithmic scaling, false otherwise (default).
+     */
+    public void setLogarithmic(boolean logarithmic) {
+        this.logarithmic = logarithmic;
+    }
+
+    /**
+     * Overrides the colors for the standard elements of the graph. The
+     * colorTag must be one of the following constants defined in the {@link org.rrd4j.graph.RrdGraphConstants}:
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_BACK COLOR_BACK}ground,
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_CANVAS COLOR_CANVAS},
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_XAXIS COLOR_XAXIS},
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_SHADEA COLOR_SHADEA} left/top border,
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_SHADEB COLOR_SHADEB} right/bottom border,
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_GRID COLOR_GRID},
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_MGRID COLOR_MGRID} major grid,
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_FONT COLOR_FONT},
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_FRAME COLOR_FRAME} and axis of the graph or
+     * {@link org.rrd4j.graph.RrdGraphConstants#COLOR_ARROW COLOR_ARROW}. This
+     * method can be called multiple times to set several colors.
+     *
+     * @param colorTag Color tag, as explained above.
+     * @param color    Any color (paint) you like
+     * @deprecated Using {@link #setColor(ElementsNames, Paint)}
+     */
+    @Deprecated
+    public void setColor(int colorTag, Paint color) {
+        if (colorTag >= 0 && colorTag < colors.length) {
+            colors[colorTag] = color;
+        } else {
+            throw new IllegalArgumentException("Invalid color index specified: " + colorTag);
+        }
+    }
+
+    /**
+     * Overrides the colors for the standard elements of the graph.
+     * @param colorTag
+     * @param color
+     */
+    public void setColor(ElementsNames colorTag, Paint color) {
+        colors[colorTag.ordinal()] = color;
+    }
+
+    /**
+     * Overrides the colors for the standard elements of the graph by element name.
+     * See {@link #setColor(int, java.awt.Paint)} for full explanation.
+     *
+     * @param colorName One of the following strings: "BACK", "CANVAS", "SHADEA", "SHADEB",
+     *                  "GRID", "MGRID", "FONT", "FRAME", "ARROW", "XAXIS", "YAXIS"
+     * @param color     Any color (paint) you like
+     * @deprecated Using {@link #setColor(ElementsNames, Paint)}
+     */
+    @Deprecated
+    public void setColor(String colorName, Paint color) {
+        setColor(ElementsNames.valueOf(colorName.toLowerCase(Locale.ENGLISH)).ordinal(), color);
+    }
+
+    /**
+     * Suppress generation of legend, only render the graph.
+     *
+     * @param noLegend true if graph legend should be omitted. False otherwise (default).
+     */
+    public void setNoLegend(boolean noLegend) {
+        this.noLegend = noLegend;
+    }
+
+    /**
+     * Suppresses anything but the graph, works only for height &lt; 64.
+     *
+     * @param onlyGraph true if only graph should be created, false otherwise (default).
+     */
+    public void setOnlyGraph(boolean onlyGraph) {
+        this.onlyGraph = onlyGraph;
+    }
+
+    /**
+     * Force the generation of HRULE and VRULE legend even if those HRULE
+     * or VRULE will not be drawn because out of graph boundaries.
+     *
+     * @param forceRulesLegend true if rule legend should be always printed,
+     *                         false otherwise (default).
+     */
+    public void setForceRulesLegend(boolean forceRulesLegend) {
+        this.forceRulesLegend = forceRulesLegend;
+    }
+
+    /**
+     * Defines a title to be written into the graph.
+     *
+     * @param title Graph title.
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Suggests which time step should be used by Rrd4j while processing data from RRD files.
+     *
+     * @param step Desired time step (don't use this method if you don't know what you're doing).
+     */
+    public void setStep(long step) {
+        this.step = step;
+    }
+
+    /**
+     * This method reset the font set to it's default values. With the flag rrdtool set to true, it's not the old
+     * default set that is used, but the one taken from rrdtool. So use false to keep compatibility with previous version
+     * and true for a graph matching rrdtool's
+     * 
+     * @param rrdtool true to use rrdtool font set
+     */
+    public void setFontSet(boolean rrdtool) {
+        if(rrdtool) {
+            // We add a factor to the font size, rrdtool and java don't agree about font size
+            float rrdtoolfactor = 12f/9;
+            fonts = new Font[] {
+                    DEFAULT_SMALL_FONT.deriveFont(8.0f * rrdtoolfactor),    // FONTTAG_DEFAULT
+                    DEFAULT_SMALL_FONT.deriveFont(9.0f * rrdtoolfactor),    // FONTTAG_TITLE
+                    DEFAULT_SMALL_FONT.deriveFont(7.0f * rrdtoolfactor),    // FONTTAG_AXIS
+                    DEFAULT_SMALL_FONT.deriveFont(8.0f * rrdtoolfactor),    // FONTTAG_UNIT
+                    DEFAULT_SMALL_FONT.deriveFont(8.0f * rrdtoolfactor),    // FONTTAG_LEGEND
+                    DEFAULT_SMALL_FONT.deriveFont(5.5f * rrdtoolfactor)     // FONTTAG_WATERMARK
+            };
+        } else {
+            fonts = new Font[] {
+                    DEFAULT_SMALL_FONT,    // FONTTAG_DEFAULT
+                    DEFAULT_LARGE_FONT,    // FONTTAG_TITLE
+                    DEFAULT_SMALL_FONT,    // FONTTAG_AXIS
+                    DEFAULT_SMALL_FONT,    // FONTTAG_UNIT
+                    DEFAULT_SMALL_FONT,    // FONTTAG_LEGEND
+                    GATOR_FONT             // FONTTAG_WATERMARK
+            };            
+        }
+    }
+
+    /**
+     * Sets default font for graphing. Note that Rrd4j will behave unpredictably if proportional
+     * font is selected.
+     *
+     * @param smallFont Default font for graphing. Use only monospaced fonts.
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void setSmallFont(final Font smallFont) {
+        this.setFont(FontTag.DEFAULT, smallFont);
+    }
+
+    /**
+     * Sets title font.
+     *
+     * @param largeFont Font to be used for graph title.
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void setLargeFont(final Font largeFont) {
+        this.setFont(FontTag.TITLE, largeFont);
+    }
+
+    /**
+     * Sets font to be used for a specific font tag. The fontTag
+     * must be one of the following constants defined in the
+     * {@link RrdGraphConstants}:
+     * {@link RrdGraphConstants#FONTTAG_DEFAULT FONTTAG_DEFAULT} default font,,
+     * {@link RrdGraphConstants#FONTTAG_TITLE FONTTAG_TITLE} title,
+     * {@link RrdGraphConstants#FONTTAG_AXIS FONTTAG_AXIS} grid axis,,
+     * {@link RrdGraphConstants#FONTTAG_UNIT FONTTAG_UNIT} vertical unit label,,
+     * {@link RrdGraphConstants#FONTTAG_LEGEND FONTTAG_LEGEND} legend,
+     * {@link RrdGraphConstants#FONTTAG_WATERMARK FONTTAG_WATERMARK} watermark.
+     * This method can be called multiple times to set several fonts.
+     *
+     * @param fontTag Font tag, as explained above.
+     * @param font Font to be used for tag
+     */
+    public void setFont(final FontTag fontTag, final Font font) {
+        this.setFont(fontTag, font, false);
+    }
+
+    /**
+     * Sets font.
+     *
+     * @param fontTag Font tag, as explained above.
+     * @param font Font to be used for tag
+     * @param setAll Boolean to flag whether to set all fonts if fontTag == FONTTAG_DEFAULT
+     */
+    public void setFont(final FontTag fontTag, final Font font, final boolean setAll) {
+        this.setFont(fontTag, font, setAll, false);
+    }
+
+    /**
+     * Sets font.
+     *
+     * @param fontTag Font tag, as explained above.
+     * @param font Font to be used for tag
+     * @param setAll Boolean to flag whether to set all fonts if fontTag == FONTTAG_DEFAULT
+     * @param keepSizes Boolean to flag whether to keep original font sizes if setting all fonts.
+     */
+    public void setFont(final FontTag fontTag, final Font font, final boolean setAll, final boolean keepSizes) {
+        if (fontTag == FontTag.DEFAULT && setAll) {
+            if (keepSizes) {
+                this.fonts[FONTTAG_DEFAULT.ordinal()] = font.deriveFont(this.fonts[FONTTAG_DEFAULT.ordinal()].getSize());
+                this.fonts[FONTTAG_TITLE.ordinal()] = font.deriveFont(this.fonts[FONTTAG_TITLE.ordinal()].getSize());
+                this.fonts[FONTTAG_AXIS.ordinal()] = font.deriveFont(this.fonts[FONTTAG_AXIS.ordinal()].getSize());
+                this.fonts[FONTTAG_UNIT.ordinal()] = font.deriveFont(this.fonts[FONTTAG_UNIT.ordinal()].getSize());
+                this.fonts[FONTTAG_LEGEND.ordinal()] = font.deriveFont(this.fonts[FONTTAG_LEGEND.ordinal()].getSize());
+                this.fonts[FONTTAG_WATERMARK.ordinal()] = font.deriveFont(this.fonts[FONTTAG_WATERMARK.ordinal()].getSize());
+            }
+            else {
+                this.fonts[FONTTAG_DEFAULT.ordinal()] = font;
+                this.fonts[FONTTAG_TITLE.ordinal()] = null;
+                this.fonts[FONTTAG_AXIS.ordinal()] = null;
+                this.fonts[FONTTAG_UNIT.ordinal()] = null;
+                this.fonts[FONTTAG_LEGEND.ordinal()] = null;
+                this.fonts[FONTTAG_WATERMARK.ordinal()] = null;
+            }
+        } else {
+            this.fonts[fontTag.ordinal()] = font;
+        }
+    }
+
+    /**
+     * Sets font.
+     *
+     * @param fontTag Font tag as String, as explained in {@link #setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, Font, boolean)}.
+     * @param font Font to be used for tag
+     */
+    public void setFont(final String fontTag, final Font font) {
+        this.setFont(FontTag.valueOf(fontTag), font);
+    }
+
+    /**
+     * Sets font.
+     *
+     * @param fontTag Font tag as String, as explained in {@link #setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, Font, boolean)}.
+     * @param font Font to be used for tag
+     * @param setAll Boolean to flag whether to set all fonts if fontTag == FONTTAG_DEFAULT
+     */
+    public void setFont(final String fontTag, final Font font, final boolean setAll) {
+        this.setFont(FontTag.valueOf(fontTag), font, setAll);
+    }
+
+    /**
+     * Sets font.
+     *
+     * @param fontTag Font tag as String, as explained in {@link #setFont(org.rrd4j.graph.RrdGraphConstants.FontTag, Font, boolean)}.
+     * @param font Font to be used for tag
+     * @param setAll Boolean to flag whether to set all fonts if fontTag == FONTTAG_DEFAULT
+     * @param keepSizes Boolean to flag whether to keep original font sizes if setting all fonts.
+     */
+    public void setFont(final String fontTag, final Font font, final boolean setAll, final boolean keepSizes) {
+        this.setFont(FontTag.valueOf(fontTag), font, setAll, keepSizes);
+    }
+
+    public Font getFont(final FontTag tag) {
+        return this.fonts[tag.ordinal()] == null ? this.fonts[FONTTAG_DEFAULT.ordinal()] : this.fonts[tag.ordinal()];
+    }
+
+
+    /**
+     * Defines virtual datasource. This datasource can then be used
+     * in other methods like {@link #datasource(String, String)} or
+     * {@link #gprint(String, ConsolFun, String)}.
+     *
+     * @param name      Source name
+     * @param rrdPath   Path to RRD file
+     * @param dsName    Datasource name in the specified RRD file
+     * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
+     */
+    public void datasource(String name, String rrdPath, String dsName, ConsolFun consolFun) {
+        sources.add(new Def(name, rrdPath, dsName, consolFun));
+    }
+
+    /**
+     * Defines virtual datasource. This datasource can then be used
+     * in other methods like {@link #datasource(String, String)} or
+     * {@link #gprint(String, ConsolFun, String)}.
+     *
+     * @param name      Source name
+     * @param rrdPath   Path to RRD file
+     * @param dsName    Datasource name in the specified RRD file
+     * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
+     * @param backend   Backend to be used while fetching data from a RRD file.
+     * 
+     * @deprecated Uses {@link #datasource(String, String, String, ConsolFun, RrdBackendFactory)} instead
+     */
+    @Deprecated
+    public void datasource(String name, String rrdPath, String dsName, ConsolFun consolFun, String backend) {
+        sources.add(new Def(name, rrdPath, dsName, consolFun, RrdBackendFactory.getFactory(backend)));
+    }
+
+    /**
+     * Defines virtual datasource. This datasource can then be used
+     * in other methods like {@link #datasource(String, String)} or
+     * {@link #gprint(String, ConsolFun, String)}.
+     *
+     * @param name      Source name
+     * @param rrdPath   Path to RRD file
+     * @param dsName    Datasource name in the specified RRD file
+     * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
+     * @param backend   Backend to be used while fetching data from a RRD file.
+     */
+    public void datasource(String name, String rrdPath, String dsName, ConsolFun consolFun, RrdBackendFactory backend) {
+        sources.add(new Def(name, rrdPath, dsName, consolFun, backend));
+    }
+
+    /**
+     * Create a new virtual datasource by evaluating a mathematical
+     * expression, specified in Reverse Polish Notation (RPN).
+     *
+     * @param name          Source name
+     * @param rpnExpression RPN expression.
+     */
+    public void datasource(String name, String rpnExpression) {
+        sources.add(new CDef(name, rpnExpression));
+    }
+
+    /**
+     * Creates a new (static) virtual datasource. The value of the datasource is constant. This value is
+     * evaluated by applying the given consolidation function to another virtual datasource.
+     *
+     * @param name      Source name
+     * @param defName   Other source name
+     * @param consolFun Consolidation function to be applied to other datasource.
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void datasource(String name, String defName, ConsolFun consolFun) {
+        datasource(name, defName, consolFun.getVariable());
+    }
+
+    public void datasource(String name, String defName, Variable var) {
+        sources.add(new VDef(name, defName, var));
+    }
+
+    /**
+     * Creates a new (plottable) datasource. Datasource values are obtained from the given plottable
+     * object.
+     *
+     * @param name      Source name.
+     * @param plottable Plottable object.
+     */
+    public void datasource(String name, Plottable plottable) {
+        sources.add(new PDef(name, plottable));
+    }
+
+    /**
+     * Creates a new 'fetched' datasource. Datasource values are obtained from the
+     * given {@link org.rrd4j.core.FetchData} object.
+     *
+     * @param name      Source name.
+     * @param fetchData FetchData object.
+     */
+    public void datasource(String name, FetchData fetchData) {
+        sources.add(new TDef(name, name, fetchData));
+    }
+
+    /**
+     * Creates a new 'fetched' datasource. Datasource values are obtained from the
+     * given {@link org.rrd4j.core.FetchData} object. 
+     * Values will be extracted from the datasource dsName in the fetchData
+     *
+     * @param name      Source name.
+     * @param dsName    Source name in fetchData.
+     * @param fetchData FetchData object.
+     */
+    public void datasource(String name, String dsName, FetchData fetchData) {
+        sources.add(new TDef(name, dsName, fetchData));
+    }
+
+    /**
+     * Create a new virtual datasource to get the 95th percentile value from another datasource
+     *
+     * @param name    Source name.
+     * @param defName Other source name.
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void percentile(String name, String defName) {
+        percentile(name, defName, DataProcessor.DEFAULT_PERCENTILE);
+    }
+
+    /**
+     * Create a new virtual datasource to get a percentile value from another datasource
+     *
+     * @param name    Source name.
+     * @param defName Other source name.
+     * @param percent The percent value.
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void percentile(String name, String defName, double percent) {
+        datasource(name, defName, new Variable.PERCENTILE(percent));
+    }
+
+    /**
+     * <p>Calculates the chosen consolidation function CF over the given datasource
+     * and creates the result by using the given format string.  In
+     * the format string there should be a '%[l]f', '%[l]g' or '%[l]e' marker in
+     * the place where the number should be printed.</p>
+     * 
+     * <p>If an additional '%s' is found AFTER the marker, the value will be
+     * scaled and an appropriate SI magnitude unit will be printed in
+     * place of the '%s' marker. The scaling will take the '--base' argument into consideration!</p>
+     * 
+     * <p>If a '%S' is used instead of a '%s', then instead of calculating
+     * the appropriate SI magnitude unit for this value, the previously
+     * calculated SI magnitude unit will be used.  This is useful if you
+     * want all the values in a print statement to have the same SI magnitude unit.
+     * If there was no previous SI magnitude calculation made,
+     * then '%S' behaves like a '%s', unless the value is 0, in which case
+     * it does not remember a SI magnitude unit and a SI magnitude unit
+     * will only be calculated when the next '%s' is seen or the next '%S'
+     * for a non-zero value.</p>
+     * 
+     * <p>Print results are collected in the {@link org.rrd4j.graph.RrdGraphInfo} object which is retrieved
+     * from the {@link RrdGraph object} once the graph is created.</p>
+     *
+     * @param srcName   Virtual source name
+     * @param consolFun Consolidation function to be applied to the source
+     * @param format    Format string (like "average = %10.3f %s")
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void print(String srcName, ConsolFun consolFun, String format) {
+        Variable var = consolFun.getVariable();
+        String tempName = srcName + "_" + var.hashCode();
+        datasource(tempName, srcName, var);
+        comments.add(new PrintText(tempName, format, false, false));
+    }
+
+    /**
+     * <p>Read the value of a variable (VDEF) and prints the value by using the given format string.  In
+     * the format string there should be a '%[l]f', '%[l]g' or '%[l]e' marker in
+     * the place where the number should be printed.</p>
+     * 
+     * <p>If an additional '%s' is found AFTER the marker, the value will be
+     * scaled and an appropriate SI magnitude unit will be printed in
+     * place of the '%s' marker. The scaling will take the '--base' argument into consideration!</p>
+     * 
+     * <p>If a '%S' is used instead of a '%s', then instead of calculating
+     * the appropriate SI magnitude unit for this value, the previously
+     * calculated SI magnitude unit will be used.  This is useful if you
+     * want all the values in a print statement to have the same SI magnitude unit.
+     * If there was no previous SI magnitude calculation made,
+     * then '%S' behaves like a '%s', unless the value is 0, in which case
+     * it does not remember a SI magnitude unit and a SI magnitude unit
+     * will only be calculated when the next '%s' is seen or the next '%S'
+     * for a non-zero value.</p>
+     * 
+     * <p>Print results are collected in the {@link org.rrd4j.graph.RrdGraphInfo} object which is retrieved
+     * from the {@link RrdGraph object} once the graph is created.</p>
+     *
+     * @param srcName   Virtual source name
+     * @param format    Format string (like "average = %10.3f %s")
+     */
+    public void print(String srcName, String format) {
+        print(srcName, format, false);
+    }
+
+    /**
+     * <p>Read the value of a variable (VDEF) and prints the the value or the time stamp, according to the strftime flag
+     * by using the given format string.  In
+     * and creates the result by using the given format string.  In
+     * the format string there should be a '%[l]f', '%[l]g' or '%[l]e' marker in
+     * the place where the number should be printed.</p>
+     * 
+     * <p>If an additional '%s' is found AFTER the marker, the value will be
+     * scaled and an appropriate SI magnitude unit will be printed in
+     * place of the '%s' marker. The scaling will take the '--base' argument into consideration!</p>
+     * 
+     * <p>If a '%S' is used instead of a '%s', then instead of calculating
+     * the appropriate SI magnitude unit for this value, the previously
+     * calculated SI magnitude unit will be used.  This is useful if you
+     * want all the values in a print statement to have the same SI magnitude unit.
+     * If there was no previous SI magnitude calculation made,
+     * then '%S' behaves like a '%s', unless the value is 0, in which case
+     * it does not remember a SI magnitude unit and a SI magnitude unit
+     * will only be calculated when the next '%s' is seen or the next '%S'
+     * for a non-zero value.</p>
+     * 
+     * <p>Print results are collected in the {@link org.rrd4j.graph.RrdGraphInfo} object which is retrieved
+     * from the {@link RrdGraph object} once the graph is created.</p>
+     *
+     * @param srcName   Virtual source name
+     * @param format    Format string (like "average = %10.3f %s")
+     * @param strftime  use the timestamp from the variable (true) or the numerical value (false)
+     */
+    public void print(String srcName, String format, boolean strftime) {
+        comments.add(new PrintText(srcName, format, false, strftime));
+    }
+
+    /**
+     * This method does basically the same thing as {@link #print(String, ConsolFun, String)},
+     * but the result is printed on the graph itself, below the chart area.
+     *
+     * @param srcName   Virtual source name.
+     * @param consolFun Consolidation function to be applied to the source.
+     * @param format    Format string (like "average = %10.3f %s")
+     * @deprecated Use {@link Variable} based method instead.
+     */
+    @Deprecated
+    public void gprint(String srcName, ConsolFun consolFun, String format) {
+        Variable var = consolFun.getVariable();
+        String tempName = srcName + "_" + var.hashCode();
+        this.datasource(tempName, srcName, var);
+        comments.add(new PrintText(tempName, format, true, false));
+    }
+
+    /**
+     * <p>Read the value of a variable (VDEF) and prints the value by using the given format string.  In
+     * the format string there should be a '%[l]f', '%[l]g' or '%[l]e' marker in
+     * the place where the number should be printed.</p>
+     * 
+     * <p>If an additional '%s' is found AFTER the marker, the value will be
+     * scaled and an appropriate SI magnitude unit will be printed in
+     * place of the '%s' marker. The scaling will take the '--base' argument into consideration!</p>
+     * 
+     * <p>If a '%S' is used instead of a '%s', then instead of calculating
+     * the appropriate SI magnitude unit for this value, the previously
+     * calculated SI magnitude unit will be used.  This is useful if you
+     * want all the values in a print statement to have the same SI magnitude unit.
+     * If there was no previous SI magnitude calculation made,
+     * then '%S' behaves like a '%s', unless the value is 0, in which case
+     * it does not remember a SI magnitude unit and a SI magnitude unit
+     * will only be calculated when the next '%s' is seen or the next '%S'
+     * for a non-zero value.</p>
+     * 
+     * print results are added to the graph as a legend
+     *
+     * @param srcName   Virtual source name
+     * @param format    Format string (like "average = %10.3f %s")
+     */
+    public void gprint(String srcName, String format) {
+        gprint(srcName, format, false);
+    }
+
+    /**
+     * <p>Read the value of a variable (VDEF) and prints the the value or the time stamp, according to the strftime flag
+     * by using the given format string.  In
+     * and creates the result by using the given format string.  In
+     * the format string there should be a '%[l]f', '%[l]g' or '%[l]e' marker in
+     * the place where the number should be printed.</p>
+     * 
+     * <p>If an additional '%s' is found AFTER the marker, the value will be
+     * scaled and an appropriate SI magnitude unit will be printed in
+     * place of the '%s' marker. The scaling will take the '--base' argument into consideration!</p>
+     * 
+     * <p>If a '%S' is used instead of a '%s', then instead of calculating
+     * the appropriate SI magnitude unit for this value, the previously
+     * calculated SI magnitude unit will be used.  This is useful if you
+     * want all the values in a print statement to have the same SI magnitude unit.
+     * If there was no previous SI magnitude calculation made,
+     * then '%S' behaves like a '%s', unless the value is 0, in which case
+     * it does not remember a SI magnitude unit and a SI magnitude unit
+     * will only be calculated when the next '%s' is seen or the next '%S'
+     * for a non-zero value.</p>
+     * 
+     * <p>print results are added to the graph as a legend.</p>
+     *
+     * @param srcName   Virtual source name
+     * @param format    Format string (like "average = %10.3f %s")
+     * @param strftime  use the timestamp from the variable (true) or the numerical value (false)
+     */
+    public void gprint(String srcName, String format, boolean strftime) {
+        comments.add(new PrintText(srcName, format, true, strftime));
+    }
+
+    /**
+     * Comment to be printed on the graph.
+     *
+     * @param text Comment text
+     */
+    public void comment(String text) {
+        comments.add(new CommentText(text));
+    }
+
+    /**
+     * Draws a horizontal rule into the graph.
+     *
+     * @param value Position of the rule
+     * @param color Rule color
+     */
+    public void hrule(double value, Paint color) {
+        hrule(value, color, null, 1.0F);
+    }
+
+    /**
+     * Draws a horizontal rule into the graph and optionally adds a legend.
+     *
+     * @param value  Position of the rule
+     * @param color  Rule color
+     * @param legend Legend text. If null, legend text will be omitted.
+     */
+    public void hrule(double value, Paint color, String legend) {
+        hrule(value, color, legend, 1.0F);
+    }
+
+    /**
+     * Draws a horizontal rule into the graph and optionally adds a legend.
+     *
+     * @param value  Position of the rule
+     * @param color  Rule color
+     * @param legend Legend text. If null, legend text will be omitted.
+     * @param width  Rule width
+     */
+    public void hrule(double value, Paint color, String legend, float width) {
+        hrule(value, color, legend, new BasicStroke(width));
+    }
+
+    /**
+     * Draws a horizontal rule into the graph and optionally adds a legend.
+     *
+     * @param value  Position of the rule
+     * @param color  Rule color
+     * @param legend Legend text. If null, legend text will be omitted.
+     * @param stroke Rule stroke
+     */
+    public void hrule(double value, Paint color, String legend, BasicStroke stroke) {
+        LegendText legendText = new LegendText(color, legend);
+        comments.add(legendText);
+        plotElements.add(new HRule(value, color, legendText, stroke));
+    }
+
+    /**
+     * Draws a vertical rule into the graph.
+     *
+     * @param timestamp Position of the rule (seconds since epoch)
+     * @param color     Rule color
+     */
+    public void vrule(long timestamp, Paint color) {
+        vrule(timestamp, color, null, 1.0F);
+    }
+
+    /**
+     * Draws a vertical rule into the graph and optionally adds a legend
+     *
+     * @param timestamp Position of the rule (seconds since epoch)
+     * @param color     Rule color
+     * @param legend    Legend text. Use null to omit the text.
+     */
+    public void vrule(long timestamp, Paint color, String legend) {
+        vrule(timestamp, color, legend, 1.0F);
+    }
+
+    /**
+     * Draws a vertical rule into the graph and optionally adds a legend
+     *
+     * @param timestamp Position of the rule (seconds since epoch)
+     * @param color     Rule color
+     * @param legend    Legend text. Use null to omit the text.
+     * @param width     Rule width
+     */
+    public void vrule(long timestamp, Paint color, String legend, float width) {
+        vrule(timestamp, color, legend, new BasicStroke(width));
+    }
+
+    /**
+     * Draws a vertical rule into the graph and optionally adds a legend
+     *
+     * @param timestamp Position of the rule (seconds since epoch)
+     * @param color     Rule color
+     * @param legend    Legend text. Use null to omit the text.
+     * @param stroke    Rule stroke
+     */
+    public void vrule(long timestamp, Paint color, String legend, BasicStroke stroke) {
+        LegendText legendText = new LegendText(color, legend);
+        comments.add(legendText);
+        plotElements.add(new VRule(timestamp, color, legendText, stroke));
+    }
+
+    /**
+     * Draws a horizontal span into the graph.
+     *
+     * @param start Starting value of the span
+     * @param end   Ending value of the span
+     * @param color Rule color
+     */
+    public void hspan(double start, double end, Paint color) {
+        hspan(start, end, color, null);
+    }
+
+    /**
+     * Draws a horizontal span into the graph and optionally adds a legend.
+     *
+     * @param start     Starting value of the span
+     * @param end       Ending value of the span
+     * @param color     Rule color
+     * @param legend    Legend text. Use null to omit the text.
+     */
+    public void hspan(double start, double end, Paint color, String legend) {
+        LegendText legendText = new LegendText(color, legend);
+        comments.add(legendText);
+        plotElements.add(new HSpan(start, end, color, legendText));
+    }
+
+    /**
+     * Draws a vertical span into the graph.
+     *
+     * @param start     Start time for the span (seconds since epoch)
+     * @param end       End time for the span (seconds since epoch)
+     * @param color     Rule color
+     */
+    public void vspan(long start, long end, Paint color) {
+        vspan(start, end, color, null);
+    }
+
+    /**
+     * Draws a vertical span into the graph and optionally adds a legend.
+     *
+     * @param start     Start time for the span (seconds since epoch)
+     * @param end       End time for the span (seconds since epoch)
+     * @param color     Rule color
+     * @param legend    Legend text. Use null to omit the text.
+     */
+    public void vspan(long start, long end, Paint color, String legend) {
+        LegendText legendText = new LegendText(color, legend);
+        comments.add(legendText);
+        plotElements.add(new VSpan(start, end, color, legendText));
+    }
+
+    /**
+     * Plots requested data as a line, using the color specified. Line width is assumed to be
+     * 1.0F.
+     *
+     * @param srcName Virtual source name
+     * @param color   Line color
+     */
+    public void line(String srcName, Paint color) {
+        line(srcName, color, null, 1F, false);
+    }
+
+    /**
+     * Plots requested data as a line, using the color specified. Line width is assumed to be
+     * 1.0F.
+     *
+     * @param srcName Virtual source name
+     * @param color   Line color
+     * @param legend  Legend text
+     */
+    public void line(String srcName, Paint color, String legend) {
+        line(srcName, color, legend, 1F, false);
+    }
+
+
+    /**
+     * Plots requested data as a line, using the color and the line width specified.
+     *
+     * @param srcName Virtual source name
+     * @param color   Line color
+     * @param width   Line width (default: 1.0F)
+     */
+    public void line(String srcName, Paint color, float width) {
+        line(srcName, color, null, width, false);
+    }
+
+    /**
+     * Plots requested data as a line, using the color and the line width specified.
+     *
+     * @param srcName Virtual source name
+     * @param color   Line color
+     * @param legend  Legend text
+     * @param width   Line width (default: 1.0F)
+     */
+    public void line(String srcName, Paint color, String legend, float width) {
+        line(srcName, color, legend, width, false);
+    }
+
+    /**
+     * Plots requested data as a line, using the color and the line width specified.
+     *
+     * @param srcName Virtual source name
+     * @param color   Line color.
+     * @param legend  Legend text.
+     * @param width   Line width (default: 1.0F).
+     * @param stack   true if it will be stacked.
+     */
+    public void line(String srcName, Paint color, String legend, float width, boolean stack) {
+        if (legend != null) {
+            comments.add(new LegendText(color, legend));
+        }
+        SourcedPlotElement parent = stack ? findParent() : null;
+        plotElements.add(new Line(srcName, color, new BasicStroke(width), parent));
+    }
+
+    /**
+     * Plots requested data as a line, using the color and the {@link java.awt.BasicStroke} specified.
+     *
+     * @param srcName Virtual source name
+     * @param color   Line color.
+     * @param legend  Legend text.
+     * @param stroke  Line stroke to use.
+     * @param stack   true if it will be stacked.
+     */
+    public void line(String srcName, Paint color, String legend, BasicStroke stroke, boolean stack) {
+        if (legend != null) {
+            comments.add(new LegendText(color, legend));
+        }
+        SourcedPlotElement parent = stack ? findParent() : null;
+        plotElements.add(new Line(srcName, color, stroke, parent));
+    }
+
+    /**
+     * Define a line like any other but with constant value, it can be stacked
+     * @param value Line position.
+     * @param color Line color.
+     * @param width Line width (default: 1.0F).
+     * @param stack true if it will be stacked.
+     */
+    public void line(double value, Paint color, float width, boolean stack) {
+        SourcedPlotElement parent = stack ? findParent() : null;
+        plotElements.add(new ConstantLine(value, color, new BasicStroke(width), parent));
+    }
+
+    /**
+     * Define a line like any other but with constant value, it can be stacked
+     * @param value  Line position.
+     * @param color  Line color.
+     * @param stroke Line stroke to use.
+     * @param stack  true if it will be stacked.
+     */
+    public void line(double value, Paint color, BasicStroke stroke, boolean stack) {
+        SourcedPlotElement parent = stack ? findParent() : null;
+        plotElements.add(new ConstantLine(value, color, stroke, parent));
+    }
+
+    /**
+     * Plots requested data in the form of the filled area starting from zero, using
+     * the color specified.
+     *
+     * @param srcName Virtual source name.
+     * @param color   Color of the filled area.
+     */
+    public void area(String srcName, Paint color) {
+        area(srcName, color, null, false);
+    }
+
+    /**
+     * Plots requested data in the form of the filled area starting from zero, using
+     * the color specified.
+     *
+     * @param srcName Virtual source name.
+     * @param color   Color of the filled area.
+     * @param legend  Legend text.
+     */
+    public void area(String srcName, Paint color, String legend) {
+        area(srcName, color, legend, false);
+    }
+
+    /**
+     * Plots requested data in the form of the filled area starting from zero, using
+     * the color specified.
+     *
+     * @param srcName Virtual source name.
+     * @param color   Color of the filled area.
+     * @param legend  Legend text.
+     * @param stack   true if it will be stacked.
+     */
+    public void area(String srcName, Paint color, String legend, boolean stack) {
+        if (legend != null) {
+            comments.add(new LegendText(color, legend));
+        }
+        SourcedPlotElement parent = stack ? findParent() : null;
+        plotElements.add(new Area(srcName, color, parent));
+    }
+
+    /**
+     * Add a area like any other but with a constant value, it can be stacked like any other area
+     * @param value Area position
+     * @param color Color of the filled area.
+     * @param stack true if it will be stacked.
+     */
+    public void area(double value, Paint color, boolean stack) {
+        SourcedPlotElement parent = stack ? findParent() : null;
+        plotElements.add(new ConstantArea(value, color, parent));
+    }
+
+    /**
+     * <p>Does the same as {@link #line(String, java.awt.Paint)},
+     * but the graph gets stacked on top of the
+     * previous LINE, AREA or STACK graph. Depending on the type of the
+     * previous graph, the STACK will be either a LINE or an AREA.  This
+     * obviously implies that the first STACK must be preceded by an AREA
+     * or LINE.</p>
+     * 
+     * <p>Note, that when you STACK onto *UNKNOWN* data, Rrd4j will not
+     * draw any graphics ... *UNKNOWN* is not zero.</p>
+     *
+     * @param srcName Virtual source name
+     * @param color   Stacked graph color
+     * @throws java.lang.IllegalArgumentException Thrown if this STACK has no previously defined AREA, STACK or LINE
+     *                                  graph bellow it.
+     */
+    public void stack(String srcName, Paint color) {
+        stack(srcName, color, null);
+    }
+
+    /**
+     * <p>Does the same as {@link #line(String, java.awt.Paint, String)},
+     * but the graph gets stacked on top of the
+     * previous LINE, AREA or STACK graph. Depending on the type of the
+     * previous graph, the STACK will be either a LINE or an AREA.  This
+     * obviously implies that the first STACK must be preceded by an AREA
+     * or LINE.</p>
+     * 
+     * Note, that when you STACK onto *UNKNOWN* data, Rrd4j will not
+     * draw any graphics ... *UNKNOWN* is not zero.
+     *
+     * @param srcName Virtual source name
+     * @param color   Stacked graph color
+     * @param legend  Legend text
+     * @throws java.lang.IllegalArgumentException Thrown if this STACK has no previously defined AREA, STACK or LINE
+     *                                  graph bellow it.
+     */
+    public void stack(String srcName, Paint color, String legend) {
+        SourcedPlotElement parent = findParent();
+        if (legend != null) {
+            comments.add(new LegendText(color, legend));
+        }
+        plotElements.add(new Stack(parent, srcName, color));
+    }
+
+    private SourcedPlotElement findParent() {
+        // find parent AREA or LINE
+        SourcedPlotElement parent = null;
+        for (int i = plotElements.size() - 1; i >= 0; i--) {
+            PlotElement plotElement = plotElements.get(i);
+            if (plotElement instanceof SourcedPlotElement) {
+                parent = (SourcedPlotElement) plotElement;
+                break;
+            }
+        }
+        if (parent == null) 
+            throw new IllegalArgumentException("You have to stack graph onto something (line or area)");
+        return parent;
+    }
+
+    /**
+     * Sets visibility of the X-axis grid.
+     *
+     * @param drawXGrid True if X-axis grid should be created (default), false otherwise.
+     */
+    public void setDrawXGrid(boolean drawXGrid) {
+        this.drawXGrid = drawXGrid;
+    }
+
+    /**
+     * Sets visibility of the Y-axis grid.
+     *
+     * @param drawYGrid True if Y-axis grid should be created (default), false otherwise.
+     */
+    public void setDrawYGrid(boolean drawYGrid) {
+        this.drawYGrid = drawYGrid;
+    }
+
+    /**
+     * Sets image quality. Relevant only for JPEG images.
+     *
+     * @param imageQuality (0F=worst, 1F=best).
+     */
+    public void setImageQuality(float imageQuality) {
+        this.imageQuality = imageQuality;
+    }
+
+    /**
+     * Controls if the chart area of the image should be antialiased or not.
+     *
+     * @param antiAliasing use true to turn antialiasing on, false to turn it off (default)
+     */
+    public void setAntiAliasing(boolean antiAliasing) {
+        this.antiAliasing = antiAliasing;
+    }
+
+    /**
+     * Controls if the text should be antialiased or not.
+     *
+     * @param textAntiAliasing use true to turn text-antialiasing on, false to turn it off (default)
+     */
+    public void setTextAntiAliasing(boolean textAntiAliasing) {
+        this.textAntiAliasing = textAntiAliasing;
+    }
+
+    /**
+     * Sets the signature string that runs along the right-side of the graph.
+     * Defaults to "Generated by RRD4J".
+     *
+     * @param signature the string to print
+     */
+    public void setSignature(String signature) {
+        this.signature = signature;
+    }
+
+    /**
+     * Gets the signature string that runs along the right-side of the graph.
+     *
+     * @return the signature string
+     */
+    public String getSignature() {
+        return this.signature;
+    }
+
+    /**
+     * Shows or hides graph signature (gator) in the top right corner of the graph
+     *
+     * @param showSignature true, if signature should be seen (default), false otherwise
+     */
+    public void setShowSignature(boolean showSignature) {
+        this.showSignature = showSignature;
+    }
+
+    /**
+     * Sets first day of the week.
+     *
+     * @param firstDayOfWeek One of the following constants:
+     *                       {@link org.rrd4j.graph.RrdGraphConstants#MONDAY MONDAY},
+     *                       {@link org.rrd4j.graph.RrdGraphConstants#TUESDAY TUESDAY},
+     *                       {@link org.rrd4j.graph.RrdGraphConstants#WEDNESDAY WEDNESDAY},
+     *                       {@link org.rrd4j.graph.RrdGraphConstants#THURSDAY THURSDAY},
+     *                       {@link org.rrd4j.graph.RrdGraphConstants#FRIDAY FRIDAY},
+     *                       {@link org.rrd4j.graph.RrdGraphConstants#SATURDAY SATURDAY},
+     *                       {@link org.rrd4j.graph.RrdGraphConstants#SUNDAY SUNDAY}
+     */
+    public void setFirstDayOfWeek(int firstDayOfWeek) {
+        this.firstDayOfWeek = firstDayOfWeek;
+    }
+
+    /**
+     * Set the locale used for the legend.
+     * <p>
+     *
+     * It overides the firstDayOfWeek
+     *
+     * @param locale the locale to set
+     */
+    public void setLocale(Locale locale) {
+        this.locale = locale;
+        this.firstDayOfWeek = Calendar.getInstance(Locale.getDefault()).getFirstDayOfWeek();
+    }
+
+    /**
+     * Set the time zone used for the legend.
+     *
+     * @param tz the time zone to set
+     */
+    public void setTimeZone(TimeZone tz) {
+        this.tz = tz;
+    }
+
+    /**
+     * Set the Stroke used to draw grid
+     *
+     * @param gridStroke a {@link java.awt.Stroke} object.
+     */
+    public void setGridStroke(Stroke gridStroke) {
+        this.gridStroke = gridStroke;
+    }
+
+    /**
+     * Set the stroke used to draw ticks
+     *
+     * @param tickStroke a {@link java.awt.Stroke} object.
+     */
+    public void setTickStroke(Stroke tickStroke) {
+        this.tickStroke = tickStroke;
+    }
+
+    /**
+     * Allows to set a downsampler, used to improved the visual representation of graph.
+     * <p>
+     * More details can be found on <a href="http://skemman.is/en/item/view/1946/15343">Sveinn Steinarsson's thesis</a>
+     * 
+     * @param downsampler The downsampler that will be used
+     */
+    public void setDownsampler(DownSampler downsampler) {
+        this.downsampler = downsampler;
+    }
+
+    int printStatementCount() {
+        int count = 0;
+        for (CommentText comment : comments) {
+            if (comment instanceof PrintText) {
+                if (comment.isPrint()) {
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+
+    boolean shouldPlot() {
+        if (plotElements.size() > 0) {
+            return true;
+        }
+        for (CommentText comment : comments) {
+            if (comment.isValidGraphElement()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    Paint getColor(ElementsNames element) {
+        return colors[element.ordinal()];
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphInfo.java b/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a3e73ce16b20ba4c705741271b6a3be9c6826a6
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/RrdGraphInfo.java
@@ -0,0 +1,127 @@
+package org.rrd4j.graph;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to represent successfully created Rrd4j graph. Objects of this class are created by method
+ * {@link org.rrd4j.graph.RrdGraph#getRrdGraphInfo()}.
+ */
+public class RrdGraphInfo {
+    String filename;
+    int width, height;
+    InputStream stream;
+    String imgInfo;
+    private List<String> printLines = new ArrayList<String>();
+
+    RrdGraphInfo() {
+        // cannot instantiate this class
+    }
+
+    void addPrintLine(String printLine) {
+        printLines.add(printLine);
+    }
+
+    /**
+     * Returns filename of the graph
+     *
+     * @return filename of the graph. '-' denotes in-memory graph (no file created)
+     */
+    public String getFilename() {
+        return filename;
+    }
+
+    /**
+     * Returns total graph width
+     *
+     * @return total graph width
+     */
+    public int getWidth() {
+        return width;
+    }
+
+    /**
+     * Returns total graph height
+     *
+     * @return total graph height
+     */
+    public int getHeight() {
+        return height;
+    }
+
+    /**
+     * Returns graph bytes
+     *
+     * @return Graph bytes
+     * @throws IllegalStateException if the images bytes are unavailable or can't be read
+     */
+    public byte[] getBytes() {
+        try {
+            byte[] content = new byte[stream.available()];
+            int read = stream.read(content);
+            if (read != content.length) {
+                throw new IllegalStateException("Unable to read image buffer");
+            }
+            return content;
+        } catch (IOException e) {
+            throw new IllegalStateException("Unable to read image bytes", e);
+        }
+    }
+
+    /**
+     * Returns PRINT lines requested by {@link org.rrd4j.graph.RrdGraphDef#print(String, org.rrd4j.ConsolFun, String)} method.
+     *
+     * @return An array of formatted PRINT lines
+     */
+    public String[] getPrintLines() {
+        return printLines.toArray(new String[printLines.size()]);
+    }
+
+    /**
+     * Returns image information requested by {@link org.rrd4j.graph.RrdGraphDef#setImageInfo(String)} method
+     *
+     * @return Image information
+     */
+    public String getImgInfo() {
+        return imgInfo;
+    }
+
+    /**
+     * Returns the number of bytes in the graph file
+     *
+     * @return Length of the graph file
+     * @throws IllegalStateException if the images bytes are unavailable
+     */
+    public int getByteCount() {
+        try {
+            return stream.available();
+        } catch (IOException e) {
+            throw new IllegalStateException("Unable to read image bytes", e);
+        }
+    }
+
+    /**
+     * Dumps complete graph information. Useful for debugging purposes.
+     *
+     * @return String containing complete graph information
+     */
+    public String dump() {
+        StringBuilder b = new StringBuilder();
+        b.append("filename = \"").append(getFilename()).append("\"\n");
+        b.append("width = ").append(getWidth()).append(", height = ").append(getHeight()).append("\n");
+        b.append("byteCount = ").append(getByteCount()).append("\n");
+        b.append("imginfo = \"").append(getImgInfo()).append("\"\n");
+        String[] plines = getPrintLines();
+        if (plines.length == 0) {
+            b.append("No print lines found\n");
+        }
+        else {
+            for (int i = 0; i < plines.length; i++) {
+                b.append("print[").append(i).append("] = \"").append(plines[i]).append("\"\n");
+            }
+        }
+        return b.toString();
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Rule.java b/apps/jrobin/java/src/org/rrd4j/graph/Rule.java
new file mode 100644
index 0000000000000000000000000000000000000000..c794d73cc0402a22e260e3da4cf9e2d945742d4e
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Rule.java
@@ -0,0 +1,15 @@
+package org.rrd4j.graph;
+
+import java.awt.BasicStroke;
+import java.awt.Paint;
+
+class Rule extends PlotElement {
+    final LegendText legend;
+    final BasicStroke stroke;
+
+    Rule(Paint color, LegendText legend, BasicStroke stroke) {
+        super(color);
+        this.legend = legend;
+        this.stroke = stroke;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/SimpleTimeLabelFormat.java b/apps/jrobin/java/src/org/rrd4j/graph/SimpleTimeLabelFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..8457a2eed3dfbda3770319a65e2de87fdb8f3003
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/SimpleTimeLabelFormat.java
@@ -0,0 +1,43 @@
+package org.rrd4j.graph;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Simple time label using a format similar to {@code strftime}. For more details on the
+ * supported conversions see the Date/Time Conversions section for {@link java.util.Formatter}.
+ * Examples:
+ *
+ * <ul>
+ *   <li>strftime pattern: {@code %Y-%m-%dT%H:%M:%S}</li>
+ *   <li>simple date format pattern: {@code yyyy'-'MM'-'dd'T'HH':'mm':'ss}</li>
+ * </ul>
+ */
+public class SimpleTimeLabelFormat implements TimeLabelFormat {
+
+    private final String format;
+
+    /**
+     * Create a new instance using a format string that is either an strftime patter or a simple
+     * date format pattern.
+     *
+     * @param format
+     */
+    public SimpleTimeLabelFormat(String format) {
+        // escape strftime like format string
+        this.format = format.replaceAll("%([^%])", "%1\\$t$1");
+    }
+
+    @Override
+    public String format(Calendar calendar, Locale locale) {
+        if (format.contains("%")) {
+            // strftime like format string
+            return String.format(locale, format, calendar);
+        } else {
+            SimpleDateFormat sdf = new SimpleDateFormat(format);
+            sdf.setCalendar(calendar);
+            return sdf.format(calendar.getTime());
+        }
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Source.java b/apps/jrobin/java/src/org/rrd4j/graph/Source.java
new file mode 100644
index 0000000000000000000000000000000000000000..430ea314cfb593dfa5a7d3098a5d39f35143604a
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Source.java
@@ -0,0 +1,13 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.data.DataProcessor;
+
+abstract class Source {
+    final String name;
+
+    Source(String name) {
+        this.name = name;
+    }
+
+    abstract void requestData(DataProcessor dproc);
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/SourcedPlotElement.java b/apps/jrobin/java/src/org/rrd4j/graph/SourcedPlotElement.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd00c735b9e101cee4eb561b9061202abdb8fbea
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/SourcedPlotElement.java
@@ -0,0 +1,67 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.core.Util;
+import org.rrd4j.data.DataProcessor;
+
+import java.awt.*;
+
+class SourcedPlotElement extends PlotElement {
+    final String srcName;
+    final SourcedPlotElement parent;
+    double[] values;
+
+    SourcedPlotElement(String srcName, Paint color) {
+        super(color);
+        this.srcName = srcName;
+        this.parent = null;
+    }
+
+    SourcedPlotElement(String srcName, Paint color, SourcedPlotElement parent) {
+        super(color);
+        this.srcName = srcName;
+        this.parent = parent;
+    }
+
+    void assignValues(DataProcessor dproc) {
+        if(parent == null) {
+            values = dproc.getValues(srcName);
+        }
+        else {
+            values = stackValues(dproc);
+        }
+    }
+
+    double[] stackValues(DataProcessor dproc) {
+        double[] parentValues = parent.getValues();
+        double[] procValues = dproc.getValues(srcName);
+        double[] stacked = new double[procValues.length];
+        for (int i = 0; i < stacked.length; i++) {
+            if (Double.isNaN(parentValues[i])) {
+                stacked[i] = procValues[i];
+            }
+            else if (Double.isNaN(procValues[i])){
+                stacked[i] = parentValues[i];
+            }
+            else {
+                stacked[i] = parentValues[i] + procValues[i];
+            }
+        }
+        return stacked;
+    }
+
+    Paint getParentColor() {
+        return parent != null ? parent.color : null;
+    }
+
+    double[] getValues() {
+        return values;
+    }
+
+    double getMinValue() {
+        return Util.min(values);
+    }
+
+    double getMaxValue() {
+        return Util.max(values);
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Span.java b/apps/jrobin/java/src/org/rrd4j/graph/Span.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d7ec1c7d794f69dda8bc20446a87d9e3a00ba12
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Span.java
@@ -0,0 +1,12 @@
+package org.rrd4j.graph;
+
+import java.awt.*;
+
+class Span extends PlotElement {
+    final LegendText legend;
+
+    Span(Paint color, LegendText legend) {
+        super(color);
+        this.legend = legend;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/Stack.java b/apps/jrobin/java/src/org/rrd4j/graph/Stack.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ab42e6df539c913985af743934a27bf9153aeca
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/Stack.java
@@ -0,0 +1,46 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.data.DataProcessor;
+
+import java.awt.*;
+
+class Stack extends SourcedPlotElement {
+
+    Stack(SourcedPlotElement parent, String srcName, Paint color) {
+        super(srcName, color, parent);
+    }
+
+    void assignValues(DataProcessor dproc) {
+        double[] parentValues = parent.getValues();
+        double[] procValues = dproc.getValues(srcName);
+        values = new double[procValues.length];
+        for (int i = 0; i < values.length; i++) {
+            if (Double.isNaN(parentValues[i])) {
+                values[i] = procValues[i];
+            }
+            else if (Double.isNaN(procValues[i])){
+                values[i] = parentValues[i];
+            }
+            else {
+                values[i] = parentValues[i] + procValues[i];
+                
+            }
+        }
+    }
+
+    float getParentLineWidth() {
+        if (parent instanceof Line) {
+            return ((Line) parent).stroke.getLineWidth();
+        }
+        else if (parent instanceof Area) {
+            return -1F;
+        }
+        else /* if(parent instanceof Stack) */ {
+            return ((Stack) parent).getParentLineWidth();
+        }
+    }
+
+    Paint getParentColor() {
+        return parent.color;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/TDef.java b/apps/jrobin/java/src/org/rrd4j/graph/TDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..dcddb8694d023b3b00f463723f9e6a6c7d12c4b8
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/TDef.java
@@ -0,0 +1,22 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.core.FetchData;
+import org.rrd4j.data.DataProcessor;
+
+/** @author Mathias Bogaert */
+class TDef extends Source {
+    private final FetchData fetchData;
+    private final String dsName;
+
+    TDef(String name, String dsName, FetchData fetchData) {
+        super(name);
+        this.dsName = dsName;
+        this.fetchData = fetchData;
+    }
+
+    @Override
+    void requestData(DataProcessor dproc) {
+        dproc.addDatasource(name, dsName, fetchData);
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/TimeAxis.java b/apps/jrobin/java/src/org/rrd4j/graph/TimeAxis.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f6068c5d2dde6294b6fde8a0875b4146061b032
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/TimeAxis.java
@@ -0,0 +1,211 @@
+package org.rrd4j.graph;
+
+import java.awt.Font;
+import java.awt.Paint;
+import java.util.Calendar;
+import java.util.Date;
+
+class TimeAxis extends Axis {
+    private static final TimeAxisSetting[] tickSettings = {
+            new TimeAxisSetting(0, SECOND, 30, MINUTE, 5, MINUTE, 5, 0, HH_MM),
+            new TimeAxisSetting(2, MINUTE, 1, MINUTE, 5, MINUTE, 5, 0, HH_MM),
+            new TimeAxisSetting(5, MINUTE, 2, MINUTE, 10, MINUTE, 10, 0, HH_MM),
+            new TimeAxisSetting(10, MINUTE, 5, MINUTE, 20, MINUTE, 20, 0, HH_MM),
+            new TimeAxisSetting(30, MINUTE, 10, HOUR, 1, HOUR, 1, 0, HH_MM),
+            new TimeAxisSetting(60, MINUTE, 30, HOUR, 2, HOUR, 2, 0, HH_MM),
+            new TimeAxisSetting(180, HOUR, 1, HOUR, 6, HOUR, 6, 0, HH_MM),
+            new TimeAxisSetting(600, HOUR, 6, DAY, 1, DAY, 1, 24 * 3600, "EEE"),
+            new TimeAxisSetting(1800, HOUR, 12, DAY, 1, DAY, 2, 24 * 3600, "EEE"),
+            new TimeAxisSetting(3600, DAY, 1, WEEK, 1, WEEK, 1, 7 * 24 * 3600, "'Week 'w"),
+            new TimeAxisSetting(3 * 3600L, WEEK, 1, MONTH, 1, WEEK, 2, 7 * 24 * 3600, "'Week 'w"),
+            new TimeAxisSetting(6 * 3600L, MONTH, 1, MONTH, 1, MONTH, 1, 30 * 24 * 3600, "MMM"),
+            new TimeAxisSetting(48 * 3600L, MONTH, 1, MONTH, 3, MONTH, 3, 30 * 24 * 3600, "MMM"),
+            new TimeAxisSetting(10 * 24 * 3600L, YEAR, 1, YEAR, 1, YEAR, 1, 365 * 24 * 3600, "yy"),
+            new TimeAxisSetting(-1, MONTH, 0, MONTH, 0, MONTH, 0, 0, "")
+    };
+
+    private final ImageParameters im;
+    private final ImageWorker worker;
+    private final RrdGraphDef gdef;
+    private final Mapper mapper;
+    private TimeAxisSetting tickSetting;
+
+    private final double secPerPix;
+    private final Calendar calendar;
+
+    TimeAxis(RrdGraph rrdGraph) {
+        this(rrdGraph, rrdGraph.worker);
+    }
+
+    /**
+     * Used for test
+     */
+    TimeAxis(RrdGraph rrdGraph, ImageWorker worker) {
+        this.im = rrdGraph.im;
+        this.worker = worker;
+        this.gdef = rrdGraph.gdef;
+        this.mapper = rrdGraph.mapper;
+        this.secPerPix = (im.end - im.start) / (double) im.xsize;
+        this.calendar = Calendar.getInstance(gdef.tz, gdef.locale);
+        this.calendar.setFirstDayOfWeek(gdef.firstDayOfWeek);
+    }
+
+    boolean draw() {
+        chooseTickSettings();
+        // early return, avoid exceptions
+        if (tickSetting == null) {
+            return false;
+        }
+
+        drawMinor();
+        drawMajor();
+        drawLabels();
+
+        return true;
+    }
+
+    private void drawMinor() {
+        if (!gdef.noMinorGrid) {
+            adjustStartingTime(tickSetting.minorUnit, tickSetting.minorUnitCount);
+            Paint color = gdef.getColor(ElementsNames.grid);
+            int y0 = im.yorigin, y1 = y0 - im.ysize;
+            for (int status = getTimeShift(); status <= 0; status = getTimeShift()) {
+                if (status == 0) {
+                    long time = calendar.getTime().getTime() / 1000L;
+                    int x = mapper.xtr(time);
+                    worker.drawLine(x, y0 - 1, x, y0 + 1, color, gdef.tickStroke);
+                    worker.drawLine(x, y0, x, y1, color, gdef.gridStroke);
+                }
+                findNextTime(tickSetting.minorUnit, tickSetting.minorUnitCount);
+            }
+        }
+    }
+
+    private void drawMajor() {
+        adjustStartingTime(tickSetting.majorUnit, tickSetting.majorUnitCount);
+        Paint color = gdef.getColor(ElementsNames.mgrid);
+        int y0 = im.yorigin, y1 = y0 - im.ysize;
+        for (int status = getTimeShift(); status <= 0; status = getTimeShift()) {
+            if (status == 0) {
+                long time = calendar.getTime().getTime() / 1000L;
+                int x = mapper.xtr(time);
+                worker.drawLine(x, y0 - 2, x, y0 + 2, color, gdef.tickStroke);
+                worker.drawLine(x, y0, x, y1, color, gdef.gridStroke);
+            }
+            findNextTime(tickSetting.majorUnit, tickSetting.majorUnitCount);
+        }
+    }
+
+    private void drawLabels() {
+        Font font = gdef.getFont(FONTTAG_AXIS);
+        Paint color = gdef.getColor(ElementsNames.font);
+        adjustStartingTime(tickSetting.labelUnit, tickSetting.labelUnitCount);
+        int y = im.yorigin + (int) worker.getFontHeight(font) + 2;
+        for (int status = getTimeShift(); status <= 0; status = getTimeShift()) {
+            String label = tickSetting.format.format(calendar, gdef.locale);
+            long time = calendar.getTime().getTime() / 1000L;
+            int x1 = mapper.xtr(time);
+            int x2 = mapper.xtr(time + tickSetting.labelSpan);
+            int labelWidth = (int) worker.getStringWidth(label, font);
+            int x = x1 + (x2 - x1 - labelWidth) / 2;
+            if (x >= im.xorigin && x + labelWidth <= im.xorigin + im.xsize) {
+                worker.drawString(label, x, y, font, color);
+            }
+            findNextTime(tickSetting.labelUnit, tickSetting.labelUnitCount);
+        }
+    }
+
+    private void findNextTime(int timeUnit, int timeUnitCount) {
+        switch (timeUnit) {
+        case SECOND:
+            calendar.add(Calendar.SECOND, timeUnitCount);
+            break;
+        case MINUTE:
+            calendar.add(Calendar.MINUTE, timeUnitCount);
+            break;
+        case HOUR:
+            calendar.add(Calendar.HOUR_OF_DAY, timeUnitCount);
+            break;
+        case DAY:
+            calendar.add(Calendar.DAY_OF_MONTH, timeUnitCount);
+            break;
+        case WEEK:
+            calendar.add(Calendar.DAY_OF_MONTH, 7 * timeUnitCount);
+            break;
+        case MONTH:
+            calendar.add(Calendar.MONTH, timeUnitCount);
+            break;
+        case YEAR:
+            calendar.add(Calendar.YEAR, timeUnitCount);
+            break;
+        }
+    }
+
+    private int getTimeShift() {
+        long time = calendar.getTime().getTime() / 1000L;
+        return (time < im.start) ? -1 : (time > im.end) ? +1 : 0;
+    }
+
+    private void adjustStartingTime(int timeUnit, int timeUnitCount) {
+        calendar.setTime(new Date(im.start * 1000L));
+        switch (timeUnit) {
+        case SECOND:
+            calendar.add(Calendar.SECOND, -(calendar.get(Calendar.SECOND) % timeUnitCount));
+            break;
+        case MINUTE:
+            calendar.set(Calendar.SECOND, 0);
+            calendar.add(Calendar.MINUTE, -(calendar.get(Calendar.MINUTE) % timeUnitCount));
+            break;
+        case HOUR:
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.add(Calendar.HOUR_OF_DAY, -(calendar.get(Calendar.HOUR_OF_DAY) % timeUnitCount));
+            break;
+        case DAY:
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            break;
+        case WEEK:
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            int diffDays = calendar.get(Calendar.DAY_OF_WEEK) - calendar.getFirstDayOfWeek();
+            if (diffDays < 0) {
+                diffDays += 7;
+            }
+            calendar.add(Calendar.DAY_OF_MONTH, -diffDays);
+            break;
+        case MONTH:
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            calendar.set(Calendar.DAY_OF_MONTH, 1);
+            calendar.add(Calendar.MONTH, -(calendar.get(Calendar.MONTH) % timeUnitCount));
+            break;
+        case YEAR:
+            calendar.set(Calendar.SECOND, 0);
+            calendar.set(Calendar.MINUTE, 0);
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+            calendar.set(Calendar.DAY_OF_MONTH, 1);
+            calendar.set(Calendar.MONTH, 0);
+            calendar.add(Calendar.YEAR, -(calendar.get(Calendar.YEAR) % timeUnitCount));
+            break;
+        }
+    }
+
+    private void chooseTickSettings() {
+        if (gdef.timeAxisSetting != null) {
+            tickSetting = new TimeAxisSetting(gdef.timeAxisSetting);
+        }
+        else {
+            for (int i = 0; tickSettings[i].secPerPix >= 0 && secPerPix > tickSettings[i].secPerPix; i++) {
+                tickSetting = tickSettings[i];
+            }
+        }
+        if (gdef.timeLabelFormat != null) {
+            tickSetting = tickSetting.withLabelFormat(gdef.timeLabelFormat);
+        }
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/TimeAxisSetting.java b/apps/jrobin/java/src/org/rrd4j/graph/TimeAxisSetting.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fb1cc3f567349b2f18b588ed0a7d20f8c7b92f2
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/TimeAxisSetting.java
@@ -0,0 +1,57 @@
+package org.rrd4j.graph;
+
+class TimeAxisSetting {
+    final long secPerPix;
+    final int minorUnit, minorUnitCount, majorUnit, majorUnitCount;
+    final int labelUnit, labelUnitCount, labelSpan;
+    final TimeLabelFormat format;
+
+    TimeAxisSetting(long secPerPix, int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
+                    int labelUnit, int labelUnitCount, int labelSpan, TimeLabelFormat format) {
+        this.secPerPix = secPerPix;
+        this.minorUnit = minorUnit;
+        this.minorUnitCount = minorUnitCount;
+        this.majorUnit = majorUnit;
+        this.majorUnitCount = majorUnitCount;
+        this.labelUnit = labelUnit;
+        this.labelUnitCount = labelUnitCount;
+        this.labelSpan = labelSpan;
+        this.format = format;
+    }
+
+    TimeAxisSetting(TimeAxisSetting s) {
+        this.secPerPix = s.secPerPix;
+        this.minorUnit = s.minorUnit;
+        this.minorUnitCount = s.minorUnitCount;
+        this.majorUnit = s.majorUnit;
+        this.majorUnitCount = s.majorUnitCount;
+        this.labelUnit = s.labelUnit;
+        this.labelUnitCount = s.labelUnitCount;
+        this.labelSpan = s.labelSpan;
+        this.format = s.format;
+    }
+
+    TimeAxisSetting(int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
+                    int labelUnit, int labelUnitCount, int labelSpan, TimeLabelFormat format) {
+        this(0, minorUnit, minorUnitCount, majorUnit, majorUnitCount,
+                labelUnit, labelUnitCount, labelSpan, format);
+    }
+
+    TimeAxisSetting(long secPerPix, int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
+                    int labelUnit, int labelUnitCount, int labelSpan, String format) {
+        this(secPerPix, minorUnit, minorUnitCount, majorUnit, majorUnitCount,
+                labelUnit, labelUnitCount, labelSpan, new SimpleTimeLabelFormat(format));
+    }
+
+    TimeAxisSetting(int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
+                    int labelUnit, int labelUnitCount, int labelSpan, String format) {
+        this(0, minorUnit, minorUnitCount, majorUnit, majorUnitCount,
+                labelUnit, labelUnitCount, labelSpan, new SimpleTimeLabelFormat(format));
+    }
+
+    TimeAxisSetting withLabelFormat(TimeLabelFormat f) {
+        return new TimeAxisSetting(
+            secPerPix, minorUnit, minorUnitCount, majorUnit, majorUnitCount,
+            labelUnit, labelUnitCount, labelSpan, f);
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/TimeLabelFormat.java b/apps/jrobin/java/src/org/rrd4j/graph/TimeLabelFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f7a2d33e646eb490d3c242fe79a4624b08182e1
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/TimeLabelFormat.java
@@ -0,0 +1,19 @@
+package org.rrd4j.graph;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Simplified version of DateFormat for just defining how to map a timestamp into a label for
+ * presentation.
+ */
+public interface TimeLabelFormat {
+    /**
+     * Format a timestamp.
+     *
+     * @param calendar   calendar to use for the formatter
+     * @param locale     locale that will be used with {@code String.format}
+     * @return           formatted string for the timestamp
+     */
+    String format(Calendar calendar, Locale locale);
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/VDef.java b/apps/jrobin/java/src/org/rrd4j/graph/VDef.java
new file mode 100644
index 0000000000000000000000000000000000000000..6161020a9e98d5e84b046ae5a6bef1bc8451115e
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/VDef.java
@@ -0,0 +1,20 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.data.DataProcessor;
+import org.rrd4j.data.Variable;
+
+class VDef extends Source {
+    private final String defName;
+    private final Variable var;
+
+    VDef(String name, String defName, Variable var) {
+        super(name);
+        this.defName = defName;
+        this.var = var;
+    }
+
+    void requestData(DataProcessor dproc) {
+        dproc.addDatasource(name, defName, var);
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/VRule.java b/apps/jrobin/java/src/org/rrd4j/graph/VRule.java
new file mode 100644
index 0000000000000000000000000000000000000000..12f1d6f59cd350114d5729f7cc16fea623a32aa6
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/VRule.java
@@ -0,0 +1,17 @@
+package org.rrd4j.graph;
+
+import java.awt.BasicStroke;
+import java.awt.Paint;
+
+class VRule extends Rule {
+    final long timestamp;
+
+    VRule(long timestamp, Paint color, LegendText legend, BasicStroke stroke) {
+        super(color, legend, stroke);
+        this.timestamp = timestamp;
+    }
+
+    void setLegendVisibility(long minval, long maxval, boolean forceLegend) {
+        legend.enabled &= (forceLegend || (timestamp >= minval && timestamp <= maxval));
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/VSpan.java b/apps/jrobin/java/src/org/rrd4j/graph/VSpan.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e4b6339b2742f292ac4f235624bbfe317767acc
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/VSpan.java
@@ -0,0 +1,25 @@
+package org.rrd4j.graph;
+
+import java.awt.Paint;
+
+class VSpan extends Span {
+    final long start;
+    final long end;
+
+    VSpan(long start, long end, Paint color, LegendText legend) {
+        super(color, legend);
+        this.start = start;
+        this.end = end;
+        assert(start < end);
+    }
+
+    private boolean checkRange(long v, long min, long max) {
+        return v >= min && v <= max;
+    }
+
+    void setLegendVisibility(long min, long max, boolean forceLegend) {
+        legend.enabled = legend.enabled && (forceLegend
+                || checkRange(start, min, max)
+                || checkRange(end, min, max));
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ValueAxis.java b/apps/jrobin/java/src/org/rrd4j/graph/ValueAxis.java
new file mode 100644
index 0000000000000000000000000000000000000000..d97c3f932d8ac36af36d76853f55b06160c06962
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ValueAxis.java
@@ -0,0 +1,249 @@
+package org.rrd4j.graph;
+
+import java.awt.Font;
+import java.awt.Paint;
+
+import org.rrd4j.core.Util;
+
+class ValueAxis extends Axis {
+    private static final YLabel[] ylabels = {
+        new YLabel(0.1, 1, 2, 5, 10),
+        new YLabel(0.2, 1, 5, 10, 20),
+        new YLabel(0.5, 1, 2, 4, 10),
+        new YLabel(1.0, 1, 2, 5, 10),
+        new YLabel(2.0, 1, 5, 10, 20),
+        new YLabel(5.0, 1, 2, 4, 10),
+        new YLabel(10.0, 1, 2, 5, 10),
+        new YLabel(20.0, 1, 5, 10, 20),
+        new YLabel(50.0, 1, 2, 4, 10),
+        new YLabel(100.0, 1, 2, 5, 10),
+        new YLabel(200.0, 1, 5, 10, 20),
+        new YLabel(500.0, 1, 2, 4, 10),
+        new YLabel(1000.0, 1, 2, 5, 10),
+        new YLabel(2000.0, 1, 5, 10, 20),
+        new YLabel(5000.0, 1, 2, 4, 10),
+        new YLabel(10000.0, 1, 2, 5, 10),
+        new YLabel(20000.0, 1, 5, 10, 20),
+        new YLabel(50000.0, 1, 2, 4, 10),
+        new YLabel(100000.0, 1, 2, 5, 10),
+        new YLabel(0.0, 0, 0, 0, 0)
+    };
+
+    private final ImageParameters im;
+    private final ImageWorker worker;
+    private final RrdGraphDef gdef;
+    private final Mapper mapper;
+
+    ValueAxis(RrdGraph rrdGraph) {
+        this(rrdGraph, rrdGraph.worker);
+    }
+
+    ValueAxis(RrdGraph rrdGraph, ImageWorker worker) {
+        this.im = rrdGraph.im;
+        this.gdef = rrdGraph.gdef;
+        this.worker = worker;
+        this.mapper = rrdGraph.mapper;
+    }
+
+    boolean draw() {
+        Font font = gdef.getFont(FONTTAG_AXIS);
+        Paint gridColor = gdef.getColor(ElementsNames.grid);
+        Paint mGridColor = gdef.getColor(ElementsNames.mgrid);
+        Paint fontColor = gdef.getColor(ElementsNames.font);
+        int labelOffset = (int) (worker.getFontAscent(font) / 2);
+        int labfact;
+        double range = im.maxval - im.minval;
+        double scaledrange = range / im.magfact;
+        double gridstep;
+        if (Double.isNaN(scaledrange)) {
+            return false;
+        }
+        String labfmt = null;
+        if (Double.isNaN(im.ygridstep)) {
+            if (gdef.altYGrid) {
+                /* find the value with max number of digits. Get number of digits */
+                int decimals = (int) Math.ceil(Math.log10(Math.max(Math.abs(im.maxval),
+                        Math.abs(im.minval))));
+                if (decimals <= 0) /* everything is small. make place for zero */ {
+                    decimals = 1;
+                }
+                int fractionals = (int) Math.floor(Math.log10(range));
+                if (fractionals < 0) /* small amplitude. */ {
+                    labfmt = Util.sprintf(gdef.locale, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
+                }
+                else {
+                    labfmt = Util.sprintf(gdef.locale, "%%%d.1f", decimals + 1);
+                }
+                gridstep = Math.pow(10, fractionals);
+                if (gridstep == 0) /* range is one -> 0.1 is reasonable scale */ {
+                    gridstep = 0.1;
+                }
+                /* should have at least 5 lines but no more then 15 */
+                if (range / gridstep < 5) {
+                    gridstep /= 10;
+                }
+                if (range / gridstep > 15) {
+                    gridstep *= 10;
+                }
+                if (range / gridstep > 5) {
+                    labfact = 1;
+                    if (range / gridstep > 8) {
+                        labfact = 2;
+                    }
+                }
+                else {
+                    gridstep /= 5;
+                    labfact = 5;
+                }
+            }
+            else {
+                //Start looking for a minimum of 3 labels, but settle for 2 or 1 if need be
+                int minimumLabelCount = 3;
+                YLabel selectedYLabel = null;
+                while(selectedYLabel == null) {
+                    selectedYLabel = findYLabel(minimumLabelCount);
+                    minimumLabelCount--;
+                }
+                gridstep = selectedYLabel.grid * im.magfact;
+                labfact = findLabelFactor(selectedYLabel);
+            }
+        }
+        else {
+            gridstep = im.ygridstep;
+            labfact = im.ylabfact;
+        }
+        int x0 = im.xorigin, x1 = x0 + im.xsize;
+        int sgrid = (int) (im.minval / gridstep - 1);
+        int egrid = (int) (im.maxval / gridstep + 1);
+        double scaledstep = gridstep / im.magfact;
+        for (int i = sgrid; i <= egrid; i++) {
+            int y = mapper.ytr(gridstep * i);
+            if (y >= im.yorigin - im.ysize && y <= im.yorigin) {
+                if (i % labfact == 0) {
+                    String graph_label;
+                    if (i == 0 || im.symbol == ' ') {
+                        if (scaledstep < 1) {
+                            if (i != 0 && gdef.altYGrid) {
+                                graph_label = Util.sprintf(gdef.locale, labfmt, scaledstep * i);
+                            }
+                            else {
+                                graph_label = Util.sprintf(gdef.locale, "%4.1f", scaledstep * i);
+                            }
+                        }
+                        else {
+                            graph_label = Util.sprintf(gdef.locale, "%4.0f", scaledstep * i);
+                        }
+                    }
+                    else {
+                        if (scaledstep < 1) {
+                            graph_label = Util.sprintf(gdef.locale, "%4.1f %c", scaledstep * i, im.symbol);
+                        }
+                        else {
+                            graph_label = Util.sprintf(gdef.locale, "%4.0f %c", scaledstep * i, im.symbol);
+                        }
+                    }
+                    int length = (int) (worker.getStringWidth(graph_label, font));
+                    worker.drawString(graph_label, x0 - length - PADDING_VLABEL, y + labelOffset, font, fontColor);
+                    worker.drawLine(x0 - 2, y, x0 + 2, y, mGridColor, gdef.tickStroke);
+                    worker.drawLine(x1 - 2, y, x1 + 2, y, mGridColor, gdef.tickStroke);
+                    worker.drawLine(x0, y, x1, y, mGridColor, gdef.gridStroke);
+                }
+                else if (!(gdef.noMinorGrid)) {
+                    worker.drawLine(x0 - 1, y, x0 + 1, y, gridColor, gdef.tickStroke);
+                    worker.drawLine(x1 - 1, y, x1 + 1, y, gridColor, gdef.tickStroke);
+                    worker.drawLine(x0, y, x1, y, gridColor, gdef.gridStroke);
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Finds an acceptable YLabel object for the current graph
+     * If the graph covers positive and negative on the y-axis, then
+     * desiredMinimumLabelCount is checked as well, to ensure the chosen YLabel definition
+     * will result in the required number of labels
+     * 
+     * Returns null if none are acceptable (none the right size or with
+     * enough labels)
+     */
+    private YLabel findYLabel(int desiredMinimumLabelCount) {
+        double scaledrange = this.getScaledRange();
+        int labelFactor;
+        //Check each YLabel definition to see if it's acceptable
+        for (int i = 0; ylabels[i].grid > 0; i++) {
+            YLabel thisYLabel = ylabels[i];
+            //First cut is whether this gridstep would give enough space per gridline
+            if (this.getPixelsPerGridline(thisYLabel) > 5 ) {
+                //Yep; now we might have to check the number of labels
+                if(im.minval < 0.0 && im.maxval > 0.0) {
+                    //The graph covers positive and negative values, so we need the
+                    // desiredMinimumLabelCount number of labels, which is going to
+                    // usually be 3, then maybe 2, then only as a last resort, 1. 
+                    // So, we need to find out what the label factor would be
+                    // if we chose this ylab definition
+                    labelFactor = findLabelFactor(thisYLabel);
+                    if(labelFactor == -1) {
+                        //Default to too many to satisfy the label count test, unless we're looking for just 1	
+                        // in which case be sure to satisfy the label count test
+                        labelFactor = desiredMinimumLabelCount==1?1:desiredMinimumLabelCount+1; 
+                    }
+                    //Adding one?  Think fenceposts (need one more than just dividing length by space between)
+                    int labelCount = ((int)(scaledrange/thisYLabel.grid)/labelFactor)+1;
+                    if(labelCount > desiredMinimumLabelCount) {
+                        return thisYLabel; //Enough pixels, *and* enough labels
+                    }
+
+                } else {
+                    //Only positive or negative on the graph y-axis.  No need to
+                    // care about the label count.
+                    return thisYLabel;
+                }
+            }
+        }
+
+        double val = 1;
+        while(val < scaledrange) {
+            val = val * 10;
+        }
+        return new YLabel(val/10, 1, 2, 5, 10);
+    }
+
+    /**
+     * Find the smallest labelFactor acceptable (can fit labels) for the given YLab definition
+     * Returns the label factor if one is ok, otherwise returns -1 if none are acceptable
+     */
+    private int findLabelFactor(YLabel thisYLabel) {
+        int pixel = this.getPixelsPerGridline(thisYLabel);
+        int fontHeight = (int) Math.ceil(worker.getFontHeight(gdef.getFont(FONTTAG_AXIS)));
+        for (int j = 0; j < 4; j++) {
+            if (pixel * thisYLabel.labelFacts[j] >= 2 * fontHeight) {
+                return thisYLabel.labelFacts[j];
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Finds the number of pixels per gridline that the given YLabel definition will result in
+     */
+    private int getPixelsPerGridline(YLabel thisYLabel) {
+        double scaledrange = this.getScaledRange();
+        return (int) (im.ysize / (scaledrange / thisYLabel.grid));
+    }
+
+    private double getScaledRange() {
+        double range = im.maxval - im.minval;
+        return range / im.magfact;
+    }
+
+    static class YLabel {
+        final double grid;
+        final int[] labelFacts;
+
+        YLabel(double grid, int lfac1, int lfac2, int lfac3, int lfac4) {
+            this.grid = grid;
+            labelFacts = new int[]{lfac1, lfac2, lfac3, lfac4};
+        }
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisLogarithmic.java b/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisLogarithmic.java
new file mode 100644
index 0000000000000000000000000000000000000000..fca83a5ae1f035582e0a5f54ccdf5414a68e198f
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisLogarithmic.java
@@ -0,0 +1,195 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.core.Util;
+
+import java.awt.*;
+
+class ValueAxisLogarithmic extends Axis {
+    private static final double[][] yloglab = {
+        {1e9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        {1e3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        {1e1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        {1e1, 1, 2.5, 5, 7.5, 0, 0, 0, 0, 0, 0, 0},
+        {1e1, 1, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0},
+        {1e1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0},
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+    };
+
+    private final ImageParameters im;
+    private final ImageWorker worker;
+    private final RrdGraphDef gdef;
+    private final int fontHeight;
+    private final Mapper mapper;
+
+    ValueAxisLogarithmic(RrdGraph rrdGraph) {
+        this(rrdGraph, rrdGraph.worker);
+    }
+
+    ValueAxisLogarithmic(RrdGraph rrdGraph, ImageWorker worker) {
+        this.im = rrdGraph.im;
+        this.gdef = rrdGraph.gdef;
+        this.worker = worker;
+        this.fontHeight = (int) Math.ceil(worker.getFontHeight(gdef.getFont(FONTTAG_AXIS)));
+        this.mapper = rrdGraph.mapper;
+    }
+
+    boolean draw() {
+        Font font = gdef.getFont(FONTTAG_AXIS);
+        Paint gridColor = gdef.getColor(ElementsNames.grid);
+        Paint mGridColor = gdef.getColor(ElementsNames.mgrid);
+        Paint fontColor = gdef.getColor(ElementsNames.font);
+        int labelOffset = (int) (worker.getFontAscent(font) / 2);
+
+        if (im.maxval == im.minval) {
+            return false;
+        }
+        double pixpex = (double) im.ysize / (log10(im.maxval) - log10(im.minval));
+        if (Double.isNaN(pixpex)) {
+            return false;
+        }
+        double minstep, pixperstep;
+        int minoridx = 0, majoridx = 0;
+        for (int i = 0; yloglab[i][0] > 0; i++) {
+            minstep = log10(yloglab[i][0]);
+            for (int ii = 1; yloglab[i][ii + 1] > 0; ii++) {
+                if (yloglab[i][ii + 2] == 0) {
+                    minstep = log10(yloglab[i][ii + 1]) - log10(yloglab[i][ii]);
+                    break;
+                }
+            }
+            pixperstep = pixpex * minstep;
+            if (pixperstep > 5) {
+                minoridx = i;
+            }
+            if (pixperstep > 2 * fontHeight) {
+                majoridx = i;
+            }
+        }
+
+        // Draw minor grid for positive values
+        double positiveMin = (im.minval > 0.0) ? im.minval : 0.0;
+        int x0 = im.xorigin, x1 = x0 + im.xsize;
+        if (yloglab[minoridx][0] == 0 || yloglab[majoridx][0] == 0) {
+            return false;
+        }
+        for (double value = Math.pow(10, log10(positiveMin)
+                - log10(positiveMin) % log10(yloglab[minoridx][0]));
+                value <= im.maxval;
+                value *= yloglab[minoridx][0]) {
+            if (value < positiveMin) continue;
+            int i = 0;
+            while (yloglab[minoridx][++i] > 0) {
+                int y = mapper.ytr(value * yloglab[minoridx][i]);
+                if (y <= im.yorigin - im.ysize) {
+                    break;
+                }
+                worker.drawLine(x0 - 1, y, x0 + 1, y, gridColor, gdef.tickStroke);
+                worker.drawLine(x1 - 1, y, x1 + 1, y, gridColor, gdef.tickStroke);
+                worker.drawLine(x0, y, x1, y, gridColor, gdef.gridStroke);
+            }
+        }
+
+        // Draw minor grid for negative values
+        double negativeMin = -1.0 * ((im.maxval < 0.0) ? im.maxval : 0.0);
+        for (double value = Math.pow(10, log10(negativeMin)
+                - log10(negativeMin) % log10(yloglab[minoridx][0]));
+                value <= -1.0 * im.minval;
+                value *= yloglab[minoridx][0]) {
+            if (value < negativeMin) continue;
+            int i = 0;
+            while (yloglab[minoridx][++i] > 0) {
+                int y = mapper.ytr(-1.0 * value * yloglab[minoridx][i]);
+                if (y <= im.yorigin - im.ysize) {
+                    break;
+                }
+                worker.drawLine(x0 - 1, y, x0 + 1, y, gridColor, gdef.tickStroke);
+                worker.drawLine(x1 - 1, y, x1 + 1, y, gridColor, gdef.tickStroke);
+                worker.drawLine(x0, y, x1, y, gridColor, gdef.gridStroke);
+            }
+        }
+
+        // If it has positive and negative, always have a tick mark at 0
+        boolean skipFirst = false;
+        if (im.minval < 0.0 && im.maxval > 0.0) {
+            skipFirst = true;
+            int y = mapper.ytr(0.0);
+            worker.drawLine(x0 - 2, y, x0 + 2, y, mGridColor, gdef.tickStroke);
+            worker.drawLine(x1 - 2, y, x1 + 2, y, mGridColor, gdef.tickStroke);
+            worker.drawLine(x0, y, x1, y, mGridColor, gdef.gridStroke);
+            String graph_label = Util.sprintf(gdef.locale, "%3.0e", 0.0);
+            int length = (int) (worker.getStringWidth(graph_label, font));
+            worker.drawString(graph_label, x0 - length - PADDING_VLABEL, y + labelOffset, font, fontColor);
+        }
+
+        // Draw major grid for positive values
+        int iter = 0;
+        for (double value = Math.pow(10, log10(positiveMin)
+                - (log10(positiveMin) % log10(yloglab[majoridx][0])));
+                value <= im.maxval;
+                value *= yloglab[majoridx][0]) {
+            if (value < positiveMin) {
+                continue;
+            }
+            ++iter;
+            if (skipFirst && iter == 1) {
+                continue;
+            }
+            int i = 0;
+            while (yloglab[majoridx][++i] > 0) {
+                int y = mapper.ytr(value * yloglab[majoridx][i]);
+                if (y <= im.yorigin - im.ysize) {
+                    break;
+                }
+                worker.drawLine(x0 - 2, y, x0 + 2, y, mGridColor, gdef.tickStroke);
+                worker.drawLine(x1 - 2, y, x1 + 2, y, mGridColor, gdef.tickStroke);
+                worker.drawLine(x0, y, x1, y, mGridColor, gdef.gridStroke);
+                String graph_label = Util.sprintf(gdef.locale, "%3.0e", value * yloglab[majoridx][i]);
+                int length = (int) (worker.getStringWidth(graph_label, font));
+                worker.drawString(graph_label, x0 - length - PADDING_VLABEL, y + labelOffset, font, fontColor);
+            }
+        }
+
+        // Draw major grid for negative values
+        iter = 0;
+        for (double value = Math.pow(10, log10(negativeMin)
+                - (log10(negativeMin) % log10(yloglab[majoridx][0])));
+                value <= -1.0 * im.minval;
+                value *= yloglab[majoridx][0]) {
+            if (value < negativeMin) {
+                continue;
+            }
+            ++iter;
+            if (skipFirst && iter == 1) {
+                continue;
+            }
+            int i = 0;
+            while (yloglab[majoridx][++i] > 0) {
+                int y = mapper.ytr(-1.0 * value * yloglab[majoridx][i]);
+                if (y <= im.yorigin - im.ysize) {
+                    break;
+                }
+                worker.drawLine(x0 - 2, y, x0 + 2, y, mGridColor, gdef.tickStroke);
+                worker.drawLine(x1 - 2, y, x1 + 2, y, mGridColor, gdef.tickStroke);
+                worker.drawLine(x0, y, x1, y, mGridColor, gdef.gridStroke);
+                String graph_label = Util.sprintf(gdef.locale, "%3.0e", -1.0 * value * yloglab[majoridx][i]);
+                int length = (int) (worker.getStringWidth(graph_label, font));
+                worker.drawString(graph_label, x0 - length - PADDING_VLABEL, y + labelOffset, font, fontColor);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Compute logarithm for the purposes of y-axis. 
+     */
+    static double log10(double v) {
+        double lv = Math.log10(Math.abs(v));
+        if (lv < 0) {
+            // Don't cross the sign line, round to 0 if that's the case
+            return 0.0;
+        } else {
+            return Math.copySign(lv, v);
+        }
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisMrtg.java b/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisMrtg.java
new file mode 100644
index 0000000000000000000000000000000000000000..2da2f397bc93a78cde30dcc811f83dca606aa89f
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisMrtg.java
@@ -0,0 +1,66 @@
+package org.rrd4j.graph;
+
+import org.rrd4j.core.Util;
+
+import java.awt.*;
+
+class ValueAxisMrtg extends Axis {
+    
+    private final ImageParameters im;
+    private final ImageWorker worker;
+    private final RrdGraphDef gdef;
+
+    ValueAxisMrtg(RrdGraph rrdGraph) {
+        this(rrdGraph, rrdGraph.worker);
+    }
+
+    ValueAxisMrtg(RrdGraph rrdGraph, ImageWorker worker) {
+        this.im = rrdGraph.im;
+        this.gdef = rrdGraph.gdef;
+        this.worker = worker;
+        im.unit = gdef.unit;
+    }
+
+    boolean draw() {
+        Font font = gdef.getFont(FONTTAG_AXIS);
+        Paint mGridColor = gdef.getColor(ElementsNames.mgrid);
+        Paint fontColor = gdef.getColor(ElementsNames.font);
+        int labelOffset = (int) (worker.getFontAscent(font) / 2);
+
+        if (Double.isNaN((im.maxval - im.minval) / im.magfact)) {
+            return false;
+        }
+
+        int xLeft = im.xorigin;
+        int xRight = im.xorigin + im.xsize;
+        String labfmt;
+        if (im.scaledstep / im.magfact * Math.max(Math.abs(im.quadrant), Math.abs(4 - im.quadrant)) <= 1.0) {
+            labfmt = "%5.2f";
+        }
+        else {
+            labfmt = Util.sprintf(gdef.locale, "%%4.%df", 1 - ((im.scaledstep / im.magfact > 10.0 || Math.ceil(im.scaledstep / im.magfact) == im.scaledstep / im.magfact) ? 1 : 0));
+        }
+        if (im.symbol != ' ' || im.unit != null) {
+            labfmt += " ";
+        }
+        if (im.symbol != ' ') {
+            labfmt += Character.toString(im.symbol);
+        }
+        if (im.unit != null) {
+            labfmt += im.unit;
+        }
+        for (int i = 0; i <= 4; i++) {
+            int y = im.yorigin - im.ysize * i / 4;
+            if (y >= im.yorigin - im.ysize && y <= im.yorigin) {
+                String graph_label = Util.sprintf(gdef.locale, labfmt, im.scaledstep / im.magfact * (i - im.quadrant));
+                int length = (int) (worker.getStringWidth(graph_label, font));
+                worker.drawString(graph_label, xLeft - length - PADDING_VLABEL, y + labelOffset, font, fontColor);
+                worker.drawLine(xLeft - 2, y, xLeft + 2, y, mGridColor, gdef.tickStroke);
+                worker.drawLine(xRight - 2, y, xRight + 2, y, mGridColor, gdef.tickStroke);
+                worker.drawLine(xLeft, y, xRight, y, mGridColor, gdef.gridStroke);
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisSetting.java b/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisSetting.java
new file mode 100644
index 0000000000000000000000000000000000000000..53fdcef9f69babb18db970d7a0aa9046d6d866d7
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ValueAxisSetting.java
@@ -0,0 +1,11 @@
+package org.rrd4j.graph;
+
+class ValueAxisSetting {
+    final double gridStep;
+    final int labelFactor;
+
+    ValueAxisSetting(double gridStep, int labelFactor) {
+        this.gridStep = gridStep;
+        this.labelFactor = labelFactor;
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/ValueScaler.java b/apps/jrobin/java/src/org/rrd4j/graph/ValueScaler.java
new file mode 100644
index 0000000000000000000000000000000000000000..1068365d9d3e50b6b2e03cdada97bf0544b8c108
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/ValueScaler.java
@@ -0,0 +1,70 @@
+package org.rrd4j.graph;
+
+class ValueScaler {
+    static final String UNIT_UNKNOWN = "?";
+    static final String UNIT_SYMBOLS[] = {
+            "a", "f", "p", "n", "u", "m", " ", "k", "M", "G", "T", "P", "E"
+    };
+    static final int SYMB_CENTER = 6;
+
+    private final double base;
+    private double magfact = -1; // nothing scaled before, rescale
+    private String unit;
+
+    ValueScaler(double base) {
+        this.base = base;
+    }
+
+    Scaled scale(double value, boolean mustRescale) {
+        Scaled scaled;
+        if (mustRescale) {
+            scaled = rescale(value);
+        }
+        else if (magfact >= 0) {
+            // already scaled, need not rescale
+            scaled = new Scaled(value / magfact, unit);
+        }
+        else {
+            // scaling not requested, but never scaled before - must rescale anyway
+            scaled = rescale(value);
+            // if zero, scale again on the next try
+            if (scaled.value == 0.0 || Double.isNaN(scaled.value)) {
+                magfact = -1.0;
+            }
+        }
+        return scaled;
+    }
+
+    private Scaled rescale(double value) {
+        int sindex;
+        if (value == 0.0 || Double.isNaN(value)) {
+            sindex = 0;
+            magfact = 1.0;
+        }
+        else {
+            sindex = (int) (Math.floor(Math.log(Math.abs(value)) / Math.log(base)));
+            magfact = Math.pow(base, sindex);
+        }
+        if (sindex <= SYMB_CENTER && sindex >= -SYMB_CENTER) {
+            unit = UNIT_SYMBOLS[sindex + SYMB_CENTER];
+        }
+        else {
+            unit = UNIT_UNKNOWN;
+        }
+        return new Scaled(value / magfact, unit);
+    }
+
+    static class Scaled {
+        double value;
+        String unit;
+
+        public Scaled(double value, String unit) {
+            this.value = value;
+            this.unit = unit;
+        }
+
+        void dump() {
+            System.out.println("[" + value + unit + "]");
+        }
+    }
+}
diff --git a/apps/jrobin/java/src/org/rrd4j/graph/package-info.java b/apps/jrobin/java/src/org/rrd4j/graph/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..3174ae25713c696e4c1313a5ac64a90b7f497ee4
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/graph/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * RRD4J graph capabilities.
+ */
+package org.rrd4j.graph;
diff --git a/apps/jrobin/java/src/org/rrd4j/package-info.java b/apps/jrobin/java/src/org/rrd4j/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ed9a528458736059943fc0b08dd36e28ba86577
--- /dev/null
+++ b/apps/jrobin/java/src/org/rrd4j/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * RRD4J is a high performance data logging and graphing system for time series data, implementing
+ * RRDTool's functionality in Java. It follows much of the same logic and uses the same data sources,
+ * archive types and definitions as RRDTool does.
+ * <p>
+ * RRD4J supports all standard operations on Round Robin Database (RRD) files: CREATE, UPDATE, FETCH,
+ * LAST, DUMP, XPORT and GRAPH. RRD4J's API is made for those who are familiar with RRDTool's concepts
+ * and logic, but prefer to work with pure java. If you provide the same data to RRDTool and RRD4J,
+ * you will get very similar results and graphs.
+ * <p>
+ * RRD4J does not use native functions and libraries, has no Runtime.exec() calls and does not require
+ * RRDTool to be present. RRD4J is distributed as a software library (jar files) and comes with full
+ * java source code (Apache License 2.0).
+ * <p>
+ * You will not understand a single thing here if you are not already familiar with RRDTool. Basic
+ * concepts and terms (such as: datasource, archive, datasource type, consolidation functions, archive steps/rows,
+ * heartbeat, RRD step, RPN, graph DEFs and CDEFs) are not explained here because they have exactly the same
+ * meaning in RRD4J and RRDTool. If you are a novice RRDTool/RRD4J user,
+ * <a href="http://oss.oetiker.ch/rrdtool/tut/rrdtutorial.en.html">this annotated RRDTool tutorial</a> is a
+ * good place to start. A, <a href="https://github.com/rrd4j/rrd4j/wiki/Tutorial">adapted version</a> for RRD4J is available
+ */
+package org.rrd4j;
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryListener.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryListener.java
index 8e8619a498d8dada3400bf001a9c4af0cbeba42c..9b06a16bba6d1173a406d0cc0fcfb422badaa6de 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryListener.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryListener.java
@@ -13,14 +13,16 @@ import net.i2p.util.Log;
 import net.i2p.util.SecureFile;
 import net.i2p.util.SecureFileOutputStream;
 
-import org.jrobin.core.Archive;
-import org.jrobin.core.RrdBackendFactory;
-import org.jrobin.core.RrdDb;
-import org.jrobin.core.RrdDef;
-import org.jrobin.core.RrdException;
-import org.jrobin.core.RrdMemoryBackendFactory;
-import org.jrobin.core.RrdNioBackendFactory;
-import org.jrobin.core.Sample;
+import org.rrd4j.ConsolFun;
+import org.rrd4j.DsType;
+import org.rrd4j.core.Archive;
+import org.rrd4j.core.RrdBackendFactory;
+import org.rrd4j.core.RrdDb;
+import org.rrd4j.core.RrdDef;
+import org.rrd4j.core.RrdException;
+import org.rrd4j.core.RrdMemoryBackendFactory;
+import org.rrd4j.core.RrdNioBackendFactory;
+import org.rrd4j.core.Sample;
 
 /**
  *  Creates and updates the in-memory or on-disk RRD database,
@@ -35,7 +37,8 @@ public class SummaryListener implements RateSummaryListener {
     static final String RRD_DIR = "rrd";
     private static final String RRD_PREFIX = "rrd-";
     private static final String RRD_SUFFIX = ".jrb";
-    static final String CF = "AVERAGE";
+    static final ConsolFun CF = ConsolFun.AVERAGE;
+    static final DsType DS = DsType.GAUGE;
     private static final double XFF = 0.9d;
     private static final int STEPS = 1;
 
@@ -88,14 +91,14 @@ public class SummaryListener implements RateSummaryListener {
                 stopListening();
                 if (path != null)
                     (new File(path)).delete();
-            } catch (IOException ioe) {
-                _log.error("Error adding", ioe);
-                stopListening();
             } catch (RrdException re) {
                 // this can happen after the time slews backwards, so don't make it an error
                 // org.jrobin.core.RrdException: Bad sample timestamp 1264343107. Last update time was 1264343172, at least one second step is required
                 if (_log.shouldLog(Log.WARN))
                     _log.warn("Error adding", re);
+            } catch (IOException ioe) {
+                _log.error("Error adding", ioe);
+                stopListening();
             }
         }
     }
@@ -149,8 +152,8 @@ public class SummaryListener implements RateSummaryListener {
                 // for info on the heartbeat, xff, steps, etc, see the rrdcreate man page, aka
                 // http://www.jrobin.org/support/man/rrdcreate.html
                 long heartbeat = period*10/1000;
-                def.addDatasource(_name, "GAUGE", heartbeat, Double.NaN, Double.NaN);
-                def.addDatasource(_eventName, "GAUGE", heartbeat, 0, Double.NaN);
+                def.addDatasource(_name, DS, heartbeat, Double.NaN, Double.NaN);
+                def.addDatasource(_eventName, DS, heartbeat, 0, Double.NaN);
                 if (_isPersistent) {
                     _rows = (int) Math.max(MIN_ROWS, Math.min(MAX_ROWS, THREE_MONTHS / period));
                 } else {
@@ -187,6 +190,7 @@ public class SummaryListener implements RateSummaryListener {
                        "\nContact packager.";
             _log.log(Log.CRIT, s);
             System.out.println(s);
+            StatSummarizer.setDisabled(_context);
         } catch (Throwable t) {
             _log.error("Error starting RRD for stat " + baseName, t);
         }
@@ -203,9 +207,7 @@ public class SummaryListener implements RateSummaryListener {
         _rate.setSummaryListener(null);
         if (!_isPersistent) {
             // close() does not release resources for memory backend
-            try {
-                ((RrdMemoryBackendFactory)RrdBackendFactory.getFactory(RrdMemoryBackendFactory.NAME)).delete(_db.getPath());
-            } catch (RrdException re) {}
+            ((RrdMemoryBackendFactory)RrdBackendFactory.getFactory("MEMORY")).delete(_db.getPath());
         }
         _db = null;
     }
@@ -254,7 +256,7 @@ public class SummaryListener implements RateSummaryListener {
     
     /** @since 0.8.7 */
     String getBackendName() {
-        return _isPersistent ? RrdNioBackendFactory.NAME : RrdMemoryBackendFactory.NAME;
+        return _isPersistent ? "NIO" : "MEMORY";
     }
 
     /** @since 0.8.7 */
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java
index 02a53e2db666a5aafa1ed97e8d7e189222728b9c..fb25b133470ccbfd2c565f0860da515a21aeb4f2 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java
@@ -1,8 +1,10 @@
 package net.i2p.router.web;
 
+import java.awt.BasicStroke;
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.Graphics;
+import java.awt.Stroke;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -22,9 +24,11 @@ import static net.i2p.router.web.GraphConstants.*;
 import net.i2p.util.Log;
 import net.i2p.util.SystemVersion;
 
-import org.jrobin.core.RrdException;
-import org.jrobin.graph.RrdGraph;
-import org.jrobin.graph.RrdGraphDef;
+import org.rrd4j.ConsolFun;
+import org.rrd4j.core.RrdException;
+import org.rrd4j.graph.ElementsNames;
+import org.rrd4j.graph.RrdGraph;
+import org.rrd4j.graph.RrdGraphDef;
 
 /**
  *  Generate the RRD graph png images,
@@ -46,6 +50,8 @@ class SummaryRenderer {
     private static final Color AREA_COLOR = new Color(100, 160, 200, 200);
     private static final Color LINE_COLOR = new Color(0, 30, 110, 255);
     private static final Color RESTART_BAR_COLOR = new Color(223, 13, 13, 255);
+    // hide the arrow, full transparent
+    private static final Color ARROW_COLOR = new Color(0, 0, 0, 0);
     private static final boolean IS_WIN = SystemVersion.isWindows();
     private static final String DEFAULT_FONT_NAME = IS_WIN ?
             "Lucida Console" : "Monospaced";
@@ -58,6 +64,8 @@ class SummaryRenderer {
     private static final int SIZE_LEGEND = 10;
     private static final int SIZE_TITLE = 13;
     private static final long[] RATES = new long[] { 60*60*1000 };
+    // dotted line
+    private static final Stroke GRID_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, new float[] {1, 1}, 0);
 
     public SummaryRenderer(I2PAppContext ctx, SummaryListener lsnr) { 
         _log = ctx.logManager().getLog(SummaryRenderer.class);
@@ -78,35 +86,6 @@ class SummaryRenderer {
     @Deprecated
     public static synchronized void render(I2PAppContext ctx, OutputStream out, String filename) throws IOException {
         throw new UnsupportedOperationException();
-/*****
-        long end = ctx.clock().now() - 60*1000;
-        long start = end - 60*1000*SummaryListener.PERIODS;
-        try {
-            RrdGraphDefTemplate template = new RrdGraphDefTemplate(filename);
-            RrdGraphDef def = template.getRrdGraphDef();
-            def.setTimeSpan(start/1000, end/1000); // ignore the periods in the template
-            // FIXME not clear how to get the height and width from the template
-            int width = DEFAULT_X;
-            int height = DEFAULT_Y;
-            def.setWidth(width);
-            def.setHeight(height);
-
-            RrdGraph graph = new RrdGraph(def);
-            int totalWidth = graph.getRrdGraphInfo().getWidth();
-            int totalHeight = graph.getRrdGraphInfo().getHeight();
-            BufferedImage img = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_USHORT_565_RGB);
-            Graphics gfx = img.getGraphics();
-            graph.render(gfx);
-            ImageOutputStream ios = new MemoryCacheImageOutputStream(out);
-            ImageIO.write(img, "png", ios);
-        } catch (RrdException re) {
-            //_log.error("Error rendering " + filename, re);
-            throw new IOException("Error plotting: " + re.getLocalizedMessage());
-        } catch (IOException ioe) {
-            //_log.error("Error rendering " + filename, ioe);
-            throw ioe;
-        }
-*****/
     }
 
     public void render(OutputStream out) throws IOException { render(out, DEFAULT_X, DEFAULT_Y,
@@ -148,13 +127,14 @@ class SummaryRenderer {
             RrdGraphDef def = new RrdGraphDef();
 
             // Override defaults
-            def.setColor(RrdGraphDef.COLOR_BACK,   BACK_COLOR);
-            def.setColor(RrdGraphDef.COLOR_SHADEA, SHADEA_COLOR);
-            def.setColor(RrdGraphDef.COLOR_SHADEB, SHADEB_COLOR);
-            def.setColor(RrdGraphDef.COLOR_GRID,   GRID_COLOR);
-            def.setColor(RrdGraphDef.COLOR_MGRID,  MGRID_COLOR);
-            def.setColor(RrdGraphDef.COLOR_FONT,   FONT_COLOR);
-            def.setColor(RrdGraphDef.COLOR_FRAME,  FRAME_COLOR);
+            def.setColor(ElementsNames.back,   BACK_COLOR);
+            def.setColor(ElementsNames.shadea, SHADEA_COLOR);
+            def.setColor(ElementsNames.shadeb, SHADEB_COLOR);
+            def.setColor(ElementsNames.grid,   GRID_COLOR);
+            def.setColor(ElementsNames.mgrid,  MGRID_COLOR);
+            def.setColor(ElementsNames.font,   FONT_COLOR);
+            def.setColor(ElementsNames.frame,  FRAME_COLOR);
+            def.setColor(ElementsNames.arrow,  ARROW_COLOR);
 
             // improve text legibility
             String lang = Messages.getLanguage(_context);
@@ -176,6 +156,7 @@ class SummaryRenderer {
             def.setFont(RrdGraphDef.FONTTAG_DEFAULT, small);
             // AXIS is unused, we do not set any axis labels
             def.setFont(RrdGraphDef.FONTTAG_AXIS, small);
+            // rrd4j sets UNIT = AXIS in RrdGraphConstants, may be bug, maybe not, no use setting them different here
             def.setFont(RrdGraphDef.FONTTAG_UNIT, small);
             def.setFont(RrdGraphDef.FONTTAG_LEGEND, legnd);
             def.setFont(RrdGraphDef.FONTTAG_TITLE, large);
@@ -234,8 +215,8 @@ class SummaryRenderer {
             }
             if (!hideLegend) {
                 def.gprint(plotName, SummaryListener.CF, "   " + _t("Avg") + ": %.2f%s");
-                def.gprint(plotName, "MAX", ' ' + _t("Max") + ": %.2f%S");
-                def.gprint(plotName, "LAST", ' ' + _t("Now") + ": %.2f%S\\l");
+                def.gprint(plotName, ConsolFun.MAX, ' ' + _t("Max") + ": %.2f%S");
+                def.gprint(plotName, ConsolFun.LAST, ' ' + _t("Now") + ": %.2f%S\\l");
             }
             String plotName2 = null;
             if (lsnr2 != null) {
@@ -247,8 +228,8 @@ class SummaryRenderer {
                 def.line(plotName2, LINE_COLOR, descr2 + "\\l", 2);
                 if (!hideLegend) {
                     def.gprint(plotName2, SummaryListener.CF, "   " + _t("Avg") + ": %.2f%s");
-                    def.gprint(plotName2, "MAX", ' ' + _t("Max") + ": %.2f%S");
-                    def.gprint(plotName2, "LAST", ' ' + _t("Now") + ": %.2f%S\\l");
+                    def.gprint(plotName2, ConsolFun.MAX, ' ' + _t("Max") + ": %.2f%S");
+                    def.gprint(plotName2, ConsolFun.LAST, ' ' + _t("Now") + ": %.2f%S\\l");
                 }
             }
             if (!hideLegend) {
@@ -290,6 +271,8 @@ class SummaryRenderer {
             }
             //System.out.println("rendering: path=" + path + " dsNames[0]=" + dsNames[0] + " dsNames[1]=" + dsNames[1] + " lsnr.getName=" + _listener.getName());
             def.setAntiAliasing(false);
+            def.setTextAntiAliasing(true);
+            def.setGridStroke(GRID_STROKE);
             //System.out.println("Rendering: \n" + def.exportXmlTemplate());
             //System.out.println("*****************\nData: \n" + _listener.getData().dump());
             def.setWidth(width);
diff --git a/build.xml b/build.xml
index 1067069239872fbc2896f6d8810597212b8b0eb2..681040cb9afa9fd798b1733b9a2e536ac3fc8768 100644
--- a/build.xml
+++ b/build.xml
@@ -842,7 +842,7 @@
             <group title="Imagegen Application" packages="com.docuverse.identicon:com.google.zxing:com.google.zxing.*:net.i2p.imagegen" />
             <group title="Installer Utilities" packages="net.i2p.installer" />
             <group title="Jetty Utilities" packages="net.i2p.jetty:net.i2p.servlet:net.i2p.servlet.*" />
-            <group title="JRobin Library" packages="org.jrobin:org.jrobin.*:engine.misc" />
+            <group title="RRD4J Library (jrobin.jar)" packages="org.rrd4j:org.rrd4j.*:com.tomgibara.crinch.hashing" />
             <group title="SAM Bridge" packages="net.i2p.sam" />
             <group title="SAM Demos" packages="net.i2p.sam.client" />
             <group title="SusiDNS Application" packages="i2p.susi.dns:net.i2p.addressbook.servlet" />
diff --git a/debian-alt/doc/dependencies.txt b/debian-alt/doc/dependencies.txt
index 74c337634c711b46aea83a0986ea46d0eb00e4aa..d78842c966f9f243e0ce0e6bb8cb9dca1f9843f9 100644
--- a/debian-alt/doc/dependencies.txt
+++ b/debian-alt/doc/dependencies.txt
@@ -123,12 +123,10 @@ the binaries or sources.
   From https://github.com/PauloMigAlmeida/identicon
   No package or not widely available.
 
-* jrobin
+* rrd4j (jrobin.jar)
   This is the Java graphing package.
-  We bundle a large portion of the 1.6.0 source from https://github.com/OpenNMS/jrobin
+  We bundle most of the source from https://github.com/rrd4j/rrd4j
   No package or not widely available.
-  There is an old 1.5.9 Maven jrobin package at http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jrobin%22
-  Jrobin is in Gentoo.
 
 * libhttpclient-java
   We only use a few classes from this large package.