changeset 9123:1cc7653ca84f

Cleanup of ChartGenerator and ChartGenerator2 code. Put some of the copy/pasted code into a common abstraction.
author gernotbelger
date Tue, 05 Jun 2018 19:21:16 +0200
parents b8e7f6becf78
children 3f619f8dfa8e
files artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoLineProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FlowDepthDevelopmentProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/InfrastructureHeightProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedChannelDepthProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedChannelWidthProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedDepthEvolPerYearProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedDepthEvolProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedFlowDepthProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedTkhProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/TkhProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/AbstractChartGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/AnnotationRenderer.java artifacts/src/main/java/org/dive4elements/river/exports/AxisSection.java artifacts/src/main/java/org/dive4elements/river/exports/ChartExportHelper.java artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/ChartSection.java artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/DischargeGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/ExportSection.java artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/LegendSection.java artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator2.java artifacts/src/main/java/org/dive4elements/river/exports/NormalizedReferenceCurveGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/TypeSection.java artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/process/AnnotationProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/AreaProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/BedHeightProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityDensityProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityDiameterProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityPorosityProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/BedWidthProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/DeltaWProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/DischargeProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/FixDeltaWAProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/FixDeltaWProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/FixDerivedProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/FixWQProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/FlowVelocityProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/ManualPointsProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/MiddleBedHeightProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/MiscDischargeProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/SQRelationProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentDensityProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentLoadLSProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentLoadProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/ShearStressProcessor.java artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java
diffstat 63 files changed, 1934 insertions(+), 3431 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoLineProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoLineProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -31,6 +31,8 @@
 
 abstract class AbstractSInfoLineProcessor<RESULT extends AbstractSInfoCalculationResult> extends AbstractSInfoProcessor {
 
+    private static final double GAP_DISTANCE = 0.101;
+
     public AbstractSInfoLineProcessor(final String i18nAxisLabel, final Set<String> handledFacetType) {
         super(i18nAxisLabel, handledFacetType);
     }
@@ -38,7 +40,7 @@
     @Override
     protected final String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
         final Map<String, String> metaData = bundle.getFacet().getMetaData();
 
         final Artifact artifact = bundle.getArtifact();
@@ -57,7 +59,7 @@
 
         final double[][] points = generatePoints(context, artifact, data, facetName);
 
-        StyledSeriesBuilder.addPoints(series, points, true);
+        StyledSeriesBuilder.addPoints(series, points, true, GAP_DISTANCE);
         generator.addAxisSeries(series, getAxisName(), visible);
 
         return metaData.get("Y");
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FlowDepthDevelopmentProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FlowDepthDevelopmentProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -22,7 +22,7 @@
 
     private static final String I18N_AXIS_LABEL = "sinfo.chart.flow_depth_development.section.yaxis.label";
 
-    private static final String SINFO_CHART_FLOW_DEPTH_DEVELOPMENT_YAXIS_LABEL = "sinfo.chart.flow_depth.yaxis.label";
+    private static final String SINFO_CHART_FLOW_DEPTH_DEVELOPMENT_YAXIS_LABEL = "sinfo.chart.flow_depth_development.yaxis.label";
 
     /* Theme name, usually defined in 'FacetTypes', but that is soooo bad dependencies... */
     // REMARK: these must end with 'filtered' so extra handling happens in chart: point are always recalculated, because
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/InfrastructureHeightProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/InfrastructureHeightProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -56,7 +56,7 @@
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
         final Map<String, String> metaData = bundle.getFacet().getMetaData();
 
         final Artifact artifact = bundle.getArtifact();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedChannelDepthProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedChannelDepthProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -56,7 +56,7 @@
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
         final Map<String, String> metaData = bundle.getFacet().getMetaData();
 
         final Artifact artifact = bundle.getArtifact();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedChannelWidthProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedChannelWidthProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -56,7 +56,7 @@
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
         final Map<String, String> metaData = bundle.getFacet().getMetaData();
 
         final Artifact artifact = bundle.getArtifact();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedDepthEvolPerYearProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedDepthEvolPerYearProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -56,7 +56,7 @@
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
         final Map<String, String> metaData = bundle.getFacet().getMetaData();
 
         final Artifact artifact = bundle.getArtifact();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedDepthEvolProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedDepthEvolProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -56,7 +56,7 @@
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
         final Map<String, String> metaData = bundle.getFacet().getMetaData();
 
         final Artifact artifact = bundle.getArtifact();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedFlowDepthProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedFlowDepthProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -14,7 +14,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.log4j.Logger;
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.CallContext;
@@ -32,8 +31,6 @@
  */
 public class PredefinedFlowDepthProcessor extends AbstractSInfoProcessor {
 
-    private final static Logger log = Logger.getLogger(PredefinedFlowDepthProcessor.class);
-
     public static final String FACET_PREDEFINED_FLOW_DEPTH = "sinfo_facet_predefined_flowdepth";
 
     private static final String I18N_AXIS_LABEL = "sinfo.chart.flow_depth.section.yaxis.label";
@@ -51,7 +48,7 @@
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
         final Map<String, String> metaData = bundle.getFacet().getMetaData();
 
         final Artifact artifact = bundle.getArtifact();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedTkhProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/PredefinedTkhProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -13,7 +13,6 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import org.apache.log4j.Logger;
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.exports.DiagramGenerator;
@@ -30,8 +29,6 @@
  */
 public class PredefinedTkhProcessor extends AbstractSInfoProcessor {
 
-    private final static Logger log = Logger.getLogger(PredefinedTkhProcessor.class);
-
     public static final String FACET_PREDEFINED_TKH = "sinfo_facet_predefined_tkh";
 
     private static final String I18N_AXIS_LABEL = "sinfo.chart.tkh.section.yaxis.label";
@@ -48,7 +45,7 @@
 
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
 
         final String facetName = bundle.getFacetName();
         final AbstractTkhCalculationResult data = (AbstractTkhCalculationResult) bundle.getData(context);
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/TkhProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/TkhProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -46,7 +46,7 @@
 
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
-        final CallContext context = generator.getCallContext();
+        final CallContext context = generator.getContext();
 
         final String facetName = bundle.getFacetName();
         final AbstractTkhCalculationResult data = (AbstractTkhCalculationResult) bundle.getData(context);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/AbstractChartGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/AbstractChartGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -9,52 +9,499 @@
  */
 package org.dive4elements.river.exports;
 
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.awt.TexturePaint;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
 import javax.xml.xpath.XPathConstants;
 
+import org.apache.log4j.Logger;
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Settings;
+import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.ArtifactNamespaceContext;
 import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.PreferredLocale;
 import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.FLYS;
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.access.RangeAccess;
 import org.dive4elements.river.artifacts.access.RiverAccess;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
+import org.dive4elements.river.collections.D4EArtifactCollection;
+import org.dive4elements.river.jfree.AxisDataset;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer;
+import org.dive4elements.river.jfree.RiverAnnotation;
+import org.dive4elements.river.jfree.StableXYDifferenceRenderer;
+import org.dive4elements.river.jfree.Style;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.jfree.StyledSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+import org.dive4elements.river.utils.Formatter;
 import org.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
 import org.jfree.chart.title.TextTitle;
+import org.jfree.data.Range;
+import org.jfree.data.general.Series;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.ui.RectangleInsets;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 /**
+ * This class re-unites the tremendous copy/paste code from ChartGenerator and ChartGenerator2. Code is still awful and
+ * encapsulation is broken in too many places.
+ * TODO: instead of deep inheritances, delegate to classes that define the various behaviors.
+ *
  * @author Gernot Belger
  */
-// FIXME: this class is intended to contain all duplicate code from ChartGenerator and ChartGenerator2; who will clean
-// up this mess...?
 abstract class AbstractChartGenerator implements OutGenerator {
+
+    protected static final Logger log = Logger.getLogger(AbstractChartGenerator.class);
+
+    private static final int DEFAULT_CHART_WIDTH = 600;
+
+    private static final int DEFAULT_CHART_HEIGHT = 400;
+
+    private static final Color DEFAULT_GRID_COLOR = Color.GRAY;
+
+    private static final float DEFAULT_GRID_LINE_WIDTH = 0.3f;
+
+    protected static final String DEFAULT_FONT_NAME = "Tahoma";
+
+    protected static final int DEFAULT_FONT_SIZE = 12;
+
+    private static final String DEFAULT_CHART_FORMAT = "png";
+
     private static final String XPATH_CHART_EXPORT = "/art:action/art:attributes/art:export/@art:value";
 
-    // TODO: move real code here
-    protected abstract D4EArtifact getArtifact();
+    private static final String XPATH_CHART_SIZE = "/art:action/art:attributes/art:size";
+
+    private static final String XPATH_CHART_FORMAT = "/art:action/art:attributes/art:format/@art:value";
+
+    private static final String XPATH_CHART_X_RANGE = "/art:action/art:attributes/art:xrange";
+
+    private static final String XPATH_CHART_Y_RANGE = "/art:action/art:attributes/art:yrange";
+
+    /** The document of the incoming out() request. */
+    private Document request;
+
+    /** The output stream where the data should be written to. */
+    private OutputStream out;
+
+    /** Artifact that is used to decorate the chart with meta information. */
+    private Artifact master;
+
+    /** Map of datasets ("index"). */
+    private final SortedMap<Integer, AxisDataset> datasets = new TreeMap<>();
+
+    /** List of annotations to insert in plot. */
+    private final List<RiverAnnotation> annotations = new ArrayList<>();
+
+    private String outName;
+
+    /** The settings that should be used during output creation. */
+    private Settings settings;
 
     /** The CallContext object. */
-    // TODO: move real code here
-    protected abstract CallContext getContext();
+    private CallContext context;
+
+    @Override
+    public void init(final String outName, final Document request, final OutputStream out, final CallContext context) {
+        log.debug("ChartGenerator.init");
+
+        this.outName = outName;
+        this.request = request;
+        this.out = out;
+        this.context = context;
+    }
+
+    @Override
+    public void setup(final Object config) {
+    }
+
+    /** Sets the master artifact. */
+    @Override
+    public void setMasterArtifact(final Artifact master) {
+        this.master = master;
+    }
+
+    /**
+     * Gets the master artifact.
+     *
+     * @return the master artifact.
+     */
+    public Artifact getMaster() {
+        return this.master;
+    }
+
+    protected final Map<Integer, AxisDataset> getDatasets() {
+        return this.datasets;
+    }
+
+    @Override
+    public void setCollection(final D4EArtifactCollection collection) {
+        /* we do not need it */
+    }
+
+    protected final D4EArtifact getArtifact() {
+        // FIXME: should already made sure when this member is set
+        return (D4EArtifact) this.master;
+    }
+
+    public final CallContext getContext() {
+        return this.context;
+    }
 
     /** The document of the incoming out() request. */
-    // TODO: move real code here
-    protected abstract Document getRequest();
+    protected final Document getRequest() {
+        return this.request;
+    }
+
+    /**
+     * Adds annotations to list. The given annotation will be visible.
+     */
+    public final void addAnnotations(final RiverAnnotation annotation) {
+        this.annotations.add(annotation);
+    }
+
+    /**
+     * This method needs to be implemented by concrete subclasses to create new
+     * instances of JFreeChart.
+     *
+     * @param context2
+     *
+     * @return a new instance of a JFreeChart.
+     */
+    protected abstract JFreeChart generateChart(CallContext context2);
+
+    /**
+     * For every outable (i.e. facets), this function is
+     * called and handles the data accordingly.
+     */
+    @Override
+    public abstract void doOut(ArtifactAndFacet bundle, ThemeDocument attr, boolean visible);
+
+    @Override
+    public void generate() throws IOException {
+        doGenerate(this.context, this.out, this.outName);
+    }
+
+    protected abstract void doGenerate(CallContext context, OutputStream out, String outName) throws IOException;
+
+    protected abstract Series getSeriesOf(XYDataset dataset, int idx);
+
+    /**
+     * Returns the default title of a chart.
+     *
+     * @param context2
+     *
+     * @return the default title of a chart.
+     */
+    protected abstract String getDefaultChartTitle(CallContext context);
+
+    /**
+     * This method is used to create new AxisDataset instances which may differ
+     * in concrete subclasses.
+     *
+     * @param idx
+     *            The index of an axis.
+     */
+    protected abstract AxisDataset createAxisDataset(int idx);
+
+    /**
+     * Combines the ranges of the X axis at index <i>idx</i>.
+     *
+     * @param bounds
+     *            A new Bounds.
+     * @param idx
+     *            The index of the X axis that should be comined with
+     *            <i>range</i>.
+     */
+    protected abstract void combineXBounds(Bounds bounds, int idx);
+
+    /**
+     * Combines the ranges of the Y axis at index <i>idx</i>.
+     *
+     * @param bounds
+     *            A new Bounds.
+     * @param index
+     *            The index of the Y axis that should be comined with.
+     *            <i>range</i>.
+     */
+    protected abstract void combineYBounds(Bounds bounds, int index);
+
+    /**
+     * This method is used to determine the ranges for axes at a given index.
+     *
+     * @param index
+     *            The index of the axes at the plot.
+     *
+     * @return a Range[] with [xrange, yrange];
+     */
+    protected abstract Range[] getRangesForAxis(int index);
+
+    protected abstract Bounds getXBounds(int axis);
+
+    protected abstract void setXBounds(int axis, Bounds bounds);
+
+    protected abstract Bounds getYBounds(int axis);
+
+    protected abstract void setYBounds(int axis, Bounds bounds);
+
+    // /**
+    // * Retuns the call context. May be null if init hasn't been called yet.
+    // *
+    // * @return the CallContext instance
+    // */
+    // protected final CallContext getCallContext() {
+    // return this.context;
+    // }
+    //
+
+    @Override
+    public final void setSettings(final Settings settings) {
+        this.settings = settings;
+    }
+
+    /**
+     * Returns the <i>settings</i> as <i>ChartSettings</i>.
+     *
+     * @return the <i>settings</i> as <i>ChartSettings</i> or null, if
+     *         <i>settings</i> is not an instance of <i>ChartSettings</i>.
+     */
+    protected final ChartSettings getChartSettings() {
+        if (this.settings instanceof ChartSettings) {
+            return (ChartSettings) this.settings;
+        }
+
+        return null;
+    }
+
+    /**
+     * Return instance of <i>ChartSettings</i> with a chart specific section
+     * but with no axes settings.
+     *
+     * @return an instance of <i>ChartSettings</i>.
+     */
+    @Override
+    public final Settings getSettings() {
+        if (this.settings != null)
+            return this.settings;
+
+        final ChartSettings settings = new ChartSettings();
+
+        final ChartSection chartSection = buildChartSection(this.context);
+        final LegendSection legendSection = buildLegendSection();
+        final ExportSection exportSection = buildExportSection();
+
+        settings.setChartSection(chartSection);
+        settings.setLegendSection(legendSection);
+        settings.setExportSection(exportSection);
+
+        final List<AxisSection> axisSections = buildAxisSections();
+        for (final AxisSection axisSection : axisSections)
+            settings.addAxisSection(axisSection);
+
+        return settings;
+    }
+
+    protected abstract ChartSection buildChartSection(CallContext context);
+
+    /**
+     * Creates a new <i>LegendSection</i>.
+     *
+     * @return a new <i>LegendSection</i>.
+     */
+    private LegendSection buildLegendSection() {
+        final LegendSection legendSection = new LegendSection();
+        legendSection.setVisibility(isLegendVisible());
+        legendSection.setFontSize(getLegendFontSize());
+        legendSection.setAggregationThreshold(10);
+        return legendSection;
+    }
+
+    /**
+     * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b>
+     * and <b>HEIGHT=400</b>.
+     *
+     * @return a new <i>ExportSection</i>.
+     */
+    private ExportSection buildExportSection() {
+        final ExportSection exportSection = new ExportSection();
+        exportSection.setWidth(DEFAULT_CHART_WIDTH);
+        exportSection.setHeight(DEFAULT_CHART_HEIGHT);
+        exportSection.setMetadata(true);
+        return exportSection;
+    }
+
+    /**
+     * Creates a list of Sections that contains all axes of the chart (including
+     * X and Y axes).
+     *
+     * @return a list of Sections for each axis in this chart.
+     */
+    private List<AxisSection> buildAxisSections() {
+        final List<AxisSection> axisSections = new ArrayList<>();
+
+        axisSections.addAll(buildXAxisSections());
+        axisSections.addAll(buildYAxisSections());
+
+        return axisSections;
+    }
+
+    /**
+     * Creates a new Section for chart's X axis.
+     *
+     * @return a List that contains a Section for the X axis.
+     */
+    protected List<AxisSection> buildXAxisSections() {
+        final List<AxisSection> axisSections = new ArrayList<>();
+
+        final String identifier = "X";
+
+        final AxisSection axisSection = new AxisSection();
+        axisSection.setIdentifier(identifier);
+        axisSection.setLabel(getXAxisLabel());
+        axisSection.setFontSize(14);
+        axisSection.setFixed(false);
+
+        // XXX We are able to find better default ranges that [0,0], but the Y
+        // axes currently have no better ranges set.
+        axisSection.setUpperRange(0d);
+        axisSection.setLowerRange(0d);
+
+        axisSections.add(axisSection);
+
+        return axisSections;
+    }
+
+    /**
+     * Returns the X-Axis label of a chart.
+     *
+     * @return the X-Axis label of a chart.
+     */
+    protected final String getXAxisLabel() {
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return getDefaultXAxisLabel(this.context);
+        }
+
+        final AxisSection as = chartSettings.getAxisSection("X");
+        if (as != null) {
+            final String label = as.getLabel();
+
+            if (label != null) {
+                return label;
+            }
+        }
+
+        return getDefaultXAxisLabel(this.context);
+    }
+
+    protected abstract List<AxisSection> buildYAxisSections();
+
+    /**
+     * Returns the default X-Axis label of a chart.
+     *
+     * @param context2
+     *
+     * @return the default X-Axis label of a chart.
+     */
+    protected abstract String getDefaultXAxisLabel(final CallContext context2);
+
+    /** Generate the diagram as an image. */
+    protected final void generateImage(final CallContext context) throws IOException {
+        log.debug("ChartGenerator2.generateImage");
+
+        final JFreeChart chart = generateChart(context);
+
+        final String format = getFormat();
+        int[] size = getSize();
+
+        if (size == null)
+            size = getExportDimension();
+
+        this.context.putContextValue("chart.width", size[0]);
+        this.context.putContextValue("chart.height", size[1]);
+
+        if (format.equals(ChartExportHelper.FORMAT_PNG)) {
+            this.context.putContextValue("chart.image.format", "png");
+
+            ChartExportHelper.exportImage(this.out, chart, this.context);
+        } else if (format.equals(ChartExportHelper.FORMAT_PDF)) {
+            preparePDFContext(this.context);
+
+            ChartExportHelper.exportPDF(this.out, chart, this.context);
+        } else if (format.equals(ChartExportHelper.FORMAT_SVG)) {
+            prepareSVGContext(this.context);
+
+            ChartExportHelper.exportSVG(this.out, chart, this.context);
+        } else if (format.equals(ChartExportHelper.FORMAT_CSV)) {
+            this.context.putContextValue("chart.image.format", "csv");
+
+            ChartExportHelper.exportCSV(this.out, chart, this.context);
+        }
+    }
 
     /**
      * Adds a metadata sub-title to the chart if it gets exported
      */
-    protected final void addMetadataSubtitle(final JFreeChart chart) {
-        if (isExport()) {
-            final String text = ChartExportHelper.createMetadataSubtitle(getArtifact(), getContext(), getRiverName());
-            chart.addSubtitle(new TextTitle(text));
-        }
+    protected final void addMetadataSubtitle(final CallContext context, final JFreeChart chart) {
+        if ((!isExport() || !isExportMetadata()))
+            return;
+
+        final String version = FLYS.VERSION;
+        final String user = CalculationUtils.findArtifactUser(context, getArtifact());
+        final Locale locale = Resources.getLocale(context.getMeta());
+        final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+        final String dateText = df.format(new Date());
+
+        final String text = Resources.getMsg(context.getMeta(), "chart.subtitle.metadata", "default", version, user, dateText);
+
+        chart.addSubtitle(new TextTitle(text));
+    }
+
+    private boolean isExportMetadata() {
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null)
+            return true;
+
+        final ExportSection exportSection = chartSettings.getExportSection();
+        if (exportSection == null)
+            return true;
+
+        return exportSection.getMetadata();
     }
 
     /**
      * This method returns the export flag specified in the <i>request</i> document
      * or <i>false</i> if no export is specified in <i>request</i>.
      */
-    protected final boolean isExport() {
+    private boolean isExport() {
         final Boolean export = (Boolean) XMLUtils.xpath(getRequest(), XPATH_CHART_EXPORT, XPathConstants.BOOLEAN, ArtifactNamespaceContext.INSTANCE);
 
         return export == null ? false : export;
@@ -74,4 +521,790 @@
         final RangeAccess rangeAccess = new RangeAccess(flys);
         return rangeAccess.getKmRange();
     }
+
+    /**
+     * Returns a boolean object that determines if the chart grid should be
+     * visible or not. This information needs to be provided by <i>settings</i>,
+     * otherwise the default is true.
+     *
+     * @param settings
+     *            A ChartSettings object.
+     *
+     * @return true, if the chart grid should be visible otherwise false.
+     *
+     * @throws NullPointerException
+     *             if <i>settings</i> is null.
+     */
+    private boolean isGridVisible(final ChartSettings settings) {
+        final ChartSection cs = settings.getChartSection();
+        return cs.getDisplayGrid();
+    }
+
+    /**
+     * This method is used to determine, if the chart's legend is visible or
+     * not. If a <i>settings</i> instance is set, this instance determines the
+     * visibility otherwise, this method returns true as default if no
+     * <i>settings</i> is set.
+     *
+     * @return true, if the legend should be visible, otherwise false.
+     */
+    protected final boolean isLegendVisible() {
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null)
+            return true;
+
+        final LegendSection ls = chartSettings.getLegendSection();
+        return ls.getVisibility();
+    }
+
+    /**
+     * This method returns the font size for the X axis. If the font size is
+     * specified in ChartSettings (if <i>chartSettings</i> is set), this size is
+     * returned. Otherwise the default font size 12 is returned.
+     *
+     * @return the font size for the x axis.
+     */
+    protected final int getXAxisLabelFontSize() {
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return DEFAULT_FONT_SIZE;
+        }
+
+        final AxisSection as = chartSettings.getAxisSection("X");
+        final Integer fontSize = as.getFontSize();
+
+        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
+    }
+
+    /**
+     * This method is used to determine the font size of the chart's legend. If
+     * a <i>settings</i> instance is set, this instance determines the font
+     * size, otherwise this method returns 12 as default if no <i>settings</i>
+     * is set or if it doesn't provide a legend font size.
+     *
+     * @return a legend font size.
+     */
+    private int getLegendFontSize() {
+
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null)
+            return DEFAULT_FONT_SIZE;
+
+        final LegendSection ls = chartSettings.getLegendSection();
+        if (ls == null)
+            return DEFAULT_FONT_SIZE;
+
+        final Integer fontSize = ls.getFontSize();
+        if (fontSize == null)
+            return DEFAULT_FONT_SIZE;
+
+        return fontSize;
+    }
+
+    /**
+     * Creates a new LegendItem with <i>name</i> and font provided by
+     * <i>createLegendLabelFont()</i>.
+     *
+     * @param theme
+     *            The theme of the chart line.
+     * @param name
+     *            The displayed name of the item.
+     *
+     * @return a new LegendItem instance.
+     */
+    protected final LegendItem createLegendItem(final ThemeDocument theme, final String name) {
+        // OPTIMIZE Pass font, parsed Theme items.
+
+        Color color = theme.parseLineColorField();
+        if (color == null)
+            color = Color.BLACK;
+
+        final LegendItem legendItem = new LegendItem(name, color);
+        legendItem.setLabelFont(createLegendLabelFont());
+        return legendItem;
+    }
+
+    /**
+     * Create new legend entries, dependent on settings.
+     *
+     * @param plot
+     *            The plot for which to modify the legend.
+     */
+    protected final void aggregateLegendEntries(final XYPlot plot) {
+
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null)
+            return;
+
+        final Integer threshold = chartSettings.getLegendSection().getAggregationThreshold();
+
+        final int aggrThreshold = threshold != null ? threshold.intValue() : 0;
+
+        LegendProcessor.aggregateLegendEntries(plot, aggrThreshold);
+    }
+
+    /**
+     * Creates Font (Family and size) to use when creating Legend Items. The
+     * font size depends in the return value of <i>getLegendFontSize()</i>.
+     *
+     * @return a new Font instance with <i>DEFAULT_FONT_NAME</i>.
+     */
+    private final Font createLegendLabelFont() {
+        return new Font(DEFAULT_FONT_NAME, Font.PLAIN, getLegendFontSize());
+    }
+
+    /**
+     * Adjust some Stroke/Grid parameters for <i>plot</i>. The chart
+     * <i>Settings</i> are applied in this method.
+     *
+     * @param plot
+     *            The XYPlot which is adapted.
+     */
+    protected void adjustPlot(final XYPlot plot) {
+        final Stroke gridStroke = new BasicStroke(DEFAULT_GRID_LINE_WIDTH, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 3.0f, new float[] { 3.0f }, 0.0f);
+
+        final ChartSettings cs = getChartSettings();
+        final boolean isGridVisible = cs != null ? isGridVisible(cs) : true;
+
+        plot.setDomainGridlineStroke(gridStroke);
+        plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR);
+        plot.setDomainGridlinesVisible(isGridVisible);
+
+        plot.setRangeGridlineStroke(gridStroke);
+        plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR);
+        plot.setRangeGridlinesVisible(isGridVisible);
+
+        plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d));
+    }
+
+    /**
+     * This helper method is used to extract the current locale from instance variable <i>context</i>.
+     *
+     * @return the current locale.
+     */
+    protected final Locale getLocale() {
+        final CallMeta meta = this.context.getMeta();
+        final PreferredLocale[] prefs = meta.getLanguages();
+
+        final int len = prefs != null ? prefs.length : 0;
+
+        final Locale[] locales = new Locale[len];
+
+        for (int i = 0; i < len; i++) {
+            locales[i] = prefs[i].getLocale();
+        }
+
+        return meta.getPreferredLocale(locales);
+    }
+
+    /**
+     * Look up \param key in i18n dictionary.
+     *
+     * @param key
+     *            key for which to find i18nd version.
+     * @param def
+     *            default, returned if lookup failed.
+     * @return value found in i18n dictionary, \param def if no value found.
+     */
+    public final String msg(final String key, final String def) {
+        return Resources.getMsg(this.context.getMeta(), key, def);
+    }
+
+    /**
+     * Look up \param key in i18n dictionary.
+     *
+     * @param key
+     *            key for which to find i18nd version.
+     * @return value found in i18n dictionary, key itself if failed.
+     */
+    public final String msg(final String key) {
+        return Resources.getMsg(this.context.getMeta(), key, key);
+    }
+
+    public final String msg(final String key, final String def, final Object[] args) {
+        return Resources.getMsg(this.context.getMeta(), key, def, args);
+    }
+
+    /**
+     * Add datasets stored in instance variable <i>datasets</i> to plot.
+     * <i>datasets</i> actually stores instances of AxisDataset, so each of this
+     * datasets is mapped to a specific axis as well.
+     *
+     * @param plot
+     *            plot to add datasets to.
+     */
+    protected void addDatasets(final XYPlot plot) {
+        log.debug("addDatasets()");
+
+        // AxisDatasets are sorted, but some might be empty.
+        // Thus, generate numbering on the fly.
+        int axisIndex = 0;
+        int datasetIndex = 0;
+
+        for (final Map.Entry<Integer, AxisDataset> entry : this.datasets.entrySet()) {
+            if (!entry.getValue().isEmpty()) {
+                // Add axis and range information.
+                final AxisDataset axisDataset = entry.getValue();
+                final NumberAxis axis = createYAxis(entry.getKey());
+
+                plot.setRangeAxis(axisIndex, axis);
+
+                if (axis.getAutoRangeIncludesZero()) {
+                    axisDataset.setRange(Range.expandToInclude(axisDataset.getRange(), 0d));
+                }
+
+                setYBounds(axisIndex, expandPointRange(axisDataset.getRange()));
+
+                // Add contained datasets, mapping to axis.
+                for (final XYDataset dataset : axisDataset.getDatasets()) {
+                    try {
+                        plot.setDataset(datasetIndex, dataset);
+                        plot.mapDatasetToRangeAxis(datasetIndex, axisIndex);
+
+                        applyThemes(plot, dataset, datasetIndex, axisDataset.isArea(dataset));
+
+                        datasetIndex++;
+                    }
+                    catch (final RuntimeException re) {
+                        log.error(re);
+                    }
+                }
+
+                axisDataset.setPlotAxisIndex(axisIndex);
+                axisIndex++;
+            }
+        }
+    }
+
+    /**
+     * Create Y (range) axis for given index.
+     * Shall be implemented by subclasses.
+     */
+    protected abstract NumberAxis createYAxis(final int index);
+
+    /**
+     * @param idx
+     *            "index" of dataset/series (first dataset to be drawn has
+     *            index 0), correlates with renderer index.
+     * @param isArea
+     *            true if the series describes an area and shall be rendered
+     *            as such.
+     */
+    private void applyThemes(final XYPlot plot, final XYDataset series, final int idx, final boolean isArea) {
+        if (isArea) {
+            applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx);
+        } else {
+            applyLineTheme(plot, series, idx);
+        }
+    }
+
+    /**
+     * Expands a given range if it collapses into one point.
+     *
+     * @param range
+     *            Range to be expanded if upper == lower bound.
+     *
+     * @return Bounds of point plus 5 percent in each direction.
+     */
+    private Bounds expandPointRange(final Range range) {
+        if (range == null) {
+            return null;
+        } else if (range.getLowerBound() == range.getUpperBound()) {
+            final Range expandedRange = ChartHelper.expandRange(range, 5d);
+            return new DoubleBounds(expandedRange.getLowerBound(), expandedRange.getUpperBound());
+        }
+
+        return new DoubleBounds(range.getLowerBound(), range.getUpperBound());
+    }
+
+    /**
+     * Creates a new instance of EnhancedLineAndShapeRenderer.
+     *
+     * @param plot
+     *            The plot which is set for the new renderer.
+     * @param idx
+     *            This value is not used in the current implementation.
+     *
+     * @return a new instance of EnhancedLineAndShapeRenderer.
+     */
+    private XYLineAndShapeRenderer createRenderer(final XYPlot plot, final int idx) {
+        log.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx);
+
+        final EnhancedLineAndShapeRenderer r = new EnhancedLineAndShapeRenderer(true, false);
+
+        r.setPlot(plot);
+
+        return r;
+    }
+
+    /**
+     * This method applies the themes defined in the series itself. Therefore,
+     * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer
+     * for the series.
+     *
+     * @param plot
+     *            The plot.
+     * @param dataset
+     *            The XYDataset which needs to support Series objects.
+     * @param idx
+     *            The index of the renderer / dataset.
+     */
+    private void applyLineTheme(final XYPlot plot, final XYDataset dataset, final int idx) {
+        log.debug("Apply LineTheme for dataset at index: " + idx);
+
+        final LegendItemCollection lic = new LegendItemCollection();
+        final LegendItemCollection anno = plot.getFixedLegendItems();
+
+        final Font legendFont = createLegendLabelFont();
+
+        final XYLineAndShapeRenderer renderer = createRenderer(plot, idx);
+
+        for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) {
+            final Series series = getSeriesOf(dataset, s);
+
+            if (series instanceof StyledSeries) {
+                final Style style = ((StyledSeries) series).getStyle();
+                style.applyTheme(renderer, s);
+            }
+
+            // special case: if there is just one single item, we need to enable
+            // points for this series, otherwise we would not see anything in
+            // the chart area.
+            if (series.getItemCount() == 1) {
+                renderer.setSeriesShapesVisible(s, true);
+            }
+
+            LegendItem legendItem = renderer.getLegendItem(idx, s);
+            if (legendItem.getLabel().endsWith(" ") || legendItem.getLabel().endsWith("interpol")) {
+                legendItem = null;
+            }
+
+            if (legendItem != null) {
+                legendItem.setLabelFont(legendFont);
+                lic.add(legendItem);
+            } else {
+                log.warn("Could not get LegentItem for renderer: " + idx + ", series-idx " + s);
+            }
+        }
+
+        if (anno != null) {
+            lic.addAll(anno);
+        }
+
+        plot.setFixedLegendItems(lic);
+
+        plot.setRenderer(idx, renderer);
+    }
+
+    /**
+     * @param plot
+     *            The plot.
+     * @param area
+     *            A StyledAreaSeriesCollection object.
+     * @param idx
+     *            The index of the dataset.
+     */
+    private final void applyAreaTheme(final XYPlot plot, final StyledAreaSeriesCollection area, final int idx) {
+        final LegendItemCollection lic = new LegendItemCollection();
+        final LegendItemCollection anno = plot.getFixedLegendItems();
+
+        final Font legendFont = createLegendLabelFont();
+
+        log.debug("Registering an 'area'renderer at idx: " + idx);
+
+        final StableXYDifferenceRenderer dRenderer = new StableXYDifferenceRenderer();
+
+        if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) {
+            dRenderer.setPositivePaint(createTransparentPaint());
+        }
+
+        plot.setRenderer(idx, dRenderer);
+
+        area.applyTheme(dRenderer);
+
+        // i18n
+        dRenderer.setAreaLabelNumberFormat(Formatter.getFormatter(this.context.getMeta(), 2, 4));
+
+        dRenderer.setAreaLabelTemplate(Resources.getMsg(this.context.getMeta(), "area.label.template", "Area=%sm2"));
+
+        final LegendItem legendItem = dRenderer.getLegendItem(idx, 0);
+        if (legendItem != null) {
+            legendItem.setLabelFont(legendFont);
+            lic.add(legendItem);
+        } else {
+            log.warn("Could not get LegentItem for renderer: " + idx + ", series-idx " + 0);
+        }
+
+        if (anno != null) {
+            lic.addAll(anno);
+        }
+
+        plot.setFixedLegendItems(lic);
+    }
+
+    /**
+     * Returns a transparently textured paint.
+     *
+     * @return a transparently textured paint.
+     */
+    private static Paint createTransparentPaint() {
+        // TODO why not use a transparent color?
+        final BufferedImage texture = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);
+
+        return new TexturePaint(texture, new Rectangle2D.Double(0d, 0d, 0d, 0d));
+    }
+
+    private void preparePDFContext(final CallContext context) {
+        final int[] dimension = getExportDimension();
+
+        context.putContextValue("chart.width", dimension[0]);
+        context.putContextValue("chart.height", dimension[1]);
+        context.putContextValue("chart.marginLeft", 5f);
+        context.putContextValue("chart.marginRight", 5f);
+        context.putContextValue("chart.marginTop", 5f);
+        context.putContextValue("chart.marginBottom", 5f);
+        context.putContextValue("chart.page.format", ChartExportHelper.DEFAULT_PAGE_SIZE);
+    }
+
+    private void prepareSVGContext(final CallContext context) {
+        final int[] dimension = getExportDimension();
+
+        context.putContextValue("chart.width", dimension[0]);
+        context.putContextValue("chart.height", dimension[1]);
+        context.putContextValue("chart.encoding", ChartExportHelper.DEFAULT_ENCODING);
+    }
+
+    /**
+     * This method retrieves the chart subtitle by calling getChartSubtitle()
+     * and adds it as TextTitle to the chart.
+     * The default implementation of getChartSubtitle() returns the same
+     * as getDefaultChartSubtitle() which must be implemented by derived
+     * classes. If you want to add multiple subtitles to the chart override
+     * this method and add your subtitles manually.
+     *
+     * @param chart
+     *            The JFreeChart chart object.
+     */
+    protected void addSubtitles(final CallContext context, final JFreeChart chart) {
+        final String subtitle = getChartSubtitle(this.context);
+
+        if (subtitle != null && subtitle.length() > 0) {
+            chart.addSubtitle(new TextTitle(subtitle));
+        }
+    }
+
+    protected abstract String getChartSubtitle(CallContext context);
+
+    /**
+     * Adds a new AxisDataset which contains <i>dataset</i> at index <i>idx</i>.
+     *
+     * @param dataset
+     *            An XYDataset.
+     * @param idx
+     *            The axis index.
+     * @param visible
+     *            Determines, if the dataset should be visible or not.
+     */
+    protected final void addAxisDataset(final XYDataset dataset, final int idx, final boolean visible) {
+        if (dataset == null || idx < 0) {
+            return;
+        }
+
+        final AxisDataset axisDataset = getAxisDataset(idx);
+
+        final Bounds[] xyBounds = ChartHelper.getBounds(dataset);
+
+        if (xyBounds == null) {
+            log.warn("Skip XYDataset for Axis (invalid ranges): " + idx);
+            return;
+        }
+
+        if (visible) {
+            if (log.isDebugEnabled()) {
+                log.debug("Add new AxisDataset at index: " + idx);
+                log.debug("X extent: " + xyBounds[0]);
+                log.debug("Y extent: " + xyBounds[1]);
+            }
+
+            axisDataset.addDataset(dataset);
+        }
+
+        combineXBounds(xyBounds[0], 0);
+        combineYBounds(xyBounds[1], idx);
+    }
+
+    /**
+     * This method grants access to the AxisDatasets stored in <i>datasets</i>.
+     * If no AxisDataset exists for index <i>idx</i>, a new AxisDataset is
+     * created using <i>createAxisDataset()</i>.
+     *
+     * @param idx
+     *            The index of the desired AxisDataset.
+     *
+     * @return an existing or new AxisDataset.
+     */
+    protected final AxisDataset getAxisDataset(final int idx) {
+        AxisDataset axisDataset = this.datasets.get(idx);
+
+        if (axisDataset == null) {
+            axisDataset = createAxisDataset(idx);
+            this.datasets.put(idx, axisDataset);
+        }
+
+        return axisDataset;
+    }
+
+    /**
+     * Returns the size of a chart export as array which has been specified by
+     * the incoming request document.
+     *
+     * @return the size of a chart as [width, height] or null if no width or
+     *         height are given in the request document.
+     */
+    protected final int[] getSize() {
+        final int[] size = new int[2];
+
+        final Element sizeEl = (Element) XMLUtils.xpath(this.request, XPATH_CHART_SIZE, XPathConstants.NODE, ArtifactNamespaceContext.INSTANCE);
+
+        if (sizeEl != null) {
+            final String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+            final String w = sizeEl.getAttributeNS(uri, "width");
+            final String h = sizeEl.getAttributeNS(uri, "height");
+
+            if (w.length() > 0 && h.length() > 0) {
+                try {
+                    size[0] = Integer.parseInt(w);
+                    size[1] = Integer.parseInt(h);
+                }
+                catch (final NumberFormatException nfe) {
+                    log.warn("Wrong values for chart width/height.");
+                }
+            }
+        }
+
+        return size[0] > 0 && size[1] > 0 ? size : null;
+    }
+
+    /**
+     * This method returns the format specified in the <i>request</i> document
+     * or <i>DEFAULT_CHART_FORMAT</i> if no format is specified in
+     * <i>request</i>.
+     *
+     * @return the format used to export this chart.
+     */
+    private String getFormat() {
+        final String format = (String) XMLUtils.xpath(this.request, XPATH_CHART_FORMAT, XPathConstants.STRING, ArtifactNamespaceContext.INSTANCE);
+
+        return format == null || format.length() == 0 ? DEFAULT_CHART_FORMAT : format;
+    }
+
+    /**
+     * Returns the X-Axis range as String array from request document.
+     * If the (x|y)range elements are not found in request document, return
+     * null (i.e. not zoomed).
+     *
+     * @return a String array with [lower, upper], null if not in document.
+     */
+    protected final String[] getDomainAxisRangeFromRequest() {
+        final Element xrange = (Element) XMLUtils.xpath(this.request, XPATH_CHART_X_RANGE, XPathConstants.NODE, ArtifactNamespaceContext.INSTANCE);
+
+        if (xrange == null) {
+            return null;
+        }
+
+        final String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        final String lower = xrange.getAttributeNS(uri, "from");
+        final String upper = xrange.getAttributeNS(uri, "to");
+
+        return new String[] { lower, upper };
+    }
+
+    /**
+     * Returns null if the (x|y)range-element was not found in
+     * request document.
+     * This usally means that the axis are not manually zoomed, i.e. showing
+     * full data extent.
+     */
+    protected final String[] getValueAxisRangeFromRequest() {
+        final Element yrange = (Element) XMLUtils.xpath(this.request, XPATH_CHART_Y_RANGE, XPathConstants.NODE, ArtifactNamespaceContext.INSTANCE);
+
+        if (yrange == null) {
+            return null;
+        }
+
+        final String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        final String lower = yrange.getAttributeNS(uri, "from");
+        final String upper = yrange.getAttributeNS(uri, "to");
+
+        return new String[] { lower, upper };
+    }
+
+    /**
+     * Returns the default size of a chart export as array.
+     *
+     * @return the default size of a chart as [width, height].
+     */
+    protected final int[] getDefaultSize() {
+        return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT };
+    }
+
+    /**
+     * This method returns the export dimension specified in ChartSettings as
+     * int array [width,height].
+     *
+     * @return an int array with [width,height].
+     */
+    private int[] getExportDimension() {
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null)
+            return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT };
+
+        final ExportSection export = chartSettings.getExportSection();
+        final Integer width = export.getWidth();
+        final Integer height = export.getHeight();
+
+        if (width != null && height != null) {
+            return new int[] { width, height };
+        }
+
+        return new int[] { 600, 400 };
+    }
+
+    /**
+     * Returns the chart title provided by <i>settings</i>.
+     *
+     * @param settings
+     *            A ChartSettings object.
+     *
+     * @return the title provided by <i>settings</i> or null if no
+     *         <i>ChartSection</i> is provided by <i>settings</i>.
+     *
+     * @throws NullPointerException
+     *             if <i>settings</i> is null.
+     */
+    private String getChartTitle(final ChartSettings settings) {
+        final ChartSection cs = settings.getChartSection();
+        return cs != null ? cs.getTitle() : null;
+    }
+
+    /**
+     * Returns the chart subtitle provided by <i>settings</i>.
+     *
+     * @param settings
+     *            A ChartSettings object.
+     *
+     * @return the subtitle provided by <i>settings</i> or null if no
+     *         <i>ChartSection</i> is provided by <i>settings</i>.
+     *
+     * @throws NullPointerException
+     *             if <i>settings</i> is null.
+     */
+    protected final String getChartSubtitle(final ChartSettings settings) {
+        final ChartSection cs = settings.getChartSection();
+        return cs != null ? cs.getSubtitle() : null;
+    }
+
+    /**
+     * Returns the title of a chart. The return value depends on the existence
+     * of ChartSettings: if there are ChartSettings set, this method returns the
+     * chart title provided by those settings. Otherwise, this method returns
+     * getDefaultChartTitle().
+     *
+     * @return the title of a chart.
+     */
+    protected String getChartTitle(final CallContext context) {
+        final ChartSettings chartSettings = getChartSettings();
+
+        if (chartSettings != null) {
+            return getChartTitle(chartSettings);
+        }
+
+        return getDefaultChartTitle(context);
+    }
+
+    /**
+     * This method always returns null. Override it in subclasses that require
+     * subtitles.
+     *
+     * @return null.
+     */
+    protected String getDefaultChartSubtitle(final CallContext context) {
+        // Override this method in subclasses
+        return null;
+    }
+
+    /** Where to place the logo. */
+    protected final String logoHPlace() {
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings != null) {
+            final ChartSection cs = chartSettings.getChartSection();
+            final String place = cs.getLogoHPlacement();
+
+            return place;
+        }
+        return "center";
+    }
+
+    /** Where to place the logo. */
+    protected final String logoVPlace() {
+        final ChartSettings chartSettings = getChartSettings();
+        if (chartSettings != null) {
+            final ChartSection cs = chartSettings.getChartSection();
+            final String place = cs.getLogoVPlacement();
+
+            return place;
+        }
+        return "top";
+    }
+
+    /** Return the logo id from settings. */
+    private String showLogo(final ChartSettings chartSettings) {
+        if (chartSettings != null) {
+            final ChartSection cs = chartSettings.getChartSection();
+            final String logo = cs.getDisplayLogo();
+
+            return logo;
+        }
+        return "none";
+    }
+
+    /**
+     * This method is used to determine if a logo should be added to the plot.
+     *
+     * @return logo name (null if none).
+     */
+    protected final String showLogo() {
+        final ChartSettings chartSettings = getChartSettings();
+        return showLogo(chartSettings);
+    }
+
+    /**
+     * This method is used to determine if the resulting chart should display
+     * grid lines or not. <b>Note: this method always returns true!</b>
+     *
+     * @return true, if the chart should display grid lines, otherwise false.
+     */
+    protected final boolean isGridVisible() {
+        return true;
+    }
+
+    protected final void addAnnotationsToRenderer(final XYPlot plot) {
+
+        final AnnotationRenderer annotationRenderer = new AnnotationRenderer(getChartSettings(), this.datasets, DEFAULT_FONT_NAME);
+        annotationRenderer.addAnnotationsToRenderer(plot, this.annotations);
+
+        doAddFurtherAnnotations(plot, this.annotations);
+    }
+
+    /**
+     * Allow further annotation processing, override to implement.
+     *
+     * Does nothing by default.
+     */
+    protected void doAddFurtherAnnotations(final XYPlot plot, final List<RiverAnnotation> annotations) {
+
+    }
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/AnnotationRenderer.java	Tue Jun 05 19:21:16 2018 +0200
@@ -0,0 +1,352 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.exports;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.river.jfree.AxisDataset;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.RiverAnnotation;
+import org.dive4elements.river.jfree.StickyAxisAnnotation;
+import org.dive4elements.river.themes.LineStyle;
+import org.dive4elements.river.themes.TextStyle;
+import org.dive4elements.river.themes.ThemeDocument;
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.annotations.XYLineAnnotation;
+import org.jfree.chart.annotations.XYTextAnnotation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+import org.jfree.ui.TextAnchor;
+
+/**
+ * @author Gernot Belger
+ */
+public final class AnnotationRenderer {
+
+    private static final Logger log = Logger.getLogger(AnnotationRenderer.class);
+
+    private static float ANNOTATIONS_AXIS_OFFSET = 0.02f;
+
+    private final ChartSettings settings;
+
+    private final Map<Integer, AxisDataset> datasets;
+
+    private final String fontName;
+
+    public AnnotationRenderer(final ChartSettings settings, final Map<Integer, AxisDataset> datasets, final String fontName) {
+        this.settings = settings;
+        this.datasets = datasets;
+        this.fontName = fontName;
+    }
+
+    /**
+     * Add annotations (Sticky, Text and hyk zones) to a plot.
+     *
+     * @param annotations
+     *            Annotations to add
+     * @param plot
+     *            XYPlot to add annotations to.
+     * @param settings
+     *            ChartSettings object for settings.
+     * @param datasets
+     *            Map of axis index and datasets
+     */
+    public final void addAnnotationsToRenderer(final XYPlot plot, final List<RiverAnnotation> annotations) {
+        if (annotations == null || annotations.isEmpty()) {
+            log.debug("addAnnotationsToRenderer: no annotations.");
+            return;
+        }
+
+        // OPTMIMIZE: Pre-calculate positions
+        final ChartArea area = new ChartArea(plot.getDomainAxis(0), plot.getRangeAxis());
+
+        // Walk over all Annotation sets.
+        for (final RiverAnnotation fa : annotations) {
+
+            // Access text styling, if any.
+            final ThemeDocument theme = fa.getTheme();
+            TextStyle textStyle = null;
+            LineStyle lineStyle = null;
+
+            // Get Theming information and add legend item.
+            if (theme != null) {
+                textStyle = theme.parseComplexTextStyle();
+                lineStyle = theme.parseComplexLineStyle();
+                if (fa.getLabel() != null) {
+                    // Legend handling, maybe misplaced?
+                    final LegendItemCollection lic = new LegendItemCollection();
+                    LegendItemCollection old = plot.getFixedLegendItems();
+
+                    Color color = theme.parseLineColorField();
+                    if (color == null) {
+                        color = Color.BLACK;
+                    }
+
+                    Color textColor = theme.parseTextColor();
+                    if (textColor == null) {
+                        textColor = Color.BLACK;
+                    }
+
+                    final LegendItem newItem = new LegendItem(fa.getLabel(), color);
+
+                    final LegendSection ls = this.settings != null ? this.settings.getLegendSection() : null;
+
+                    final Integer size = ls != null ? ls.getFontSize() : null;
+
+                    newItem.setLabelFont(new Font(this.fontName, Font.PLAIN, size));
+
+                    newItem.setLabelPaint(textColor);
+
+                    lic.add(newItem);
+                    // (Re-)Add prior legend entries.
+                    if (old != null) {
+                        old.addAll(lic);
+                    } else {
+                        old = lic;
+                    }
+                    plot.setFixedLegendItems(old);
+                }
+            }
+
+            // The 'Sticky' Annotations (at axis, with line and text).
+            for (final StickyAxisAnnotation sta : fa.getAxisTextAnnotations()) {
+                addStickyAnnotation(sta, plot, area, lineStyle, textStyle, theme, this.datasets.get(new Integer(sta.getAxisSymbol())));
+            }
+
+            // Other Text Annotations (e.g. labels of (manual) points).
+            for (final XYTextAnnotation ta : fa.getTextAnnotations()) {
+                // Style the text.
+                if (textStyle != null) {
+                    textStyle.apply(ta);
+                }
+                ta.setY(area.above(0.05d, ta.getY()));
+                plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND);
+            }
+        }
+    }
+
+    /**
+     * Add a text and a line annotation.
+     *
+     * @param area
+     *            convenience to determine positions in plot.
+     * @param theme
+     *            (optional) theme document
+     */
+    private void addStickyAnnotation(final StickyAxisAnnotation annotation, final XYPlot plot, final ChartArea area, final LineStyle lineStyle,
+            final TextStyle textStyle, final ThemeDocument theme, final AxisDataset dataset) {
+        // OPTIMIZE pre-calculate area-related values
+        final float TEXT_OFF = 0.03f;
+
+        XYLineAnnotation lineAnnotation = null;
+        XYTextAnnotation textAnnotation = null;
+
+        final int axisIndex = annotation.getAxisSymbol();
+        XYItemRenderer renderer = null;
+        if (dataset != null && dataset.getDatasets().length > 0) {
+            renderer = plot.getRendererForDataset(dataset.getDatasets()[0]);
+        } else {
+            renderer = plot.getRenderer();
+        }
+
+        if (annotation.atX()) {
+            textAnnotation = new CollisionFreeXYTextAnnotation(annotation.getText(), annotation.getPos(), area.ofGround(TEXT_OFF));
+            // OPTIMIZE externalize the calculation involving PI.
+            // textAnnotation.setRotationAngle(270f*Math.PI/180f);
+            lineAnnotation = createGroundStickAnnotation(area, annotation.getPos(), lineStyle);
+            textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT);
+            textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT);
+        } else {
+            // Stick to the "right" (opposed to left) Y-Axis.
+            if (axisIndex != 0 && plot.getRangeAxis(axisIndex) != null) {
+                // OPTIMIZE: Pass a different area to this function,
+                // do the adding to renderer outside (let this
+                // function return the annotations).
+                // Note that this path is travelled rarely.
+                textAnnotation = new CollisionFreeXYTextAnnotation(annotation.getText(), area.ofRight(TEXT_OFF), annotation.getPos());
+                textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT);
+                textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT);
+                lineAnnotation = createRightStickAnnotation(area, annotation.getPos(), lineStyle);
+
+                // hit-lines for duration curve
+                final ChartArea area2 = new ChartArea(plot.getDomainAxis(), plot.getRangeAxis(axisIndex));
+                if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
+                    // New line annotation to hit curve.
+                    if (theme.parseShowVerticalLine()) {
+                        final XYLineAnnotation hitLineAnnotation = createStickyLineAnnotation(StickyAxisAnnotation.SimpleAxis.X_AXIS, annotation.getHitPoint(),
+                                annotation.getPos(),
+                                // annotation.getHitPoint(),
+                                area2, lineStyle);
+                        renderer.addAnnotation(hitLineAnnotation, org.jfree.ui.Layer.BACKGROUND);
+                    }
+                    if (theme.parseShowHorizontalLine()) {
+                        final XYLineAnnotation lineBackAnnotation = createStickyLineAnnotation(StickyAxisAnnotation.SimpleAxis.Y_AXIS2, annotation.getPos(),
+                                annotation.getHitPoint(), area2, lineStyle);
+                        renderer.addAnnotation(lineBackAnnotation, org.jfree.ui.Layer.BACKGROUND);
+                    }
+                }
+            } else { // Stick to the left y-axis.
+                textAnnotation = new CollisionFreeXYTextAnnotation(annotation.getText(), area.ofLeft(TEXT_OFF), annotation.getPos());
+                textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT);
+                textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT);
+                lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle);
+                if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
+                    // New line annotation to hit curve.
+                    if (theme.parseShowHorizontalLine()) {
+                        final XYLineAnnotation hitLineAnnotation = createStickyLineAnnotation(StickyAxisAnnotation.SimpleAxis.Y_AXIS, annotation.getPos(),
+                                annotation.getHitPoint(), area, lineStyle);
+                        renderer.addAnnotation(hitLineAnnotation, org.jfree.ui.Layer.BACKGROUND);
+                    }
+                    if (theme.parseShowVerticalLine()) {
+                        final XYLineAnnotation lineBackAnnotation = createStickyLineAnnotation(StickyAxisAnnotation.SimpleAxis.X_AXIS, annotation.getHitPoint(),
+                                annotation.getPos(), area, lineStyle);
+                        renderer.addAnnotation(lineBackAnnotation, org.jfree.ui.Layer.BACKGROUND);
+                    }
+                }
+            }
+        }
+
+        // Style the text.
+        if (textStyle != null) {
+            textStyle.apply(textAnnotation);
+        }
+
+        // Add the Annotations to renderer.
+        renderer.addAnnotation(textAnnotation, org.jfree.ui.Layer.FOREGROUND);
+        renderer.addAnnotation(lineAnnotation, org.jfree.ui.Layer.FOREGROUND);
+    }
+
+    public final void addYAnnotationsToRenderer(final XYPlot plot, final SortedMap<Integer, RiverAnnotation> yAnnotations) {
+        final List<RiverAnnotation> annotations = new ArrayList<>();
+
+        for (final Map.Entry<Integer, RiverAnnotation> entry : yAnnotations.entrySet()) {
+            final int axis = entry.getKey();
+            final AxisDataset dataset = this.datasets.get(new Integer(axis));
+
+            if (dataset == null || dataset.getRange() == null) {
+                log.warn("No dataset available and active for axis " + axis);
+            } else {
+                final RiverAnnotation ya = entry.getValue();
+                for (final StickyAxisAnnotation sta : ya.getAxisTextAnnotations()) {
+                    sta.setAxisSymbol(axis);
+                }
+                annotations.add(ya);
+            }
+        }
+
+        addAnnotationsToRenderer(plot, annotations);
+    }
+
+    /**
+     * Create annotation that sticks to "ground" (X) axis.
+     *
+     * @param area
+     *            helper to calculate coordinates
+     * @param pos
+     *            one-dimensional position (distance from axis)
+     * @param lineStyle
+     *            the line style to use for the line.
+     */
+    private XYLineAnnotation createGroundStickAnnotation(final ChartArea area, final float pos, final LineStyle lineStyle) {
+        if (lineStyle != null)
+            return new XYLineAnnotation(pos, area.atGround(), pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET), new BasicStroke(lineStyle.getWidth()),
+                    lineStyle.getColor());
+
+        return new XYLineAnnotation(pos, area.atGround(), pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET));
+    }
+
+    /**
+     * Create annotation that sticks to the second Y axis ("right").
+     *
+     * @param area
+     *            helper to calculate coordinates
+     * @param pos
+     *            one-dimensional position (distance from axis)
+     * @param lineStyle
+     *            the line style to use for the line.
+     */
+    private XYLineAnnotation createRightStickAnnotation(final ChartArea area, final float pos, final LineStyle lineStyle) {
+        if (lineStyle != null)
+            return new XYLineAnnotation(area.atRight(), pos, area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos, new BasicStroke(lineStyle.getWidth()),
+                    lineStyle.getColor());
+
+        return new XYLineAnnotation(area.atRight(), pos, area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos);
+    }
+
+    /**
+     * Create annotation that sticks to the first Y axis ("left").
+     *
+     * @param area
+     *            helper to calculate coordinates
+     * @param pos
+     *            one-dimensional position (distance from axis)
+     * @param lineStyle
+     *            the line style to use for the line.
+     */
+    private XYLineAnnotation createLeftStickAnnotation(final ChartArea area, final float pos, final LineStyle lineStyle) {
+        if (lineStyle != null)
+            return new XYLineAnnotation(area.atLeft(), pos, area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos, new BasicStroke(lineStyle.getWidth()),
+                    lineStyle.getColor());
+
+        return new XYLineAnnotation(area.atLeft(), pos, area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos);
+    }
+
+    /**
+     * Create a line from a axis to a given point.
+     *
+     * @param axis
+     *            The "simple" axis.
+     * @param fromD1
+     *            from-location in first dimension.
+     * @param toD2
+     *            to-location in second dimension.
+     * @param area
+     *            helper to calculate offsets.
+     * @param lineStyle
+     *            optional line style.
+     */
+    public static XYLineAnnotation createStickyLineAnnotation(final StickyAxisAnnotation.SimpleAxis axis, final float fromD1, final float toD2,
+            final ChartArea area, final LineStyle lineStyle) {
+        double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d;
+        switch (axis) {
+        case X_AXIS:
+            anchorX1 = fromD1;
+            anchorX2 = fromD1;
+            anchorY1 = area.atGround();
+            anchorY2 = toD2;
+            break;
+        case Y_AXIS:
+            anchorX1 = area.atLeft();
+            anchorX2 = toD2;
+            anchorY1 = fromD1;
+            anchorY2 = fromD1;
+            break;
+        case Y_AXIS2:
+            anchorX1 = area.atRight();
+            anchorX2 = toD2;
+            anchorY1 = fromD1;
+            anchorY2 = fromD1;
+            break;
+        }
+
+        if (lineStyle != null)
+            return new XYLineAnnotation(anchorX1, anchorY1, anchorX2, anchorY2, new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
+
+        return new XYLineAnnotation(anchorX1, anchorY1, anchorX2, anchorY2);
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/exports/AxisSection.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/AxisSection.java	Tue Jun 05 19:21:16 2018 +0200
@@ -85,8 +85,8 @@
     }
 
 
-    public Boolean isFixed() {
-        return getBooleanValue(FIXATION_ATTR);
+    public boolean isFixed() {
+        return getBooleanValue(FIXATION_ATTR, false);
     }
 
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartExportHelper.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartExportHelper.java	Tue Jun 05 19:21:16 2018 +0200
@@ -495,17 +495,4 @@
 
         return origin;
     }
-
-
-    public static String createMetadataSubtitle(Artifact artifact, final CallContext context, final String riverName) {
-        
-        final String version = FLYS.VERSION;
-        final String user = CalculationUtils.findArtifactUser(context, artifact);
-        final Locale locale = Resources.getLocale(context.getMeta());
-        final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
-        final String dateText = df.format(new Date());
-        
-        return Resources.getMsg(context.getMeta(), "chart.subtitle.metadata", "default", version, user, dateText, riverName);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -8,66 +8,18 @@
 
 package org.dive4elements.river.exports;
 
-import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
-import org.dive4elements.artifactdatabase.state.Settings;
-import org.dive4elements.artifacts.Artifact;
-import org.dive4elements.artifacts.ArtifactNamespaceContext;
-import org.dive4elements.artifacts.CallContext;
-import org.dive4elements.artifacts.CallMeta;
-import org.dive4elements.artifacts.PreferredLocale;
-import org.dive4elements.artifacts.common.utils.XMLUtils;
-
-import org.dive4elements.river.artifacts.access.RiverAccess;
-import org.dive4elements.river.artifacts.access.RangeAccess;
-import org.dive4elements.river.artifacts.D4EArtifact;
-import org.dive4elements.river.artifacts.resources.Resources;
-import org.dive4elements.river.collections.D4EArtifactCollection;
-import org.dive4elements.river.jfree.Bounds;
-import org.dive4elements.river.jfree.DoubleBounds;
-import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer;
-import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.StableXYDifferenceRenderer;
-import org.dive4elements.river.jfree.Style;
-import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
-import org.dive4elements.river.jfree.StyledSeries;
-import org.dive4elements.river.jfree.AxisDataset;
-import org.dive4elements.river.themes.ThemeDocument;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
 import java.awt.Font;
-import java.awt.Paint;
-import java.awt.Stroke;
-import java.awt.TexturePaint;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import javax.xml.xpath.XPathConstants;
 
-import org.apache.log4j.Logger;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.LegendItem;
-import org.jfree.chart.LegendItemCollection;
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.jfree.RiverAnnotation;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.jfree.chart.axis.NumberAxis;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-import org.jfree.chart.title.TextTitle;
 import org.jfree.data.Range;
-import org.jfree.data.general.Series;
-import org.jfree.data.xy.XYDataset;
-import org.jfree.ui.RectangleInsets;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import org.dive4elements.river.utils.Formatter;
 
 /**
  * The base class for chart creation. It should provide some basic things that
@@ -82,57 +34,6 @@
  */
 public abstract class ChartGenerator extends AbstractChartGenerator {
 
-    private static Logger log = Logger.getLogger(ChartGenerator.class);
-
-    public static final int    DEFAULT_CHART_WIDTH     = 600;
-    public static final int    DEFAULT_CHART_HEIGHT    = 400;
-    public static final String DEFAULT_CHART_FORMAT    = "png";
-    public static final Color  DEFAULT_GRID_COLOR      = Color.GRAY;
-    public static final float  DEFAULT_GRID_LINE_WIDTH = 0.3f;
-    public static final int    DEFAULT_FONT_SIZE       = 12;
-    public static final String DEFAULT_FONT_NAME       = "Tahoma";
-
-    protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f;
-
-    public static final String XPATH_CHART_SIZE =
-        "/art:action/art:attributes/art:size";
-
-    public static final String XPATH_CHART_FORMAT =
-        "/art:action/art:attributes/art:format/@art:value";
-
-    public static final String XPATH_CHART_X_RANGE =
-        "/art:action/art:attributes/art:xrange";
-
-    public static final String XPATH_CHART_Y_RANGE =
-        "/art:action/art:attributes/art:yrange";
-
-
-    /** The document of the incoming out() request.*/
-    protected Document request;
-
-    /** The output stream where the data should be written to.*/
-    protected OutputStream out;
-
-    /** The CallContext object.*/
-    protected CallContext context;
-
-    protected D4EArtifactCollection collection;
-
-    /** Artifact that is used to decorate the chart with meta information.*/
-    protected Artifact master;
-
-    /** The settings that should be used during output creation.*/
-    protected Settings settings;
-
-    /** Map of datasets ("index"). */
-    protected SortedMap<Integer, AxisDataset> datasets;
-
-    /** List of annotations to insert in plot. */
-    protected List<RiverAnnotation> annotations =
-        new ArrayList<RiverAnnotation>();
-
-    protected String outName;
-
     /**
      * A mini interface that allows to walk over the YAXIS enums defined in
      * subclasses.
@@ -145,158 +46,8 @@
     } // end of YAxisWalker interface
 
 
-    /**
-     * Default constructor that initializes internal data structures.
-     */
-    public ChartGenerator() {
-        datasets = new TreeMap<>();
-    }
-    
-    @Override
-    protected final D4EArtifact getArtifact() {
-        // FIXME: should already made sure when this member is set
-        return (D4EArtifact) this.master;
-    }
-
-    @Override
-    protected final CallContext getContext() {
-        return this.context;
-    }
-    
-    @Override
-    protected final Document getRequest() {
-        return this.request;
-    }
-    
-    @Override
-    public void setup(Object config) {
-        log.debug("ChartGenerator.setup");
-    }
-
-    /**
-     * Adds annotations to list. The given annotation will be visible.
-     */
-    public void addAnnotations(RiverAnnotation annotation) {
-        annotations.add(annotation);
-    }
-
-
-    /**
-     * This method needs to be implemented by concrete subclasses to create new
-     * instances of JFreeChart.
-     *
-     * @return a new instance of a JFreeChart.
-     */
-    public abstract JFreeChart generateChart();
-
-
-    /** For every outable (i.e. facets), this function is
-     * called and handles the data accordingly. */
-    @Override
-    public abstract void doOut(
-        ArtifactAndFacet bundle,
-        ThemeDocument    attr,
-        boolean          visible);
-
-
     protected abstract YAxisWalker getYAxisWalker();
 
-
-    protected abstract Series getSeriesOf(XYDataset dataset, int idx);
-
-    /**
-     * Returns the default title of a chart.
-     *
-     * @return the default title of a chart.
-     */
-    protected abstract String getDefaultChartTitle();
-
-
-    /**
-     * Returns the default X-Axis label of a chart.
-     *
-     * @return the default X-Axis label of a chart.
-     */
-    protected abstract String getDefaultXAxisLabel();
-
-
-    /**
-     * This method is called to retrieve the default label for an Y axis at
-     * position <i>pos</i>.
-     *
-     * @param pos The position of an Y axis.
-     *
-     * @return the default Y axis label at position <i>pos</i>.
-     */
-    protected abstract String getDefaultYAxisLabel(int pos);
-
-
-    /**
-     * This method is used to create new AxisDataset instances which may differ
-     * in concrete subclasses.
-     *
-     * @param idx The index of an axis.
-     */
-    protected abstract AxisDataset createAxisDataset(int idx);
-
-
-    /**
-     * Combines the ranges of the X axis at index <i>idx</i>.
-     *
-     * @param bounds A new Bounds.
-     * @param idx The index of the X axis that should be comined with
-     * <i>range</i>.
-     */
-    protected abstract void combineXBounds(Bounds bounds, int idx);
-
-
-    /**
-     * Combines the ranges of the Y axis at index <i>idx</i>.
-     *
-     * @param bounds A new Bounds.
-     * @param index The index of the Y axis that should be comined with.
-     * <i>range</i>.
-     */
-    protected abstract void combineYBounds(Bounds bounds, int index);
-
-
-    /**
-     * This method is used to determine the ranges for axes at a given index.
-     *
-     * @param index The index of the axes at the plot.
-     *
-     * @return a Range[] with [xrange, yrange];
-     */
-    public abstract Range[] getRangesForAxis(int index);
-
-    public abstract Bounds getXBounds(int axis);
-
-    protected abstract void setXBounds(int axis, Bounds bounds);
-
-    public abstract Bounds getYBounds(int axis);
-
-    protected abstract void setYBounds(int axis, Bounds bounds);
-
-
-    /**
-     * This method retrieves the chart subtitle by calling getChartSubtitle()
-     * and adds it as TextTitle to the chart.
-     * The default implementation of getChartSubtitle() returns the same
-     * as getDefaultChartSubtitle() which must be implemented by derived
-     * classes. If you want to add multiple subtitles to the chart override
-     * this method and add your subtitles manually.
-     *
-     * @param chart The JFreeChart chart object.
-     */
-    protected void addSubtitles(JFreeChart chart) {
-        String subtitle = getChartSubtitle();
-
-        if (subtitle != null && subtitle.length() > 0) {
-            chart.addSubtitle(new TextTitle(subtitle));
-        }
-    }
-
-
     /**
      * Register annotations like MainValues for later plotting
      *
@@ -329,148 +80,21 @@
         }
     }
 
-
-    /**
-     * Generate chart.
-     */
-    @Override
-    public void generate()
-    throws IOException
-    {
-        log.debug("ChartGenerator.generate");
-
-        JFreeChart chart = generateChart();
-
-        String format = getFormat();
-        int[]  size   = getSize();
-
-        if (size == null) {
-            size = getExportDimension();
-        }
-
-        context.putContextValue("chart.width",  size[0]);
-        context.putContextValue("chart.height", size[1]);
-
-        if (format.equals(ChartExportHelper.FORMAT_PNG)) {
-            context.putContextValue("chart.image.format", "png");
-
-            ChartExportHelper.exportImage(
-                out,
-                chart,
-                context);
-        }
-        else if (format.equals(ChartExportHelper.FORMAT_PDF)) {
-            preparePDFContext(context);
-
-            ChartExportHelper.exportPDF(
-                out,
-                chart,
-                context);
-        }
-        else if (format.equals(ChartExportHelper.FORMAT_SVG)) {
-            prepareSVGContext(context);
-
-            ChartExportHelper.exportSVG(
-                out,
-                chart,
-                context);
-        }
-        else if (format.equals(ChartExportHelper.FORMAT_CSV)) {
-            context.putContextValue("chart.image.format", "csv");
-
-            ChartExportHelper.exportCSV(
-                out,
-                chart,
-                context);
-        }
-    }
-
-
     @Override
-    public void init(
-        String outName,
-        Document request,
-        OutputStream out,
-        CallContext context
-    ) {
-        log.debug("ChartGenerator.init");
-
-        this.outName = outName;
-        this.request = request;
-        this.out     = out;
-        this.context = context;
-    }
-
-
-    /** Sets the master artifact. */
-    @Override
-    public void setMasterArtifact(Artifact master) {
-        this.master = master;
-    }
-
-
-    /**
-     * Gets the master artifact.
-     * @return the master artifact.
-     */
-    public Artifact getMaster() {
-        return master;
-    }
-
-
-    /** Sets the collection. */
-    @Override
-    public void setCollection(D4EArtifactCollection collection) {
-        this.collection = collection;
+    protected void doGenerate(CallContext context, OutputStream out, String outName) throws IOException {
+        generateImage(context);
     }
 
-
-    @Override
-    public void setSettings(Settings settings) {
-        this.settings = settings;
-    }
-
-
-    /**
-     * Returns instance of <i>ChartSettings</i> with a chart specific section
-     * but with no axes settings.
-     *
-     * @return an instance of <i>ChartSettings</i>.
-     */
-    @Override
-    public Settings getSettings() {
-        if (this.settings != null) {
-            return this.settings;
-        }
-
-        ChartSettings settings = new ChartSettings();
-
-        ChartSection  chartSection  = buildChartSection();
-        LegendSection legendSection = buildLegendSection();
-        ExportSection exportSection = buildExportSection();
-
-        settings.setChartSection(chartSection);
-        settings.setLegendSection(legendSection);
-        settings.setExportSection(exportSection);
-
-        List<AxisSection> axisSections = buildAxisSections();
-        for (AxisSection axisSection: axisSections) {
-            settings.addAxisSection(axisSection);
-        }
-
-        return settings;
-    }
-
-
     /**
      * Creates a new <i>ChartSection</i>.
      *
      * @return a new <i>ChartSection</i>.
      */
-    protected ChartSection buildChartSection() {
+    @Override
+    protected ChartSection buildChartSection(final CallContext context) {
         ChartSection chartSection = new ChartSection();
-        chartSection.setTitle(getChartTitle());
-        chartSection.setSubtitle(getChartSubtitle());
+        chartSection.setTitle(getChartTitle(context));
+        chartSection.setSubtitle(getChartSubtitle(context));
         chartSection.setDisplayGrid(isGridVisible());
         chartSection.setDisplayLogo(showLogo());
         chartSection.setLogoVPlacement(logoVPlace());
@@ -478,78 +102,6 @@
         return chartSection;
     }
 
-
-    /**
-     * Creates a new <i>LegendSection</i>.
-     *
-     * @return a new <i>LegendSection</i>.
-     */
-    protected LegendSection buildLegendSection() {
-        LegendSection legendSection = new LegendSection();
-        legendSection.setVisibility(isLegendVisible());
-        legendSection.setFontSize(getLegendFontSize());
-        legendSection.setAggregationThreshold(10);
-        return legendSection;
-    }
-
-
-    /**
-     * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b>
-     * and <b>HEIGHT=400</b>.
-     *
-     * @return a new <i>ExportSection</i>.
-     */
-    protected ExportSection buildExportSection() {
-        ExportSection exportSection = new ExportSection();
-        exportSection.setWidth(600);
-        exportSection.setHeight(400);
-        return exportSection;
-    }
-
-
-    /**
-     * Create list of Sections that contains all axes of the chart (including
-     * X and Y axes).
-     *
-     * @return a list of Sections for each axis in this chart.
-     */
-    protected List<AxisSection> buildAxisSections() {
-        List<AxisSection> axisSections = new ArrayList<AxisSection>();
-
-        axisSections.addAll(buildXAxisSections());
-        axisSections.addAll(buildYAxisSections());
-
-        return axisSections;
-    }
-
-
-    /**
-     * Creates a new Section for chart's X axis.
-     *
-     * @return a List that contains a Section for the X axis.
-     */
-    protected List<AxisSection> buildXAxisSections() {
-        List<AxisSection> axisSections = new ArrayList<AxisSection>();
-
-        String identifier = "X";
-
-        AxisSection axisSection = new AxisSection();
-        axisSection.setIdentifier(identifier);
-        axisSection.setLabel(getXAxisLabel());
-        axisSection.setFontSize(14);
-        axisSection.setFixed(false);
-
-        // XXX We are able to find better default ranges that [0,0], but the Y
-        // axes currently have no better ranges set.
-        axisSection.setUpperRange(0d);
-        axisSection.setLowerRange(0d);
-
-        axisSections.add(axisSection);
-
-        return axisSections;
-    }
-
-
     /**
      * Creates a list of Section for the chart's Y axes. This method makes use
      * of <i>getYAxisWalker</i> to be able to access all Y axes defined in
@@ -557,8 +109,9 @@
      *
      * @return a list of Y axis sections.
      */
-    protected List<AxisSection> buildYAxisSections() {
-        List<AxisSection> axisSections = new ArrayList<AxisSection>();
+    @Override
+    protected final List<AxisSection> buildYAxisSections() {
+        List<AxisSection> axisSections = new ArrayList<>();
 
         YAxisWalker walker = getYAxisWalker();
         for (int i = 0, n = walker.length(); i < n; i++) {
@@ -581,127 +134,6 @@
         return axisSections;
     }
 
-
-    /**
-     * Returns the <i>settings</i> as <i>ChartSettings</i>.
-     *
-     * @return the <i>settings</i> as <i>ChartSettings</i> or null, if
-     * <i>settings</i> is not an instance of <i>ChartSettings</i>.
-     */
-    public ChartSettings getChartSettings() {
-        if (settings instanceof ChartSettings) {
-            return (ChartSettings) settings;
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Returns the chart title provided by <i>settings</i>.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return the title provided by <i>settings</i> or null if no
-     * <i>ChartSection</i> is provided by <i>settings</i>.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public String getChartTitle(ChartSettings settings) {
-        ChartSection cs = settings.getChartSection();
-        return cs != null ? cs.getTitle() : null;
-    }
-
-
-    /**
-     * Returns the chart subtitle provided by <i>settings</i>.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return the subtitle provided by <i>settings</i> or null if no
-     * <i>ChartSection</i> is provided by <i>settings</i>.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public String getChartSubtitle(ChartSettings settings) {
-        ChartSection cs = settings.getChartSection();
-        return cs != null ? cs.getSubtitle() : null;
-    }
-
-
-    /**
-     * Returns a boolean object that determines if the chart grid should be
-     * visible or not. This information needs to be provided by <i>settings</i>,
-     * otherweise the default is true.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return true, if the chart grid should be visible otherwise false.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public boolean isGridVisible(ChartSettings settings) {
-        ChartSection     cs = settings.getChartSection();
-        Boolean displayGrid = cs.getDisplayGrid();
-
-        return displayGrid != null ? displayGrid : true;
-    }
-
-
-    /**
-     * Returns a boolean object that determines if the chart legend should be
-     * visible or not. This information needs to be provided by <i>settings</i>,
-     * otherwise the default is true.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return true, if the chart legend should be visible otherwise false.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public boolean isLegendVisible(ChartSettings settings) {
-        LegendSection      ls = settings.getLegendSection();
-        Boolean displayLegend = ls.getVisibility();
-
-        return displayLegend != null ? displayLegend : true;
-    }
-
-
-    /**
-     * Returns the legend font size specified in <i>settings</i> or null if no
-     * <i>LegendSection</i> is provided by <i>settings</i>.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return the legend font size or null.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public Integer getLegendFontSize(ChartSettings settings) {
-        LegendSection ls = settings.getLegendSection();
-        return ls != null ? ls.getFontSize() : null;
-    }
-
-
-    /**
-     * Returns the title of a chart. The return value depends on the existence
-     * of ChartSettings: if there are ChartSettings set, this method returns the
-     * chart title provided by those settings. Otherwise, this method returns
-     * getDefaultChartTitle().
-     *
-     * @return the title of a chart.
-     */
-    protected String getChartTitle() {
-        ChartSettings chartSettings = getChartSettings();
-
-        if (chartSettings != null) {
-            return getChartTitle(chartSettings);
-        }
-
-        return getDefaultChartTitle();
-    }
-
-
     /**
      * Returns the subtitle of a chart. The return value depends on the
      * existence of ChartSettings: if there are ChartSettings set, this method
@@ -710,171 +142,16 @@
      *
      * @return the subtitle of a chart.
      */
-    protected String getChartSubtitle() {
+    @Override
+    protected String getChartSubtitle(final CallContext context) {
         ChartSettings chartSettings = getChartSettings();
 
-        if (chartSettings != null) {
+        if (chartSettings != null) 
             return getChartSubtitle(chartSettings);
-        }
-
-        return getDefaultChartSubtitle();
-    }
-
-
-    /**
-     * This method always returns null. Override it in subclasses that require
-     * subtitles.
-     *
-     * @return null.
-     */
-    protected String getDefaultChartSubtitle() {
-        // Override this method in subclasses
-        return null;
-    }
-
-
-    /**
-     * This method is used to determine, if the chart's legend is visible or
-     * not. If a <i>settings</i> instance is set, this instance determines the
-     * visibility otherwise, this method returns true as default if no
-     * <i>settings</i> is set.
-     *
-     * @return true, if the legend should be visible, otherwise false.
-     */
-    protected boolean isLegendVisible() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings != null) {
-            return isLegendVisible(chartSettings);
-        }
-
-        return true;
-    }
-
-
-    /** Where to place the logo. */
-    protected String logoHPlace() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings != null) {
-            ChartSection cs    = chartSettings.getChartSection();
-            String       place = cs.getLogoHPlacement();
-
-            return place;
-        }
-        return "center";
-    }
-
-
-    /** Where to place the logo. */
-    protected String logoVPlace() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings != null) {
-            ChartSection cs    = chartSettings.getChartSection();
-            String       place = cs.getLogoVPlacement();
-
-            return place;
-        }
-        return "top";
-    }
-
-
-    /** Return the logo id from settings. */
-    protected String showLogo(ChartSettings chartSettings) {
-        if (chartSettings != null) {
-            ChartSection cs   = chartSettings.getChartSection();
-            String       logo = cs.getDisplayLogo();
-
-            return logo;
-        }
-        return "none";
-    }
-
 
-    /**
-     * This method is used to determine if a logo should be added to the plot.
-     *
-     * @return logo name (null if none).
-     */
-    protected String showLogo() {
-        ChartSettings chartSettings = getChartSettings();
-        return showLogo(chartSettings);
-    }
-
-
-    /**
-     * This method is used to determine the font size of the chart's legend. If
-     * a <i>settings</i> instance is set, this instance determines the font
-     * size, otherwise this method returns 12 as default if no <i>settings</i>
-     * is set or if it doesn't provide a legend font size.
-     *
-     * @return a legend font size.
-     */
-    protected int getLegendFontSize() {
-        Integer fontSize = null;
-
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings != null) {
-            fontSize = getLegendFontSize(chartSettings);
-        }
-
-        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
-    }
-
-
-    /**
-     * This method is used to determine if the resulting chart should display
-     * grid lines or not. <b>Note: this method always returns true!</b>
-     *
-     * @return true, if the chart should display grid lines, otherwise false.
-     */
-    protected boolean isGridVisible() {
-        return true;
+        return getDefaultChartSubtitle(context);
     }
 
-
-    /**
-     * Returns the X-Axis label of a chart.
-     *
-     * @return the X-Axis label of a chart.
-     */
-    protected String getXAxisLabel() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings == null) {
-            return getDefaultXAxisLabel();
-        }
-
-        AxisSection as = chartSettings.getAxisSection("X");
-        if (as != null) {
-            String label = as.getLabel();
-
-            if (label != null) {
-                return label;
-            }
-        }
-
-        return getDefaultXAxisLabel();
-    }
-
-
-    /**
-     * This method returns the font size for the X axis. If the font size is
-     * specified in ChartSettings (if <i>chartSettings</i> is set), this size is
-     * returned. Otherwise the default font size 12 is returned.
-     *
-     * @return the font size for the x axis.
-     */
-    protected int getXAxisLabelFontSize() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings == null) {
-            return DEFAULT_FONT_SIZE;
-        }
-
-        AxisSection   as = chartSettings.getAxisSection("X");
-        Integer fontSize = as.getFontSize();
-
-        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
-    }
-
-
     /**
      * This method returns the font size for an Y axis. If the font size is
      * specified in ChartSettings (if <i>chartSettings</i> is set), this size is
@@ -899,31 +176,6 @@
         return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
     }
 
-
-    /**
-     * This method returns the export dimension specified in ChartSettings as
-     * int array [width,height].
-     *
-     * @return an int array with [width,height].
-     */
-    protected int[] getExportDimension() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings == null) {
-            return new int[] { 600, 400 };
-        }
-
-        ExportSection export = chartSettings.getExportSection();
-        Integer width  = export.getWidth();
-        Integer height = export.getHeight();
-
-        if (width != null && height != null) {
-            return new int[] { width, height };
-        }
-
-        return new int[] { 600, 400 };
-    }
-
-
     /**
      * Returns the Y-Axis label of a chart at position <i>pos</i>.
      *
@@ -947,7 +199,17 @@
 
         return getDefaultYAxisLabel(pos);
     }
-
+    
+    /**
+     * This method is called to retrieve the default label for an Y axis at
+     * position <i>pos</i>.
+     *
+     * @param pos
+     *            The position of an Y axis.
+     *
+     * @return the default Y axis label at position <i>pos</i>.
+     */
+    protected abstract String getDefaultYAxisLabel(int pos);
 
     /**
      * This method searches for a specific axis in the <i>settings</i> if
@@ -972,9 +234,7 @@
             return null;
         }
 
-        Boolean fixed = as.isFixed();
-
-        if (fixed != null && fixed) {
+        if (as.isFixed()) {
 
             /* Only time series charts have time ranges so prefer those. */
             if (axisId.equals("X")) {
@@ -1002,486 +262,6 @@
         return null;
     }
 
-
-    /**
-     * Adds a new AxisDataset which contains <i>dataset</i> at index <i>idx</i>.
-     *
-     * @param dataset An XYDataset.
-     * @param idx The axis index.
-     * @param visible Determines, if the dataset should be visible or not.
-     */
-    public void addAxisDataset(XYDataset dataset, int idx, boolean visible) {
-        if (dataset == null || idx < 0) {
-            return;
-        }
-
-        AxisDataset axisDataset = getAxisDataset(idx);
-
-        Bounds[] xyBounds = ChartHelper.getBounds(dataset);
-
-        if (xyBounds == null) {
-            log.warn("Skip XYDataset for Axis (invalid ranges): " + idx);
-            return;
-        }
-
-        if (visible) {
-            if (log.isDebugEnabled()) {
-                log.debug("Add new AxisDataset at index: " + idx);
-                log.debug("X extent: " + xyBounds[0]);
-                log.debug("Y extent: " + xyBounds[1]);
-            }
-
-            axisDataset.addDataset(dataset);
-        }
-
-        combineXBounds(xyBounds[0], 0);
-        combineYBounds(xyBounds[1], idx);
-    }
-
-
-    /**
-     * This method grants access to the AxisDatasets stored in <i>datasets</i>.
-     * If no AxisDataset exists for index <i>idx</i>, a new AxisDataset is
-     * created using <i>createAxisDataset()</i>.
-     *
-     * @param idx The index of the desired AxisDataset.
-     *
-     * @return an existing or new AxisDataset.
-     */
-    public AxisDataset getAxisDataset(int idx) {
-        AxisDataset axisDataset = datasets.get(idx);
-
-        if (axisDataset == null) {
-            axisDataset = createAxisDataset(idx);
-            datasets.put(idx, axisDataset);
-        }
-
-        return axisDataset;
-    }
-
-
-    /**
-     * Adjust some Stroke/Grid parameters for <i>plot</i>. The chart
-     * <i>Settings</i> are applied in this method.
-     *
-     * @param plot The XYPlot which is adapted.
-     */
-    protected void adjustPlot(XYPlot plot) {
-        Stroke gridStroke = new BasicStroke(
-            DEFAULT_GRID_LINE_WIDTH,
-            BasicStroke.CAP_BUTT,
-            BasicStroke.JOIN_MITER,
-            3.0f,
-            new float[] { 3.0f },
-            0.0f);
-
-        ChartSettings      cs = getChartSettings();
-        boolean isGridVisible = cs != null ? isGridVisible(cs) : true;
-
-        plot.setDomainGridlineStroke(gridStroke);
-        plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR);
-        plot.setDomainGridlinesVisible(isGridVisible);
-
-        plot.setRangeGridlineStroke(gridStroke);
-        plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR);
-        plot.setRangeGridlinesVisible(isGridVisible);
-
-        plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d));
-    }
-
-
-    /**
-     * This helper mehtod is used to extract the current locale from instance
-     * vairable <i>context</i>.
-     *
-     * @return the current locale.
-     */
-    protected Locale getLocale() {
-        CallMeta           meta = context.getMeta();
-        PreferredLocale[] prefs = meta.getLanguages();
-
-        int len = prefs != null ? prefs.length : 0;
-
-        Locale[] locales = new Locale[len];
-
-        for (int i = 0; i < len; i++) {
-            locales[i] = prefs[i].getLocale();
-        }
-
-        return meta.getPreferredLocale(locales);
-    }
-
-
-    /**
-     * Look up \param key in i18n dictionary.
-     * @param key key for which to find i18nd version.
-     * @param def default, returned if lookup failed.
-     * @return value found in i18n dictionary, \param def if no value found.
-     */
-    protected String msg(String key, String def) {
-        return Resources.getMsg(context.getMeta(), key, def);
-    }
-
-    /**
-     * Look up \param key in i18n dictionary.
-     * @param key key for which to find i18nd version.
-     * @return value found in i18n dictionary, key itself if failed.
-     */
-    protected String msg(String key) {
-        return Resources.getMsg(context.getMeta(), key, key);
-    }
-
-    protected String msg(String key, Object[] args) {
-        return Resources.getMsg(context.getMeta(), key, key, args);
-    }
-
-    protected String msg(String key, String def, Object[] args) {
-        return Resources.getMsg(context.getMeta(), key, def, args);
-    }
-
-    /**
-     * Returns the size of a chart export as array which has been specified by
-     * the incoming request document.
-     *
-     * @return the size of a chart as [width, height] or null if no width or
-     * height are given in the request document.
-     */
-    protected int[] getSize() {
-        int[] size = new int[2];
-
-        Element sizeEl = (Element)XMLUtils.xpath(
-            request,
-            XPATH_CHART_SIZE,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (sizeEl != null) {
-            String uri = ArtifactNamespaceContext.NAMESPACE_URI;
-
-            String w = sizeEl.getAttributeNS(uri, "width");
-            String h = sizeEl.getAttributeNS(uri, "height");
-
-            if (w.length() > 0 && h.length() > 0) {
-                try {
-                    size[0] = Integer.parseInt(w);
-                    size[1] = Integer.parseInt(h);
-                }
-                catch (NumberFormatException nfe) {
-                    log.warn("Wrong values for chart width/height.");
-                }
-            }
-        }
-
-        return size[0] > 0 && size[1] > 0 ? size : null;
-    }
-
-
-    /**
-     * This method returns the format specified in the <i>request</i> document
-     * or <i>DEFAULT_CHART_FORMAT</i> if no format is specified in
-     * <i>request</i>.
-     *
-     * @return the format used to export this chart.
-     */
-    protected String getFormat() {
-        String format = (String) XMLUtils.xpath(
-            request,
-            XPATH_CHART_FORMAT,
-            XPathConstants.STRING,
-            ArtifactNamespaceContext.INSTANCE);
-
-        return format == null || format.length() == 0
-            ? DEFAULT_CHART_FORMAT
-            : format;
-    }
-
-    /**
-     * Returns the X-Axis range as String array from request document.
-     * If the (x|y)range elements are not found in request document, return
-     * null (i.e. not zoomed).
-     *
-     * @return a String array with [lower, upper], null if not in document.
-     */
-    protected String[] getDomainAxisRangeFromRequest() {
-        Element xrange = (Element)XMLUtils.xpath(
-            request,
-            XPATH_CHART_X_RANGE,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (xrange == null) {
-            return null;
-        }
-
-        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
-
-        String lower = xrange.getAttributeNS(uri, "from");
-        String upper = xrange.getAttributeNS(uri, "to");
-
-        return new String[] { lower, upper };
-    }
-
-
-    /** Returns null if the (x|y)range-element was not found in
-     * request document.
-     * This usally means that the axis are not manually zoomed, i.e. showing
-     * full data extent. */
-    protected String[] getValueAxisRangeFromRequest() {
-        Element yrange = (Element)XMLUtils.xpath(
-            request,
-            XPATH_CHART_Y_RANGE,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (yrange == null) {
-            return null;
-        }
-
-
-        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
-
-        String lower = yrange.getAttributeNS(uri, "from");
-        String upper = yrange.getAttributeNS(uri, "to");
-
-        return new String[] { lower, upper };
-    }
-
-
-    /**
-     * Returns the default size of a chart export as array.
-     *
-     * @return the default size of a chart as [width, height].
-     */
-    protected int[] getDefaultSize() {
-        return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT };
-    }
-
-
-    /**
-     * Add datasets stored in instance variable <i>datasets</i> to plot.
-     * <i>datasets</i> actually stores instances of AxisDataset, so each of this
-     * datasets is mapped to a specific axis as well.
-     *
-     * @param plot plot to add datasets to.
-     */
-    protected void addDatasets(XYPlot plot) {
-        log.debug("addDatasets()");
-
-        // AxisDatasets are sorted, but some might be empty.
-        // Thus, generate numbering on the fly.
-        int axisIndex    = 0;
-        int datasetIndex = 0;
-
-        for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) {
-            if (!entry.getValue().isEmpty()) {
-                // Add axis and range information.
-                AxisDataset axisDataset = entry.getValue();
-                NumberAxis  axis        = createYAxis(entry.getKey());
-
-                plot.setRangeAxis(axisIndex, axis);
-
-                if (axis.getAutoRangeIncludesZero()) {
-                    axisDataset.setRange(
-                        Range.expandToInclude(axisDataset.getRange(), 0d));
-                }
-
-                setYBounds(
-                    axisIndex, expandPointRange(axisDataset.getRange()));
-
-                // Add contained datasets, mapping to axis.
-                for (XYDataset dataset: axisDataset.getDatasets()) {
-                    plot.setDataset(datasetIndex, dataset);
-                    plot.mapDatasetToRangeAxis(datasetIndex, axisIndex);
-
-                    applyThemes(plot, dataset,
-                        datasetIndex,
-                        axisDataset.isArea(dataset));
-
-                    datasetIndex++;
-                }
-
-                axisDataset.setPlotAxisIndex(axisIndex);
-                axisIndex++;
-            }
-        }
-    }
-
-
-    /**
-     * @param idx "index" of dataset/series (first dataset to be drawn has
-     *            index 0), correlates with renderer index.
-     * @param isArea true if the series describes an area and shall be rendered
-     *                as such.
-     */
-    protected void applyThemes(
-        XYPlot    plot,
-        XYDataset series,
-        int       idx,
-        boolean   isArea
-    ) {
-        if (isArea) {
-            applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx);
-        }
-        else {
-            applyLineTheme(plot, series, idx);
-        }
-    }
-
-
-    /**
-     * This method applies the themes defined in the series itself. Therefore,
-     * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer
-     * for the series.
-     *
-     * @param plot The plot.
-     * @param dataset The XYDataset which needs to support Series objects.
-     * @param idx The index of the renderer / dataset.
-     */
-    protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) {
-        log.debug("Apply LineTheme for dataset at index: " + idx);
-
-        LegendItemCollection lic  = new LegendItemCollection();
-        LegendItemCollection anno = plot.getFixedLegendItems();
-
-        Font legendFont = createLegendLabelFont();
-
-        XYLineAndShapeRenderer renderer = createRenderer(plot, idx);
-
-        for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) {
-            Series series = getSeriesOf(dataset, s);
-
-            if (series instanceof StyledSeries) {
-                Style style = ((StyledSeries) series).getStyle();
-                style.applyTheme(renderer, s);
-            }
-
-            // special case: if there is just one single item, we need to enable
-            // points for this series, otherwise we would not see anything in
-            // the chart area.
-            if (series.getItemCount() == 1) {
-                renderer.setSeriesShapesVisible(s, true);
-            }
-
-            LegendItem legendItem = renderer.getLegendItem(idx, s);
-            if (legendItem.getLabel().endsWith(" ") ||
-                legendItem.getLabel().endsWith("interpol")) {
-                legendItem = null;
-            }
-
-            if (legendItem != null) {
-                legendItem.setLabelFont(legendFont);
-                lic.add(legendItem);
-            }
-            else {
-                log.warn("Could not get LegentItem for renderer: "
-                    + idx + ", series-idx " + s);
-            }
-        }
-
-        if (anno != null) {
-            lic.addAll(anno);
-        }
-
-        plot.setFixedLegendItems(lic);
-
-        plot.setRenderer(idx, renderer);
-    }
-
-
-    /**
-     * @param plot The plot.
-     * @param area A StyledAreaSeriesCollection object.
-     * @param idx The index of the dataset.
-     */
-    protected void applyAreaTheme(
-        XYPlot                     plot,
-        StyledAreaSeriesCollection area,
-        int                        idx
-    ) {
-        LegendItemCollection lic  = new LegendItemCollection();
-        LegendItemCollection anno = plot.getFixedLegendItems();
-
-        Font legendFont = createLegendLabelFont();
-
-        log.debug("Registering an 'area'renderer at idx: " + idx);
-
-        StableXYDifferenceRenderer dRenderer =
-            new StableXYDifferenceRenderer();
-
-        if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) {
-            dRenderer.setPositivePaint(createTransparentPaint());
-        }
-
-        plot.setRenderer(idx, dRenderer);
-
-        area.applyTheme(dRenderer);
-
-        // i18n
-        dRenderer.setAreaLabelNumberFormat(
-            Formatter.getFormatter(context.getMeta(), 2, 4));
-
-        dRenderer.setAreaLabelTemplate(Resources.getMsg(
-            context.getMeta(), "area.label.template", "Area=%sm2"));
-
-        LegendItem legendItem = dRenderer.getLegendItem(idx, 0);
-        if (legendItem != null) {
-            legendItem.setLabelFont(legendFont);
-            lic.add(legendItem);
-        }
-        else {
-            log.warn("Could not get LegentItem for renderer: "
-                + idx + ", series-idx " + 0);
-        }
-
-        if (anno != null) {
-            lic.addAll(anno);
-        }
-
-        plot.setFixedLegendItems(lic);
-    }
-
-
-    /**
-     * Expands a given range if it collapses into one point.
-     *
-     * @param range Range to be expanded if upper == lower bound.
-     *
-     * @return Bounds of point plus 5 percent in each direction.
-     */
-    private Bounds expandPointRange(Range range) {
-        if (range == null) {
-            return null;
-        }
-        else if (range.getLowerBound() == range.getUpperBound()) {
-            Range expandedRange = ChartHelper.expandRange(range, 5d);
-            return new DoubleBounds(
-                expandedRange.getLowerBound(), expandedRange.getUpperBound());
-        }
-
-        return new DoubleBounds(range.getLowerBound(), range.getUpperBound());
-    }
-
-
-    /**
-     * Creates a new instance of EnhancedLineAndShapeRenderer.
-     *
-     * @param plot The plot which is set for the new renderer.
-     * @param idx This value is not used in the current implementation.
-     *
-     * @return a new instance of EnhancedLineAndShapeRenderer.
-     */
-    protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) {
-        log.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx);
-
-        EnhancedLineAndShapeRenderer r =
-            new EnhancedLineAndShapeRenderer(true, false);
-
-        r.setPlot(plot);
-
-        return r;
-    }
-
-
     /**
      * Creates a new instance of <i>IdentifiableNumberAxis</i>.
      *
@@ -1490,17 +270,17 @@
      *
      * @return an instance of IdentifiableNumberAxis.
      */
-    protected NumberAxis createNumberAxis(int idx, String label) {
+    protected final NumberAxis createNumberAxis(int idx, String label) {
         YAxisWalker walker = getYAxisWalker();
 
         return new IdentifiableNumberAxis(walker.getId(idx), label);
     }
 
-
     /**
      * Create Y (range) axis for given index.
      * Shall be overriden by subclasses.
      */
+    @Override
     protected NumberAxis createYAxis(int index) {
         YAxisWalker walker = getYAxisWalker();
 
@@ -1523,113 +303,5 @@
         
         return axis;
     }
-
-
-    /**
-     * Creates a new LegendItem with <i>name</i> and font provided by
-     * <i>createLegendLabelFont()</i>.
-     *
-     * @param theme The theme of the chart line.
-     * @param name The displayed name of the item.
-     *
-     * @return a new LegendItem instance.
-     */
-    public LegendItem createLegendItem(ThemeDocument theme, String name) {
-        // OPTIMIZE Pass font, parsed Theme items.
-
-        Color color = theme.parseLineColorField();
-        if (color == null) {
-            color = Color.BLACK;
-        }
-
-        LegendItem legendItem  = new LegendItem(name, color);
-
-        legendItem.setLabelFont(createLegendLabelFont());
-        return legendItem;
-    }
-
-
-    /**
-     * Creates Font (Family and size) to use when creating Legend Items. The
-     * font size depends in the return value of <i>getLegendFontSize()</i>.
-     *
-     * @return a new Font instance with <i>DEFAULT_FONT_NAME</i>.
-     */
-    protected Font createLegendLabelFont() {
-        return new Font(
-            DEFAULT_FONT_NAME,
-            Font.PLAIN,
-            getLegendFontSize()
-        );
-    }
-
-
-    /**
-     * Create new legend entries, dependent on settings.
-     * @param plot The plot for which to modify the legend.
-     */
-    public void aggregateLegendEntries(XYPlot plot) {
-        int AGGR_THRESHOLD = 0;
-
-        if (getChartSettings() == null) {
-            return;
-        }
-        Integer threshold = getChartSettings().getLegendSection()
-            .getAggregationThreshold();
-
-        AGGR_THRESHOLD = (threshold != null) ? threshold.intValue() : 0;
-
-        LegendProcessor.aggregateLegendEntries(plot, AGGR_THRESHOLD);
-    }
-
-
-    /**
-     * Returns a transparently textured paint.
-     *
-     * @return a transparently textured paint.
-     */
-    protected static Paint createTransparentPaint() {
-        // TODO why not use a transparent color?
-        BufferedImage texture = new BufferedImage(
-            1, 1, BufferedImage.TYPE_4BYTE_ABGR);
-
-        return new TexturePaint(
-            texture, new Rectangle2D.Double(0d, 0d, 0d, 0d));
-    }
-
-
-    protected void preparePDFContext(CallContext context) {
-        int[] dimension = getExportDimension();
-
-        context.putContextValue("chart.width", dimension[0]);
-        context.putContextValue("chart.height", dimension[1]);
-        context.putContextValue("chart.marginLeft",   5f);
-        context.putContextValue("chart.marginRight",  5f);
-        context.putContextValue("chart.marginTop",    5f);
-        context.putContextValue("chart.marginBottom", 5f);
-        context.putContextValue(
-            "chart.page.format",
-            ChartExportHelper.DEFAULT_PAGE_SIZE);
-    }
-
-
-    protected void prepareSVGContext(CallContext context) {
-        int[] dimension = getExportDimension();
-
-        context.putContextValue("chart.width", dimension[0]);
-        context.putContextValue("chart.height", dimension[1]);
-        context.putContextValue(
-            "chart.encoding",
-            ChartExportHelper.DEFAULT_ENCODING);
-    }
-
-    /**
-     * Retuns the call context. May be null if init hasn't been called yet.
-     *
-     * @return the CallContext instance
-     */
-    public CallContext getCallContext() {
-        return context;
-    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java	Tue Jun 05 19:21:16 2018 +0200
@@ -8,302 +8,69 @@
 
 package org.dive4elements.river.exports;
 
-import java.awt.BasicStroke;
-import java.awt.Color;
+import static org.dive4elements.river.exports.injector.InjectorConstants.CURRENT_KM;
+
 import java.awt.Font;
 import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.Stroke;
-import java.awt.TexturePaint;
 import java.awt.Transparency;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.text.NumberFormat;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
-import javax.xml.xpath.XPathConstants;
-
-import org.apache.log4j.Logger;
-import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
-import org.dive4elements.artifactdatabase.state.Settings;
-import org.dive4elements.artifacts.Artifact;
-import org.dive4elements.artifacts.ArtifactNamespaceContext;
 import org.dive4elements.artifacts.CallContext;
-import org.dive4elements.artifacts.CallMeta;
-import org.dive4elements.artifacts.PreferredLocale;
 import org.dive4elements.artifacts.common.utils.XMLUtils;
-import org.dive4elements.river.artifacts.D4EArtifact;
-import org.dive4elements.river.artifacts.access.RangeAccess;
-import org.dive4elements.river.artifacts.resources.Resources;
-import org.dive4elements.river.collections.D4EArtifactCollection;
 import org.dive4elements.river.java2d.NOPGraphics2D;
-import org.dive4elements.river.jfree.AxisDataset;
-import org.dive4elements.river.jfree.Bounds;
-import org.dive4elements.river.jfree.DoubleBounds;
-import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer;
 import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.StableXYDifferenceRenderer;
-import org.dive4elements.river.jfree.Style;
-import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
-import org.dive4elements.river.jfree.StyledSeries;
-import org.dive4elements.river.model.River;
-import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.Formatter;
-import org.dive4elements.river.utils.RiverUtils;
 import org.jfree.chart.ChartRenderingInfo;
 import org.jfree.chart.JFreeChart;
-import org.jfree.chart.LegendItem;
-import org.jfree.chart.LegendItemCollection;
 import org.jfree.chart.axis.NumberAxis;
 import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-import org.jfree.chart.title.TextTitle;
 import org.jfree.data.Range;
-import org.jfree.data.general.Series;
-import org.jfree.data.xy.XYDataset;
-import org.jfree.ui.RectangleInsets;
 import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import static org.dive4elements.river.exports.injector.InjectorConstants.CURRENT_KM;
 
 /**
  * Implementation of the OutGenerator interface for charts.
  * It should provide some basic things that equal in all chart types.
  *
  */
-// FIXME: copy/paste of ChartGenerator with LOTS of duplicate code... move duplicates to AbstractChartGenrator!
 public abstract class ChartGenerator2 extends AbstractChartGenerator {
 
-    private static Logger log = Logger.getLogger(ChartGenerator2.class);
-
-    public static final boolean USE_NOP_GRAPHICS =
-        Boolean.getBoolean("info.rendering.nop.graphics");
-
-
-    public static final int    DEFAULT_CHART_WIDTH     = 600;
-    public static final int    DEFAULT_CHART_HEIGHT    = 400;
-    public static final String DEFAULT_CHART_FORMAT    = "png";
-    public static final Color  DEFAULT_GRID_COLOR      = Color.GRAY;
-    public static final float  DEFAULT_GRID_LINE_WIDTH = 0.3f;
-    public static final int    DEFAULT_FONT_SIZE       = 12;
-    public static final String DEFAULT_FONT_NAME       = "Tahoma";
-
-
-    public static final String XPATH_CHART_SIZE =
-        "/art:action/art:attributes/art:size";
-
-    public static final String XPATH_CHART_FORMAT =
-        "/art:action/art:attributes/art:format/@art:value";
-
-    public static final String XPATH_CHART_X_RANGE =
-        "/art:action/art:attributes/art:xrange";
-
-    public static final String XPATH_CHART_Y_RANGE =
-        "/art:action/art:attributes/art:yrange";
-
-    /** The document of the incoming out() request.*/
-    protected Document request;
-
-    /** The output stream where the data should be written to.*/
-    protected OutputStream out;
-
-    /** The CallContext object.*/
-    protected CallContext context;
-
-    protected D4EArtifactCollection collection;
-
-    /** Artifact that is used to decorate the chart with meta information.*/
-    protected Artifact master;
-
-    /** The settings that should be used during output creation.*/
-    protected Settings settings;
-
-    /** Map of datasets ("index"). */
-    protected SortedMap<Integer, AxisDataset> datasets;
+    private static final boolean USE_NOP_GRAPHICS = Boolean.getBoolean("info.rendering.nop.graphics");
 
     /** Map of annotations to add at specific Y-axis. */
-    protected SortedMap<Integer, RiverAnnotation> yAnnotations;
-
-    /** List of annotations to insert in plot. */
-    protected List<RiverAnnotation> annotations =
-        new ArrayList<RiverAnnotation>();
-
-    protected abstract List<AxisSection> buildYAxisSections();
-
-    protected String outName;
+    protected SortedMap<Integer, RiverAnnotation> yAnnotations = new TreeMap<>();
 
     private Map<String, IdentifiableNumberAxis> axisNameToAxis = new HashMap<>();
 
-    /**
-     * Default constructor that initializes internal data structures.
-     */
-    public ChartGenerator2() {
-        datasets = new TreeMap<>();
-        yAnnotations = new TreeMap<>();
-    }
-    
-    @Override
-    protected final D4EArtifact getArtifact() {
-        // FIXME: should already made sure when this member is set
-        return (D4EArtifact) this.master;
-    }
-
-    @Override
-    protected final CallContext getContext() {
-        return this.context;
-    }
-    
-    @Override
-    protected final Document getRequest() {
-        return this.request;
-    }
-
-    /**
-     * Adds annotations to list. The given annotation will be visible.
-     */
-    public void addAnnotations(RiverAnnotation annotation) {
-        annotations.add(annotation);
-    }
-
     public void addYAnnotation(RiverAnnotation annotation, int axisIndex) {
         yAnnotations.put(axisIndex, annotation);
     }
 
-    /**
-     * This method needs to be implemented by concrete subclasses to create new
-     * instances of JFreeChart.
-     *
-     * @return a new instance of a JFreeChart.
-     */
-    public abstract JFreeChart generateChart();
-
-
-    /** For every outable (i.e. facets), this function is
-     * called and handles the data accordingly. */
     @Override
-    public abstract void doOut(
-        ArtifactAndFacet bundle,
-        ThemeDocument    attr,
-        boolean          visible);
-
-
-
-    protected abstract Series getSeriesOf(XYDataset dataset, int idx);
-
-    /**
-     * Returns the default title of a chart.
-     *
-     * @return the default title of a chart.
-     */
-    protected abstract String getDefaultChartTitle();
-
-    protected abstract String getDefaultYAxisLabel(String axisName);
-
-
-    /**
-     * Returns the default X-Axis label of a chart.
-     *
-     * @return the default X-Axis label of a chart.
-     */
-    protected abstract String getDefaultXAxisLabel();
-
-    /**
-     * This method is used to create new AxisDataset instances which may differ
-     * in concrete subclasses.
-     *
-     * @param idx The index of an axis.
-     */
-    protected abstract AxisDataset createAxisDataset(int idx);
-
-
-    /**
-     * Combines the ranges of the X axis at index <i>idx</i>.
-     *
-     * @param bounds A new Bounds.
-     * @param idx The index of the X axis that should be comined with
-     * <i>range</i>.
-     */
-    protected abstract void combineXBounds(Bounds bounds, int idx);
-
-
-    /**
-     * Combines the ranges of the Y axis at index <i>idx</i>.
-     *
-     * @param bounds A new Bounds.
-     * @param index The index of the Y axis that should be comined with.
-     * <i>range</i>.
-     */
-    protected abstract void combineYBounds(Bounds bounds, int index);
-
-
-    /**
-     * This method is used to determine the ranges for axes at a given index.
-     *
-     * @param index The index of the axes at the plot.
-     *
-     * @return a Range[] with [xrange, yrange];
-     */
-    public abstract Range[] getRangesForAxis(int index);
-
-    public abstract Bounds getXBounds(int axis);
-
-    protected abstract void setXBounds(int axis, Bounds bounds);
-
-    public abstract Bounds getYBounds(int axis);
-
-    protected abstract void setYBounds(int axis, Bounds bounds);
-
-
-    /**
-     * This method retrieves the chart subtitle by calling getChartSubtitle()
-     * and adds it as TextTitle to the chart.
-     * The default implementation of getChartSubtitle() returns the same
-     * as getDefaultChartSubtitle() which must be implemented by derived
-     * classes. If you want to add multiple subtitles to the chart override
-     * this method and add your subtitles manually.
-     *
-     * @param chart The JFreeChart chart object.
-     */
-    protected void addSubtitles(JFreeChart chart) {
-        String subtitle = getChartSubtitle();
-
-        if (subtitle != null && subtitle.length() > 0) {
-            chart.addSubtitle(new TextTitle(subtitle));
-        }
-    }
-    
-    /**
-     * Generate chart.
-     */
-    @Override
-    public void generate() throws IOException {
-
+    protected void doGenerate(final CallContext context, final OutputStream out, final String outName) throws IOException {
         log.debug("ChartGenerator2.generate");
 
-        if (outName.indexOf("chartinfo") > 0) {
-            generateInfo();
-        }
-        else {
-            generateImage();
-        }
+        if (outName.indexOf("chartinfo") > 0)
+            generateInfo(context, out);
+        else 
+            generateImage(context);
     }
 
-
-    /** Generate only meta infos */
-    private void generateInfo() throws IOException {
+    /** 
+     * Generate only meta infos 
+     */
+    private void generateInfo(final CallContext context, final OutputStream out) {
 
         log.debug("ChartInfoGenerator2.generateInfo");
 
-        JFreeChart chart = generateChart();
+        JFreeChart chart = generateChart(context);
 
         int[] size = getSize();
         if (size == null) {
@@ -348,142 +115,16 @@
         XMLUtils.toStream(doc, out);
     }
 
-    /** Generate the diagram as an image. */
-    private void generateImage() throws IOException {
-        log.debug("ChartGenerator2.generateImage");
-
-        JFreeChart chart = generateChart();
-
-        String format = getFormat();
-        int[]  size   = getSize();
-
-        if (size == null) {
-            size = getExportDimension();
-        }
-
-        context.putContextValue("chart.width",  size[0]);
-        context.putContextValue("chart.height", size[1]);
-
-        if (format.equals(ChartExportHelper.FORMAT_PNG)) {
-            context.putContextValue("chart.image.format", "png");
-
-            ChartExportHelper.exportImage(
-                out,
-                chart,
-                context);
-        }
-        else if (format.equals(ChartExportHelper.FORMAT_PDF)) {
-            preparePDFContext(context);
-
-            ChartExportHelper.exportPDF(
-                out,
-                chart,
-                context);
-        }
-        else if (format.equals(ChartExportHelper.FORMAT_SVG)) {
-            prepareSVGContext(context);
-
-            ChartExportHelper.exportSVG(
-                out,
-                chart,
-                context);
-        }
-        else if (format.equals(ChartExportHelper.FORMAT_CSV)) {
-            context.putContextValue("chart.image.format", "csv");
-
-            ChartExportHelper.exportCSV(
-                out,
-                chart,
-                context);
-        }
-    }
-
-
-    @Override
-    public void init(
-        String outName,
-        Document request,
-        OutputStream out,
-        CallContext context
-    ) {
-        log.debug("ChartGenerator2.init");
-
-        this.outName = outName;
-        this.request = request;
-        this.out     = out;
-        this.context = context;
-    }
-
-
-    /** Sets the master artifact. */
-    @Override
-    public void setMasterArtifact(Artifact master) {
-        this.master = master;
-    }
-
-
-    /**
-     * Gets the master artifact.
-     * @return the master artifact.
-     */
-    public Artifact getMaster() {
-        return master;
-    }
-
-
-    /** Sets the collection. */
-    @Override
-    public void setCollection(D4EArtifactCollection collection) {
-        this.collection = collection;
-    }
-
-
-    @Override
-    public void setSettings(Settings settings) {
-        this.settings = settings;
-    }
-
-
-    /**
-     * Return instance of <i>ChartSettings</i> with a chart specific section
-     * but with no axes settings.
-     *
-     * @return an instance of <i>ChartSettings</i>.
-     */
-    @Override
-    public Settings getSettings() {
-        if (this.settings != null) {
-            return this.settings;
-        }
-
-        ChartSettings settings = new ChartSettings();
-
-        ChartSection  chartSection  = buildChartSection();
-        LegendSection legendSection = buildLegendSection();
-        ExportSection exportSection = buildExportSection();
-
-        settings.setChartSection(chartSection);
-        settings.setLegendSection(legendSection);
-        settings.setExportSection(exportSection);
-
-        List<AxisSection> axisSections = buildAxisSections();
-        for (AxisSection axisSection: axisSections) {
-            settings.addAxisSection(axisSection);
-        }
-
-        return settings;
-    }
-
-
     /**
      * Creates a new <i>ChartSection</i>.
      *
      * @return a new <i>ChartSection</i>.
      */
-    protected ChartSection buildChartSection() {
+    @Override
+    protected ChartSection buildChartSection(final CallContext context) {
         ChartSection chartSection = new ChartSection();
-        chartSection.setTitle(getChartTitle());
-        chartSection.setSubtitle(getChartSubtitlePure());
+        chartSection.setTitle(getChartTitle(context));
+        chartSection.setSubtitle(getChartSubtitlePure(context));
         chartSection.setDisplayGrid(isGridVisible());
         chartSection.setDisplayLogo(showLogo());
         chartSection.setLogoVPlacement(logoVPlace());
@@ -491,198 +132,7 @@
         return chartSection;
     }
 
-
-    /**
-     * Creates a new <i>LegendSection</i>.
-     *
-     * @return a new <i>LegendSection</i>.
-     */
-    protected LegendSection buildLegendSection() {
-        LegendSection legendSection = new LegendSection();
-        legendSection.setVisibility(isLegendVisible());
-        legendSection.setFontSize(getLegendFontSize());
-        legendSection.setAggregationThreshold(10);
-        return legendSection;
-    }
-
-
-    /**
-     * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b>
-     * and <b>HEIGHT=400</b>.
-     *
-     * @return a new <i>ExportSection</i>.
-     */
-    protected ExportSection buildExportSection() {
-        ExportSection exportSection = new ExportSection();
-        exportSection.setWidth(600);
-        exportSection.setHeight(400);
-        return exportSection;
-    }
-
-
-    /**
-     * Creates a list of Sections that contains all axes of the chart (including
-     * X and Y axes).
-     *
-     * @return a list of Sections for each axis in this chart.
-     */
-    protected List<AxisSection> buildAxisSections() {
-        List<AxisSection> axisSections = new ArrayList<AxisSection>();
-
-        axisSections.addAll(buildXAxisSections());
-        axisSections.addAll(buildYAxisSections());
-
-        return axisSections;
-    }
-
-
-    /**
-     * Creates a new Section for chart's X axis.
-     *
-     * @return a List that contains a Section for the X axis.
-     */
-    protected List<AxisSection> buildXAxisSections() {
-        List<AxisSection> axisSections = new ArrayList<AxisSection>();
-
-        String identifier = "X";
-
-        AxisSection axisSection = new AxisSection();
-        axisSection.setIdentifier(identifier);
-        axisSection.setLabel(getXAxisLabel());
-        axisSection.setFontSize(14);
-        axisSection.setFixed(false);
-
-        // XXX We are able to find better default ranges that [0,0], but the Y
-        // axes currently have no better ranges set.
-        axisSection.setUpperRange(0d);
-        axisSection.setLowerRange(0d);
-
-        axisSections.add(axisSection);
-
-        return axisSections;
-    }
-
-
-    /**
-     * Returns the <i>settings</i> as <i>ChartSettings</i>.
-     *
-     * @return the <i>settings</i> as <i>ChartSettings</i> or null, if
-     * <i>settings</i> is not an instance of <i>ChartSettings</i>.
-     */
-    public ChartSettings getChartSettings() {
-        if (settings instanceof ChartSettings) {
-            return (ChartSettings) settings;
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Returns the chart title provided by <i>settings</i>.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return the title provided by <i>settings</i> or null if no
-     * <i>ChartSection</i> is provided by <i>settings</i>.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public String getChartTitle(ChartSettings settings) {
-        ChartSection cs = settings.getChartSection();
-        return cs != null ? cs.getTitle() : null;
-    }
-
-
-    /**
-     * Returns the chart subtitle provided by <i>settings</i>.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return the subtitle provided by <i>settings</i> or null if no
-     * <i>ChartSection</i> is provided by <i>settings</i>.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public String getChartSubtitle(ChartSettings settings) {
-        ChartSection cs = settings.getChartSection();
-        return cs != null ? cs.getSubtitle() : null;
-    }
-
-
-    /**
-     * Returns a boolean object that determines if the chart grid should be
-     * visible or not. This information needs to be provided by <i>settings</i>,
-     * otherweise the default is true.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return true, if the chart grid should be visible otherwise false.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public boolean isGridVisible(ChartSettings settings) {
-        ChartSection     cs = settings.getChartSection();
-        Boolean displayGrid = cs.getDisplayGrid();
-
-        return displayGrid != null ? displayGrid : true;
-    }
-
-
-    /**
-     * Returns a boolean object that determines if the chart legend should be
-     * visible or not. This information needs to be provided by <i>settings</i>,
-     * otherwise the default is true.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return true, if the chart legend should be visible otherwise false.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public boolean isLegendVisible(ChartSettings settings) {
-        LegendSection      ls = settings.getLegendSection();
-        Boolean displayLegend = ls.getVisibility();
-
-        return displayLegend != null ? displayLegend : true;
-    }
-
-
-    /**
-     * Returns the legend font size specified in <i>settings</i> or null if no
-     * <i>LegendSection</i> is provided by <i>settings</i>.
-     *
-     * @param settings A ChartSettings object.
-     *
-     * @return the legend font size or null.
-     *
-     * @throws NullPointerException if <i>settings</i> is null.
-     */
-    public Integer getLegendFontSize(ChartSettings settings) {
-        LegendSection ls = settings.getLegendSection();
-        return ls != null ? ls.getFontSize() : null;
-    }
-
-
-    /**
-     * Returns the title of a chart. The return value depends on the existence
-     * of ChartSettings: if there are ChartSettings set, this method returns the
-     * chart title provided by those settings. Otherwise, this method returns
-     * getDefaultChartTitle().
-     *
-     * @return the title of a chart.
-     */
-    protected String getChartTitle() {
-        ChartSettings chartSettings = getChartSettings();
-
-        if (chartSettings != null) {
-            return getChartTitle(chartSettings);
-        }
-
-        return getDefaultChartTitle();
-    }
-
-    protected String interpolateVariables(String s) {
+    protected String interpolateVariables(final CallContext context, String s) {
         log.debug("Interpolate variables in string '" + s + "'");
         Object radius = context.getContextValue("radius");
         if (radius instanceof Double) {
@@ -707,17 +157,18 @@
      * existence of ChartSettings: if there are ChartSettings set, this method
      * returns the chart title provided by those settings. Otherwise, this
      * method returns getDefaultChartSubtitle().
+     * @param context 
      *
      * @return the subtitle of a chart.
      */
-    protected String getChartSubtitlePure() {
+    protected String getChartSubtitlePure(CallContext context) {
         ChartSettings chartSettings = getChartSettings();
 
         String subTitle = chartSettings != null
             ? getChartSubtitle(chartSettings)
-            : getDefaultChartSubtitle();
+            : getDefaultChartSubtitle(context);
 
-        String defSubTitle = getDefaultChartSubtitle();
+        String defSubTitle = getDefaultChartSubtitle(context);
 
         if (subTitle == null) {
             subTitle = defSubTitle != null ? defSubTitle : "";
@@ -726,162 +177,9 @@
         return subTitle;
     }
 
-    protected String getChartSubtitle() {
-        return interpolateVariables(getChartSubtitlePure());
-    }
-
-
-    /**
-     * This method always returns null. Override it in subclasses that require
-     * subtitles.
-     *
-     * @return null.
-     */
-    protected String getDefaultChartSubtitle() {
-        // Override this method in subclasses
-        return null;
-    }
-
-
-    /**
-     * This method is used to determine, if the chart's legend is visible or
-     * not. If a <i>settings</i> instance is set, this instance determines the
-     * visibility otherwise, this method returns true as default if no
-     * <i>settings</i> is set.
-     *
-     * @return true, if the legend should be visible, otherwise false.
-     */
-    protected boolean isLegendVisible() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings != null) {
-            return isLegendVisible(chartSettings);
-        }
-
-        return true;
-    }
-
-
-    /** Where to place the logo. */
-    protected String logoHPlace() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings != null) {
-            ChartSection cs    = chartSettings.getChartSection();
-            String       place = cs.getLogoHPlacement();
-
-            return place;
-        }
-        return "center";
-    }
-
-
-    /** Where to place the logo. */
-    protected String logoVPlace() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings != null) {
-            ChartSection cs    = chartSettings.getChartSection();
-            String       place = cs.getLogoVPlacement();
-
-            return place;
-        }
-        return "top";
-    }
-
-
-    /** Return the logo id from settings. */
-    protected String showLogo(ChartSettings chartSettings) {
-        if (chartSettings != null) {
-            ChartSection cs   = chartSettings.getChartSection();
-            String       logo = cs.getDisplayLogo();
-
-            return logo;
-        }
-        return "none";
-    }
-
-
-    /**
-     * This method is used to determine if a logo should be added to the plot.
-     *
-     * @return logo name (null if none).
-     */
-    protected String showLogo() {
-        ChartSettings chartSettings = getChartSettings();
-        return showLogo(chartSettings);
-    }
-
-
-    /**
-     * This method is used to determine the font size of the chart's legend. If
-     * a <i>settings</i> instance is set, this instance determines the font
-     * size, otherwise this method returns 12 as default if no <i>settings</i>
-     * is set or if it doesn't provide a legend font size.
-     *
-     * @return a legend font size.
-     */
-    protected int getLegendFontSize() {
-        Integer fontSize = null;
-
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings != null) {
-            fontSize = getLegendFontSize(chartSettings);
-        }
-
-        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
-    }
-
-
-    /**
-     * This method is used to determine if the resulting chart should display
-     * grid lines or not. <b>Note: this method always returns true!</b>
-     *
-     * @return true, if the chart should display grid lines, otherwise false.
-     */
-    protected boolean isGridVisible() {
-        return true;
-    }
-
-
-    /**
-     * Returns the X-Axis label of a chart.
-     *
-     * @return the X-Axis label of a chart.
-     */
-    protected String getXAxisLabel() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings == null) {
-            return getDefaultXAxisLabel();
-        }
-
-        AxisSection as = chartSettings.getAxisSection("X");
-        if (as != null) {
-            String label = as.getLabel();
-
-            if (label != null) {
-                return label;
-            }
-        }
-
-        return getDefaultXAxisLabel();
-    }
-
-
-    /**
-     * This method returns the font size for the X axis. If the font size is
-     * specified in ChartSettings (if <i>chartSettings</i> is set), this size is
-     * returned. Otherwise the default font size 12 is returned.
-     *
-     * @return the font size for the x axis.
-     */
-    protected int getXAxisLabelFontSize() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings == null) {
-            return DEFAULT_FONT_SIZE;
-        }
-
-        AxisSection   as = chartSettings.getAxisSection("X");
-        Integer fontSize = as.getFontSize();
-
-        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
+    @Override
+    protected String getChartSubtitle(CallContext context) {
+        return interpolateVariables(context, getChartSubtitlePure(context));
     }
 
     /**
@@ -911,29 +209,6 @@
         return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
     }
 
-    /**
-     * This method returns the export dimension specified in ChartSettings as
-     * int array [width,height].
-     *
-     * @return an int array with [width,height].
-     */
-    protected int[] getExportDimension() {
-        ChartSettings chartSettings = getChartSettings();
-        if (chartSettings == null) {
-            return new int[] { 600, 400 };
-        }
-
-        ExportSection export = chartSettings.getExportSection();
-        Integer width  = export.getWidth();
-        Integer height = export.getHeight();
-
-        if (width != null && height != null) {
-            return new int[] { width, height };
-        }
-
-        return new int[] { 600, 400 };
-    }
-
     protected abstract String getYAxisLabel(String axisName);
 
     /**
@@ -959,9 +234,7 @@
             return null;
         }
 
-        Boolean  fixed = as.isFixed();
-
-        if (fixed != null && fixed) {
+        if (as.isFixed()) {
             Double upper = as.getUpperRange();
             Double lower = as.getLowerRange();
 
@@ -975,486 +248,6 @@
         return null;
     }
 
-
-    /**
-     * Adds a new AxisDataset which contains <i>dataset</i> at index <i>idx</i>.
-     *
-     * @param dataset An XYDataset.
-     * @param idx The axis index.
-     * @param visible Determines, if the dataset should be visible or not.
-     */
-    public void addAxisDataset(XYDataset dataset, int idx, boolean visible) {
-        if (dataset == null || idx < 0) {
-            return;
-        }
-
-        AxisDataset axisDataset = getAxisDataset(idx);
-
-        Bounds[] xyBounds = ChartHelper.getBounds(dataset);
-
-        if (xyBounds == null) {
-            log.warn("Skip XYDataset for Axis (invalid ranges): " + idx);
-            return;
-        }
-
-        if (visible) {
-            if (log.isDebugEnabled()) {
-                log.debug("Add new AxisDataset at index: " + idx);
-                log.debug("X extent: " + xyBounds[0]);
-                log.debug("Y extent: " + xyBounds[1]);
-            }
-
-            axisDataset.addDataset(dataset);
-        }
-
-        combineXBounds(xyBounds[0], 0);
-        combineYBounds(xyBounds[1], idx);
-    }
-
-
-    /**
-     * This method grants access to the AxisDatasets stored in <i>datasets</i>.
-     * If no AxisDataset exists for index <i>idx</i>, a new AxisDataset is
-     * created using <i>createAxisDataset()</i>.
-     *
-     * @param idx The index of the desired AxisDataset.
-     *
-     * @return an existing or new AxisDataset.
-     */
-    public AxisDataset getAxisDataset(int idx) {
-        AxisDataset axisDataset = datasets.get(idx);
-
-        if (axisDataset == null) {
-            axisDataset = createAxisDataset(idx);
-            datasets.put(idx, axisDataset);
-        }
-
-        return axisDataset;
-    }
-
-
-    /**
-     * Adjust some Stroke/Grid parameters for <i>plot</i>. The chart
-     * <i>Settings</i> are applied in this method.
-     *
-     * @param plot The XYPlot which is adapted.
-     */
-    protected void adjustPlot(XYPlot plot) {
-        Stroke gridStroke = new BasicStroke(
-            DEFAULT_GRID_LINE_WIDTH,
-            BasicStroke.CAP_BUTT,
-            BasicStroke.JOIN_MITER,
-            3.0f,
-            new float[] { 3.0f },
-            0.0f);
-
-        ChartSettings      cs = getChartSettings();
-        boolean isGridVisible = cs != null ? isGridVisible(cs) : true;
-
-        plot.setDomainGridlineStroke(gridStroke);
-        plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR);
-        plot.setDomainGridlinesVisible(isGridVisible);
-
-        plot.setRangeGridlineStroke(gridStroke);
-        plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR);
-        plot.setRangeGridlinesVisible(isGridVisible);
-
-        plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d));
-    }
-
-
-    /**
-     * This helper mehtod is used to extract the current locale from instance
-     * vairable <i>context</i>.
-     *
-     * @return the current locale.
-     */
-    protected Locale getLocale() {
-        CallMeta           meta = context.getMeta();
-        PreferredLocale[] prefs = meta.getLanguages();
-
-        int len = prefs != null ? prefs.length : 0;
-
-        Locale[] locales = new Locale[len];
-
-        for (int i = 0; i < len; i++) {
-            locales[i] = prefs[i].getLocale();
-        }
-
-        return meta.getPreferredLocale(locales);
-    }
-
-
-    /**
-     * Look up \param key in i18n dictionary.
-     * @param key key for which to find i18nd version.
-     * @param def default, returned if lookup failed.
-     * @return value found in i18n dictionary, \param def if no value found.
-     */
-    public String msg(String key, String def) {
-        return Resources.getMsg(context.getMeta(), key, def);
-    }
-
-    /**
-     * Look up \param key in i18n dictionary.
-     * @param key key for which to find i18nd version.
-     * @return value found in i18n dictionary, key itself if failed.
-     */
-    public String msg(String key) {
-        return Resources.getMsg(context.getMeta(), key, key);
-    }
-
-    public String msg(String key, String def, Object[] args) {
-        return Resources.getMsg(context.getMeta(), key, def, args);
-    }
-
-    /**
-     * Returns the size of a chart export as array which has been specified by
-     * the incoming request document.
-     *
-     * @return the size of a chart as [width, height] or null if no width or
-     * height are given in the request document.
-     */
-    protected int[] getSize() {
-        int[] size = new int[2];
-
-        Element sizeEl = (Element)XMLUtils.xpath(
-            request,
-            XPATH_CHART_SIZE,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (sizeEl != null) {
-            String uri = ArtifactNamespaceContext.NAMESPACE_URI;
-
-            String w = sizeEl.getAttributeNS(uri, "width");
-            String h = sizeEl.getAttributeNS(uri, "height");
-
-            if (w.length() > 0 && h.length() > 0) {
-                try {
-                    size[0] = Integer.parseInt(w);
-                    size[1] = Integer.parseInt(h);
-                }
-                catch (NumberFormatException nfe) {
-                    log.warn("Wrong values for chart width/height.");
-                }
-            }
-        }
-
-        return size[0] > 0 && size[1] > 0 ? size : null;
-    }
-
-
-    /**
-     * This method returns the format specified in the <i>request</i> document
-     * or <i>DEFAULT_CHART_FORMAT</i> if no format is specified in
-     * <i>request</i>.
-     *
-     * @return the format used to export this chart.
-     */
-    protected String getFormat() {
-        String format = (String) XMLUtils.xpath(
-            request,
-            XPATH_CHART_FORMAT,
-            XPathConstants.STRING,
-            ArtifactNamespaceContext.INSTANCE);
-
-        return format == null || format.length() == 0
-            ? DEFAULT_CHART_FORMAT
-            : format;
-    }
-
-    /**
-     * Returns the X-Axis range as String array from request document.
-     * If the (x|y)range elements are not found in request document, return
-     * null (i.e. not zoomed).
-     *
-     * @return a String array with [lower, upper], null if not in document.
-     */
-    protected String[] getDomainAxisRangeFromRequest() {
-        Element xrange = (Element)XMLUtils.xpath(
-            request,
-            XPATH_CHART_X_RANGE,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (xrange == null) {
-            return null;
-        }
-
-        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
-
-        String lower = xrange.getAttributeNS(uri, "from");
-        String upper = xrange.getAttributeNS(uri, "to");
-
-        return new String[] { lower, upper };
-    }
-
-
-    /** Returns null if the (x|y)range-element was not found in
-     *request document.
-     * This usally means that the axis are not manually zoomed, i.e. showing
-     * full data extent. */
-    protected String[] getValueAxisRangeFromRequest() {
-        Element yrange = (Element)XMLUtils.xpath(
-            request,
-            XPATH_CHART_Y_RANGE,
-            XPathConstants.NODE,
-            ArtifactNamespaceContext.INSTANCE);
-
-        if (yrange == null) {
-            return null;
-        }
-
-
-        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
-
-        String lower = yrange.getAttributeNS(uri, "from");
-        String upper = yrange.getAttributeNS(uri, "to");
-
-        return new String[] { lower, upper };
-    }
-
-
-    /**
-     * Returns the default size of a chart export as array.
-     *
-     * @return the default size of a chart as [width, height].
-     */
-    protected int[] getDefaultSize() {
-        return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT };
-    }
-
-
-    /**
-     * Add datasets stored in instance variable <i>datasets</i> to plot.
-     * <i>datasets</i> actually stores instances of AxisDataset, so each of this
-     * datasets is mapped to a specific axis as well.
-     *
-     * @param plot plot to add datasets to.
-     */
-    protected void addDatasets(XYPlot plot) {
-        log.debug("addDatasets()");
-
-        // AxisDatasets are sorted, but some might be empty.
-        // Thus, generate numbering on the fly.
-        int axisIndex    = 0;
-        int datasetIndex = 0;
-
-        for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) {
-            if (!entry.getValue().isEmpty()) {
-                // Add axis and range information.
-                AxisDataset axisDataset = entry.getValue();
-                NumberAxis  axis        = createYAxis(entry.getKey());
-
-                plot.setRangeAxis(axisIndex, axis);
-
-                if (axis.getAutoRangeIncludesZero()) {
-                    axisDataset.setRange(
-                        Range.expandToInclude(axisDataset.getRange(), 0d));
-                }
-
-                setYBounds(axisIndex, expandPointRange(axisDataset.getRange()));
-
-                // Add contained datasets, mapping to axis.
-                for (XYDataset dataset: axisDataset.getDatasets()) {
-                    try {
-                        plot.setDataset(datasetIndex, dataset);
-                        plot.mapDatasetToRangeAxis(datasetIndex, axisIndex);
-
-                        applyThemes(plot, dataset,
-                            datasetIndex,
-                            axisDataset.isArea(dataset));
-
-                        datasetIndex++;
-                    }
-                    catch (RuntimeException re) {
-                        log.error(re);
-                    }
-                }
-
-                axisDataset.setPlotAxisIndex(axisIndex);
-                axisIndex++;
-            }
-        }
-    }
-
-
-    /**
-     * @param idx "index" of dataset/series (first dataset to be drawn has
-     *            index 0), correlates with renderer index.
-     * @param isArea true if the series describes an area and shall be rendered
-     *                as such.
-     */
-    protected void applyThemes(
-        XYPlot    plot,
-        XYDataset series,
-        int       idx,
-        boolean   isArea
-    ) {
-        if (isArea) {
-            applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx);
-        }
-        else {
-            applyLineTheme(plot, series, idx);
-        }
-    }
-
-
-    /**
-     * This method applies the themes defined in the series itself. Therefore,
-     * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer
-     * for the series.
-     *
-     * @param plot The plot.
-     * @param dataset The XYDataset which needs to support Series objects.
-     * @param idx The index of the renderer / dataset.
-     */
-    protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) {
-        log.debug("Apply LineTheme for dataset at index: " + idx);
-
-        LegendItemCollection lic  = new LegendItemCollection();
-        LegendItemCollection anno = plot.getFixedLegendItems();
-
-        Font legendFont = createLegendLabelFont();
-
-        XYLineAndShapeRenderer renderer = createRenderer(plot, idx);
-
-        for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) {
-            Series series = getSeriesOf(dataset, s);
-
-            if (series instanceof StyledSeries) {
-                Style style = ((StyledSeries) series).getStyle();
-                style.applyTheme(renderer, s);
-            }
-
-            // special case: if there is just one single item, we need to enable
-            // points for this series, otherwise we would not see anything in
-            // the chart area.
-            if (series.getItemCount() == 1) {
-                renderer.setSeriesShapesVisible(s, true);
-            }
-
-            LegendItem legendItem = renderer.getLegendItem(idx, s);
-            if (legendItem.getLabel().endsWith(" ") ||
-                legendItem.getLabel().endsWith("interpol")) {
-                legendItem = null;
-            }
-
-            if (legendItem != null) {
-                legendItem.setLabelFont(legendFont);
-                lic.add(legendItem);
-            }
-            else {
-                log.warn("Could not get LegentItem for renderer: "
-                    + idx + ", series-idx " + s);
-            }
-        }
-
-        if (anno != null) {
-            lic.addAll(anno);
-        }
-
-        plot.setFixedLegendItems(lic);
-
-        plot.setRenderer(idx, renderer);
-    }
-
-
-    /**
-     * @param plot The plot.
-     * @param area A StyledAreaSeriesCollection object.
-     * @param idx The index of the dataset.
-     */
-    protected void applyAreaTheme(
-        XYPlot                     plot,
-        StyledAreaSeriesCollection area,
-        int                        idx
-    ) {
-        LegendItemCollection lic  = new LegendItemCollection();
-        LegendItemCollection anno = plot.getFixedLegendItems();
-
-        Font legendFont = createLegendLabelFont();
-
-        log.debug("Registering an 'area'renderer at idx: " + idx);
-
-        StableXYDifferenceRenderer dRenderer =
-            new StableXYDifferenceRenderer();
-
-        if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) {
-            dRenderer.setPositivePaint(createTransparentPaint());
-        }
-
-        plot.setRenderer(idx, dRenderer);
-
-        area.applyTheme(dRenderer);
-
-        // i18n
-        dRenderer.setAreaLabelNumberFormat(
-            Formatter.getFormatter(context.getMeta(), 2, 4));
-
-        dRenderer.setAreaLabelTemplate(Resources.getMsg(
-            context.getMeta(), "area.label.template", "Area=%sm2"));
-
-        LegendItem legendItem = dRenderer.getLegendItem(idx, 0);
-        if (legendItem != null) {
-            legendItem.setLabelFont(legendFont);
-            lic.add(legendItem);
-        }
-        else {
-            log.warn("Could not get LegentItem for renderer: "
-                + idx + ", series-idx " + 0);
-        }
-
-        if (anno != null) {
-            lic.addAll(anno);
-        }
-
-        plot.setFixedLegendItems(lic);
-    }
-
-
-    /**
-     * Expands a given range if it collapses into one point.
-     *
-     * @param range Range to be expanded if upper == lower bound.
-     *
-     * @return Bounds of point plus 5 percent in each direction.
-     */
-    private Bounds expandPointRange(Range range) {
-        if (range == null) {
-            return null;
-        }
-        else if (range.getLowerBound() == range.getUpperBound()) {
-            Range expandedRange = ChartHelper.expandRange(range, 5d);
-            return new DoubleBounds(
-                expandedRange.getLowerBound(), expandedRange.getUpperBound());
-        }
-
-        return new DoubleBounds(range.getLowerBound(), range.getUpperBound());
-    }
-
-
-    /**
-     * Creates a new instance of EnhancedLineAndShapeRenderer.
-     *
-     * @param plot The plot which is set for the new renderer.
-     * @param idx This value is not used in the current implementation.
-     *
-     * @return a new instance of EnhancedLineAndShapeRenderer.
-     */
-    protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) {
-        log.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx);
-
-        EnhancedLineAndShapeRenderer r =
-            new EnhancedLineAndShapeRenderer(true, false);
-
-        r.setPlot(plot);
-
-        return r;
-    }
-
-
     /**
      * Creates a new instance of <i>IdentifiableNumberAxis</i>.
      *
@@ -1463,15 +256,15 @@
      *
      * @return an instance of IdentifiableNumberAxis.
      */
-    protected NumberAxis createNumberAxis(int idx, String label) {
+    protected final NumberAxis createNumberAxis(int idx, String label) {
         return new IdentifiableNumberAxis(axisIndexToName(idx), label);
     }
 
-
     /**
      * Create Y (range) axis for given index.
      * Shall be overriden by subclasses.
      */
+    @Override
     protected NumberAxis createYAxis(int index) {
 
         Font labelFont = new Font(
@@ -1496,114 +289,6 @@
         return axis;
     }
 
-
-    /**
-     * Creates a new LegendItem with <i>name</i> and font provided by
-     * <i>createLegendLabelFont()</i>.
-     *
-     * @param theme The theme of the chart line.
-     * @param name The displayed name of the item.
-     *
-     * @return a new LegendItem instance.
-     */
-    public LegendItem createLegendItem(ThemeDocument theme, String name) {
-        // OPTIMIZE Pass font, parsed Theme items.
-
-        Color color = theme.parseLineColorField();
-        if (color == null) {
-            color = Color.BLACK;
-        }
-
-        LegendItem legendItem  = new LegendItem(name, color);
-
-        legendItem.setLabelFont(createLegendLabelFont());
-        return legendItem;
-    }
-
-
-    /**
-     * Creates Font (Family and size) to use when creating Legend Items. The
-     * font size depends in the return value of <i>getLegendFontSize()</i>.
-     *
-     * @return a new Font instance with <i>DEFAULT_FONT_NAME</i>.
-     */
-    protected Font createLegendLabelFont() {
-        return new Font(
-            DEFAULT_FONT_NAME,
-            Font.PLAIN,
-            getLegendFontSize()
-        );
-    }
-
-
-    /**
-     * Create new legend entries, dependent on settings.
-     * @param plot The plot for which to modify the legend.
-     */
-    public void aggregateLegendEntries(XYPlot plot) {
-        int AGGR_THRESHOLD = 0;
-
-        if (getChartSettings() == null) {
-            return;
-        }
-        Integer threshold = getChartSettings().getLegendSection()
-            .getAggregationThreshold();
-
-        AGGR_THRESHOLD = (threshold != null) ? threshold.intValue() : 0;
-
-        LegendProcessor.aggregateLegendEntries(plot, AGGR_THRESHOLD);
-    }
-
-
-    /**
-     * Returns a transparently textured paint.
-     *
-     * @return a transparently textured paint.
-     */
-    protected static Paint createTransparentPaint() {
-        // TODO why not use a transparent color?
-        BufferedImage texture = new BufferedImage(
-            1, 1, BufferedImage.TYPE_4BYTE_ABGR);
-
-        return new TexturePaint(
-            texture, new Rectangle2D.Double(0d, 0d, 0d, 0d));
-    }
-
-
-    protected void preparePDFContext(CallContext context) {
-        int[] dimension = getExportDimension();
-
-        context.putContextValue("chart.width", dimension[0]);
-        context.putContextValue("chart.height", dimension[1]);
-        context.putContextValue("chart.marginLeft",   5f);
-        context.putContextValue("chart.marginRight",  5f);
-        context.putContextValue("chart.marginTop",    5f);
-        context.putContextValue("chart.marginBottom", 5f);
-        context.putContextValue(
-            "chart.page.format",
-            ChartExportHelper.DEFAULT_PAGE_SIZE);
-    }
-
-
-    protected void prepareSVGContext(CallContext context) {
-        int[] dimension = getExportDimension();
-
-        context.putContextValue("chart.width", dimension[0]);
-        context.putContextValue("chart.height", dimension[1]);
-        context.putContextValue(
-            "chart.encoding",
-            ChartExportHelper.DEFAULT_ENCODING);
-    }
-
-    /**
-     * Retuns the call context. May be null if init hasn't been called yet.
-     *
-     * @return the CallContext instance
-     */
-    public CallContext getCallContext() {
-        return context;
-    }
-    
     public final IdentifiableNumberAxis getAxis(final String axisName) {
         return axisNameToAxis.get(axisName);
     }
@@ -1612,4 +297,9 @@
     public final int getNumYAxes() {
         return axisNameToAxis.size();
     }
+    
+    protected final void addYAnnotationsToRenderer(final XYPlot plot) {
+        final AnnotationRenderer annotationRenderer = new AnnotationRenderer(getChartSettings(), getDatasets(), DEFAULT_FONT_NAME);
+        annotationRenderer.addYAnnotationsToRenderer(plot, this.yAnnotations);
+    }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -69,6 +69,7 @@
         this.generator = generator;
     }
 
+    @Override
     public void setup(Object config) {
         log.debug("ChartInfoGenerator.setup");
     }
@@ -81,6 +82,7 @@
      * @param out
      * @param context
      */
+    @Override
     public void init(
         String outName,
         Document request,
@@ -98,21 +100,21 @@
      *
      * @param master The master artifact
      */
+    @Override
     public void setMasterArtifact(Artifact master) {
         generator.setMasterArtifact(master);
     }
 
-
     /**
      * Dispatches the operation to the instantiated generator.
      *
      * @param collection The collection.
      */
+    @Override
     public void setCollection(D4EArtifactCollection collection) {
         generator.setCollection(collection);
     }
-
-
+    
     /**
      * Dispatches the operation to the instantiated generator.
      */
@@ -137,7 +139,7 @@
     {
         log.debug("ChartInfoGenerator.generate");
 
-        JFreeChart chart = generator.generateChart();
+        JFreeChart chart = generator.generateChart(generator.getContext());
 
         int[] size = generator.getSize();
         if (size == null) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartSection.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartSection.java	Tue Jun 05 19:21:16 2018 +0200
@@ -93,8 +93,8 @@
     }
 
 
-    public Boolean getDisplayGrid() {
-        return getBooleanValue(DISPLAYGRID_ATTR);
+    public boolean getDisplayGrid() {
+        return getBooleanValue(DISPLAYGRID_ATTR, true);
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -16,13 +16,8 @@
 import java.util.List;
 
 import org.apache.log4j.Logger;
-import org.jfree.chart.LegendItemCollection;
-import org.jfree.chart.annotations.XYBoxAnnotation;
-import org.jfree.chart.annotations.XYTextAnnotation;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.data.xy.XYSeries;
-
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.DataProvider;
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.geom.Lines;
@@ -35,8 +30,13 @@
 import org.dive4elements.river.model.FastCrossSectionLine;
 import org.dive4elements.river.themes.TextStyle;
 import org.dive4elements.river.themes.ThemeDocument;
+import org.dive4elements.river.utils.Formatter;
 import org.dive4elements.river.utils.RiverUtils;
-import org.dive4elements.river.utils.Formatter;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.annotations.XYBoxAnnotation;
+import org.jfree.chart.annotations.XYTextAnnotation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.xy.XYSeries;
 
 
 /**
@@ -93,7 +93,7 @@
      * Get localized chart title.
      */
     @Override
-    public String getDefaultChartTitle() {
+    public String getDefaultChartTitle(final CallContext context) {
         Object[] i18n_msg_args = new Object[] {
                 getRiverName()
         };
@@ -103,19 +103,19 @@
 
     /** Always return default subtitle. */
     @Override
-    protected String getChartSubtitle() {
+    protected String getChartSubtitle(CallContext context) {
         // XXX NOTE: overriding this method disables ChartSettings subtitle!
         // The default implementation of this method in ChartGenerator returns
         // the subtitle changed via the chart settings dialog. This method
         // always returns the subtitle containing river and km, NEVER the
         // ChartSettings subtitle!
-        return getDefaultChartSubtitle();
+        return getDefaultChartSubtitle(context);
     }
 
-
+    
     /** Get Charts default subtitle. */
     @Override
-    protected String getDefaultChartSubtitle() {
+    protected String getDefaultChartSubtitle(final CallContext context) {
         List<DataProvider> providers =
             context.getDataProvider(
                 CrossSectionFacet.BLACKBOARD_CS_MASTER_DATA);
@@ -164,8 +164,7 @@
     }
 
     @Override
-    protected void addAnnotationsToRenderer(XYPlot plot) {
-        super.addAnnotationsToRenderer(plot);
+    protected void doAddFurtherAnnotations(XYPlot plot, List<RiverAnnotation> annotations) {
 
         // Paints for the boxes/lines.
         Stroke basicStroke = new BasicStroke(1.0f);
@@ -179,7 +178,7 @@
                 plot.getDomainAxis(0),
                 plot.getRangeAxis());
 
-        for(RiverAnnotation fa : this.annotations) {
+        for(RiverAnnotation fa : annotations) {
 
             // Access text styling, if any.
             ThemeDocument theme = fa.getTheme();
@@ -245,14 +244,14 @@
     }
 
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(final CallContext context) {
         return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
     }
 
 
     @Override
     protected String getDefaultYAxisLabel(int pos) {
-        D4EArtifact flys = (D4EArtifact) master;
+        D4EArtifact flys = getArtifact();
 
         String unit = RiverUtils.getRiver(flys).getWstUnit().getName();
 
@@ -279,6 +278,8 @@
             log.error("No facet name for doOut(). No output generated!");
             return;
         }
+        
+        CallContext context = getContext();
 
         if (name.equals(CROSS_SECTION)) {
             doCrossSectionOut(
@@ -351,11 +352,13 @@
         // DO NOT SORT DATA! This destroys the gaps indicated by NaNs.
         StyledXYSeries series = new StyledXYSeries(seriesName, false, theme);
 
+        CallContext context = getContext();
+        
         if (!theme.parseShowLineLabel()) {
             series.setLabel("");
         }
         if (theme.parseShowWidth()) {
-            NumberFormat nf = Formatter.getMeterFormat(this.context);
+            NumberFormat nf = Formatter.getMeterFormat(context);
             String labelAdd = "b=" + nf.format(lines.width) + "m";
             if (series.getLabel().length() == 0) {
                 series.setLabel(labelAdd);
@@ -366,8 +369,8 @@
         }
         if (theme.parseShowLevel() && lines.points.length > 1
                 && lines.points[1].length > 0) {
-            NumberFormat nf = Formatter.getMeterFormat(this.context);
-            D4EArtifact flys = (D4EArtifact) master;
+            NumberFormat nf = Formatter.getMeterFormat(context);
+            D4EArtifact flys = getArtifact();
 
             String unit = RiverUtils.getRiver(flys).getWstUnit().getName();
 
@@ -380,7 +383,7 @@
             }
         }
         if (theme.parseShowMiddleHeight() && lines.width != 0) {
-            NumberFormat nf = Formatter.getMeterFormat(this.context);
+            NumberFormat nf = Formatter.getMeterFormat(context);
             String labelAdd = "T=" + nf.format(lines.area / lines.width) + "m";
             // : " + lines.area + "/" + lines.width);
             if (series.getLabel().length() == 0) {
@@ -450,9 +453,9 @@
      * @return a new <i>ChartSection</i>.
      */
     @Override
-    protected ChartSection buildChartSection() {
+    protected ChartSection buildChartSection(final CallContext context) {
         ChartSection chartSection = new ChartSection();
-        chartSection.setTitle(getChartTitle());
+        chartSection.setTitle(getChartTitle(context));
         chartSection.setDisplayGrid(isGridVisible());
         chartSection.setDisplayLogo(showLogo());
         chartSection.setLogoVPlacement(logoVPlace());
--- a/artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -10,9 +10,7 @@
 
 import java.awt.Color;
 import java.awt.Font;
-
 import java.text.NumberFormat;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -21,62 +19,43 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-import java.io.OutputStream;
 
 import javax.swing.ImageIcon;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
-
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
-
 import org.dive4elements.artifacts.CallContext;
-
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.exports.DiagramAttributes.AxisAttributes;
 import org.dive4elements.river.exports.process.Processor;
-
-import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.AnnotationHelper;
 import org.dive4elements.river.jfree.AxisDataset;
 import org.dive4elements.river.jfree.Bounds;
 import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
 import org.dive4elements.river.jfree.XYMetaSeriesCollection;
-
 import org.dive4elements.river.themes.ThemeDocument;
-
 import org.jfree.chart.ChartFactory;
 import org.jfree.chart.JFreeChart;
 import org.jfree.chart.LegendItem;
-
 import org.jfree.chart.annotations.XYAnnotation;
 import org.jfree.chart.annotations.XYImageAnnotation;
-
 import org.jfree.chart.axis.LogarithmicAxis;
 import org.jfree.chart.axis.NumberAxis;
 import org.jfree.chart.axis.ValueAxis;
-
 import org.jfree.chart.plot.Marker;
 import org.jfree.chart.plot.PlotOrientation;
 import org.jfree.chart.plot.XYPlot;
-
 import org.jfree.data.Range;
-
 import org.jfree.data.general.Series;
-
 import org.jfree.data.xy.XYDataset;
 import org.jfree.data.xy.XYSeries;
 import org.jfree.data.xy.XYSeriesCollection;
 
-import org.w3c.dom.Document;
-
-import org.apache.commons.lang.StringUtils;
-
 
 /**
  * The main diagram creation class.
@@ -102,9 +81,9 @@
     /** The log that is used in this generator. */
     private static Logger log = Logger.getLogger(DiagramGenerator.class);
 
-    protected List<Marker> domainMarkers = new ArrayList<Marker>();
+    protected List<Marker> domainMarkers = new ArrayList<>();
 
-    protected List<Marker> valueMarkers = new ArrayList<Marker>();
+    protected List<Marker> valueMarkers = new ArrayList<>();
 
     /** The max X range to include all X values of all series for each axis. */
     protected Map<Integer, Bounds> xBounds;
@@ -144,17 +123,7 @@
         diagramAttributes = da.new Instance();
     }
 
-    @Override
-    public void init(
-        String       outName,
-        Document     request,
-        OutputStream out,
-        CallContext  context
-    ) {
-        super.init(outName, request, out, context);
-    }
-
-    private void setInvertedFromConfig() {
+    private void setInvertedFromConfig(CallContext context) {
         DiagramAttributes.DomainAxisAttributes dx =
             diagramAttributes.getDomainAxis();
 
@@ -175,13 +144,13 @@
      * Generate the chart anew (including localized axis and all).
      */
     @Override
-    public JFreeChart generateChart() {
+    protected JFreeChart generateChart(CallContext context) {
         log.debug("DiagramGenerator.generateChart");
 
         postProcess();
 
         JFreeChart chart = ChartFactory.createXYLineChart(
-            getChartTitle(),
+            getChartTitle(context),
             "",
             "",
             null,
@@ -191,13 +160,13 @@
             false);
 
         XYPlot plot = (XYPlot) chart.getPlot();
-        ValueAxis axis = createXAxis(getXAxisLabel());
+        ValueAxis axis = createXAxis(context, getXAxisLabel());
         plot.setDomainAxis(axis);
 
         chart.setBackgroundPaint(Color.WHITE);
         plot.setBackgroundPaint(Color.WHITE);
-        addSubtitles(chart);
-        addMetadataSubtitle(chart);
+        addSubtitles(context, chart);
+        addMetadataSubtitle(context, chart);
         adjustPlot(plot);
 
         //debugAxis(plot);
@@ -215,7 +184,7 @@
 
         localizeAxes(plot);
 
-        setInvertedFromConfig();
+        setInvertedFromConfig(context);
 
         adjustAxes(plot);
         if (!(axis instanceof LogarithmicAxis)) {
@@ -230,10 +199,8 @@
         //debugAxis(plot);
 
         // These have to go after the autozoom.
-        AnnotationHelper.addAnnotationsToRenderer(annotations, plot,
-                getChartSettings(), datasets);
-        AnnotationHelper.addYAnnotationsToRenderer(yAnnotations, plot,
-                getChartSettings(), datasets);
+        addAnnotationsToRenderer(plot);
+        addYAnnotationsToRenderer(plot);
 
         // Add a logo (maybe).
         addLogo(plot);
@@ -249,10 +216,6 @@
         return chart;
     }
 
-    public String getOutName() {
-        return outName;
-    }
-
     /**
      * Return left most data points x value (on first axis).
      */
@@ -393,7 +356,7 @@
     }
 
 
-    protected NumberAxis createXAxis(String label) {
+    protected NumberAxis createXAxis(final CallContext context, String label) {
         boolean logarithmic = (Boolean)diagramAttributes.getDomainAxis().
             isLog().evaluate((D4EArtifact)getMaster(), context);
 
@@ -1113,7 +1076,7 @@
     }
 
     @Override
-    public String getDefaultChartTitle() {
+    protected final String getDefaultChartTitle(CallContext context) {
         DiagramAttributes.Title dTitle = diagramAttributes.getTitle();
         if (dTitle == null) {
             return "Title not configured in conf.xml";
@@ -1122,8 +1085,9 @@
         return dTitle.evaluate((D4EArtifact)getMaster(), context);
     }
 
+    
     @Override
-    public String getDefaultChartSubtitle() {
+    protected String getDefaultChartSubtitle(CallContext context) {
         String parts = "";
         DiagramAttributes.Title dTitle = diagramAttributes.getSubtitle();
         if (dTitle == null &&
@@ -1154,7 +1118,7 @@
      * Get internationalized label for the x axis.
      */
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(CallContext context) {
         DiagramAttributes.DomainAxisAttributes dx =
             diagramAttributes.getDomainAxis();
 
@@ -1167,8 +1131,7 @@
         return "Domain Axis Title not configured in conf.xml";
     }
 
-    @Override
-    protected String getDefaultYAxisLabel(String axisName) {
+    protected final String getDefaultYAxisLabel(String axisName) {
         Set labelSet = axesLabels.get(diagramAttributes.getAxisIndex(axisName));
         log.debug("Labels for axis: " + labelSet);
         if (labelSet != null && !labelSet.isEmpty()) {
@@ -1198,8 +1161,9 @@
      *
      * @return a list of Y axis sections.
      */
-    protected List<AxisSection> buildYAxisSections() {
-        List<AxisSection> axisSections = new ArrayList<AxisSection>();
+    @Override
+    protected final List<AxisSection> buildYAxisSections() {
+        List<AxisSection> axisSections = new ArrayList<>();
 
         List<DiagramAttributes.AxisAttributes> axesAttrs =
             diagramAttributes.getAxesAttributes();
@@ -1315,7 +1279,7 @@
         
         final AxisAttributes axisAttributes = diagramAttributes.getAxesAttributes().get(index);
         
-        boolean logarithmic = (Boolean)axisAttributes.isLog().evaluate((D4EArtifact)getMaster(), context);
+        boolean logarithmic = (Boolean)axisAttributes.isLog().evaluate((D4EArtifact)getMaster(), getContext());
 
         NumberAxis axis;
         if (logarithmic) {
@@ -1347,4 +1311,4 @@
     public void addSubtitle(String part) {
         this.subTitleParts.add(part);
     }
-}
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -28,6 +28,7 @@
 import org.dive4elements.river.themes.ThemeDocument;
 
 import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.GaugeDischargeCurveArtifact;
 
 import org.apache.log4j.Logger;
@@ -121,8 +122,8 @@
         if (getMaster() instanceof GaugeDischargeCurveArtifact) {
             GaugeDischargeCurveArtifact myMaster =
                 (GaugeDischargeCurveArtifact) getMaster();
-            State state = myMaster.getCurrentState(context);
-            if (myMaster.STATIC_STATE_NAME.equals(state.getID())) {
+            State state = myMaster.getCurrentState(getContext());
+            if (GaugeDischargeCurveArtifact.STATIC_STATE_NAME.equals(state.getID())) {
                 return;
             }
         }
@@ -213,13 +214,13 @@
      * Returns always null to suppress subtitles.
      */
     @Override
-    protected String getDefaultChartTitle() {
+    protected String getDefaultChartTitle(final CallContext context) {
         return null;
     }
 
 
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(final CallContext context) {
         return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
     }
 
@@ -278,6 +279,8 @@
         String name = artifactFacet.getFacetName();
         log.debug("DischargeCurveGenerator.doOut: " + name);
 
+        final CallContext context = getContext();
+        
         MiscDischargeProcessor dProcessor = new MiscDischargeProcessor(
             getRange()[0]);
         if (dProcessor.canHandle(name)) {
@@ -376,8 +379,7 @@
         double y,
         ThemeDocument theme
     ) {
-        List<XYTextAnnotation> textAnnos =
-            new ArrayList<XYTextAnnotation>();
+        final List<XYTextAnnotation> textAnnos = new ArrayList<>();
         XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
                 title,
                 x,
@@ -429,8 +431,7 @@
 
         // If no Q values (i.e. all -1) found, add annotations.
         if (hasNoDischarge(data)) {
-            List<StickyAxisAnnotation> xy =
-                new ArrayList<StickyAxisAnnotation>();
+            final List<StickyAxisAnnotation> xy = new ArrayList<>();
 
             for (double y: data[1]) {
                 if (translate != 0d) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/DischargeGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DischargeGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -39,7 +39,7 @@
     public void addDatasets(XYPlot plot) {
         super.addDatasets(plot);
 
-        Object pnp = context.getContextValue(PNP);
+        Object pnp = getContext().getContextValue(PNP);
         if (!(pnp instanceof Number)) {
             return;
         }
@@ -52,7 +52,7 @@
             return;
         }
 
-        AxisDataset data = datasets.get(wAxisIndex);
+        AxisDataset data = getDatasets().get(wAxisIndex);
         if (data == null) {
             // No W axis
             return;
@@ -94,8 +94,7 @@
             getYAxisFontSize(wAxisIndex));
 
         String axisName = "W.in.cm";
-        String axisLabel = Resources.getMsg(context.getMeta(),
-            I18N_AXIS_LABEL, "W [cm]");
+        String axisLabel = msg( I18N_AXIS_LABEL, "W [cm]" );
 
         IdentifiableNumberAxis axis = new IdentifiableNumberAxis(
             axisName, axisLabel);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -9,6 +9,7 @@
 package org.dive4elements.river.exports;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.model.WQDay;
 import org.dive4elements.river.jfree.Bounds;
@@ -95,13 +96,13 @@
 
 
     @Override
-    protected String getDefaultChartTitle() {
+    protected String getDefaultChartTitle(final CallContext context) {
         return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
     }
 
 
     @Override
-    protected String getDefaultChartSubtitle() {
+    protected String getDefaultChartSubtitle(final CallContext context) {
         double[] dist  = getRange();
 
         Object[] args = new Object[] {
@@ -114,7 +115,7 @@
 
 
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(final CallContext context) {
         return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
     }
 
@@ -123,7 +124,7 @@
     protected String getDefaultYAxisLabel(int index) {
         String label = "default";
         if (index == YAXIS.W.idx) {
-            label = msg(I18N_YAXIS_LABEL_W, new Object[] { getRiverUnit() });
+            label = msg(I18N_YAXIS_LABEL_W, I18N_YAXIS_LABEL_W, new Object[] { getRiverUnit() });
         }
         else if (index == YAXIS.Q.idx) {
             label = msg(I18N_YAXIS_LABEL_Q);
@@ -192,6 +193,8 @@
             log.error("No facet given. Cannot create dataset.");
             return;
         }
+        
+        final CallContext context = getContext();
 
         if (name.equals(DURATION_W)) {
             doWOut(
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ExportSection.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ExportSection.java	Tue Jun 05 19:21:16 2018 +0200
@@ -18,13 +18,15 @@
     public static final String WIDTH_ATTR  = "width";
     public static final String HEIGHT_ATTR = "height";
 
+    private static final String METADATA_ATTR = "chart_settings_export_metadata";
+
 
     public ExportSection() {
         super("export");
     }
 
 
-    public void setWidth(int width) {
+    public void setWidth(final int width) {
         if (width <= 0) {
             return;
         }
@@ -38,7 +40,7 @@
     }
 
 
-    public void setHeight(int height) {
+    public void setHeight(final int height) {
         if (height <= 0) {
             return;
         }
@@ -50,5 +52,12 @@
     public Integer getHeight() {
         return getIntegerValue(HEIGHT_ATTR);
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+
+    public void setMetadata(final boolean metadata) {
+        setBooleanValue(METADATA_ATTR, metadata);
+    }
+
+    public boolean getMetadata() {
+        return getBooleanValue(METADATA_ATTR, true);
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -13,7 +13,7 @@
 import org.apache.log4j.Logger;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
-
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.D4EArtifact;
 
 import org.dive4elements.river.artifacts.access.HistoricalDischargeAccess;
@@ -90,13 +90,13 @@
     }
 
     @Override
-    protected String getDefaultChartTitle() {
+    protected String getDefaultChartTitle(final CallContext context) {
         return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
     }
 
     @Override
-    protected String getDefaultChartSubtitle() {
-        D4EArtifact flys = (D4EArtifact) master;
+    protected String getDefaultChartSubtitle(final CallContext context) {
+        D4EArtifact flys = getArtifact();
         Timerange evalTime = new HistoricalDischargeAccess(flys)
             .getEvaluationTimerange();
 
@@ -107,7 +107,7 @@
     }
 
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(final CallContext context) {
         return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
     }
 
@@ -138,6 +138,8 @@
         log.debug("Theme description is: "
             + artifactFacet.getFacetDescription());
 
+        final CallContext context = getContext();
+        
         if (name.equals(HISTORICAL_DISCHARGE_Q)) {
             doHistoricalDischargeOutQ(
                 (D4EArtifact) artifactFacet.getArtifact(),
--- a/artifacts/src/main/java/org/dive4elements/river/exports/LegendSection.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/LegendSection.java	Tue Jun 05 19:21:16 2018 +0200
@@ -55,8 +55,8 @@
     }
 
 
-    public Boolean getVisibility() {
-        return getBooleanValue(VISIBILITY_ATTR);
+    public boolean getVisibility() {
+        return getBooleanValue(VISIBILITY_ATTR, true);
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -10,6 +10,7 @@
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.geom.Lines;
 import org.dive4elements.river.artifacts.model.AreaFacet;
@@ -157,18 +158,17 @@
      * @return the default title for this chart.
      */
     @Override
-    public String getDefaultChartTitle() {
+    public String getDefaultChartTitle(final CallContext context) {
         return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
     }
 
-
     /**
      * Returns the default subtitle for this chart.
      *
      * @return the default subtitle for this chart.
      */
     @Override
-    protected String getDefaultChartSubtitle() {
+    protected String getDefaultChartSubtitle(final CallContext context) {
         double[] dist = getRange();
 
         Object[] args = null;
@@ -208,8 +208,8 @@
      * Get internationalized label for the x axis.
      */
     @Override
-    protected String getDefaultXAxisLabel() {
-        D4EArtifact flys = (D4EArtifact) master;
+    protected String getDefaultXAxisLabel(CallContext context) {
+        D4EArtifact flys = getArtifact();
 
         return msg(
             I18N_XAXIS_LABEL,
@@ -241,7 +241,7 @@
      * Get internationalized label for the y axis.
      */
     protected String getWAxisLabel() {
-        D4EArtifact flys = (D4EArtifact) master;
+        D4EArtifact flys = getArtifact();
 
         String unit = RiverUtils.getRiver(flys).getWstUnit().getName();
 
@@ -340,6 +340,8 @@
             return;
         }
 
+        CallContext context = getContext();
+        
         Processor wProcessor = new WOutProcessor();
         Processor qProcessor = new QOutProcessor();
         Processor bdyProcessor = new BedDiffYearProcessor();
--- a/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator2.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator2.java	Tue Jun 05 19:21:16 2018 +0200
@@ -10,6 +10,7 @@
 
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.jfree.Bounds;
 import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.artifacts.model.FacetTypes;
@@ -62,7 +63,7 @@
         "chart.subtitle.radius";
 
     @Override
-    public String getDefaultChartSubtitle() {
+    public String getDefaultChartSubtitle(final CallContext context) {
         double[] dist = getRange();
 
         String parts = "";
@@ -124,6 +125,7 @@
 
         // This might not be neccessary if every facet uses only the
         // radius and does not do its own zoomscale calculation.
+        final CallContext context = getContext();
         context.putContextValue("startkm", candidate.getLowerBound());
         context.putContextValue("endkm", candidate.getUpperBound());
         context.putContextValue("bounds_defined", true);
@@ -146,6 +148,7 @@
         }
 
         // fake startkm and endkm for the dry run
+        final CallContext context = getContext();
         context.putContextValue("startkm", 0d);
         context.putContextValue("endkm", 42d);
         for (SuperBundle superbundle: postOutAF) {
@@ -185,15 +188,13 @@
 
             SuperBundle superbundle = new SuperBundle(bundle, theme, visible);
             if (postOutAF == null) {
-                postOutAF = new ArrayList<SuperBundle>();
+                postOutAF = new ArrayList<>();
             }
             postOutAF.add(superbundle);
             if (visible) {
                 log.debug("Adding radius subtitle.");
 
-                addSubtitle(Resources.getMsg(
-                            getCallContext().getMeta(),
-                            I18N_SUBTITLE_RADIUS) + ": $RADIUS");
+                addSubtitle(msg(I18N_SUBTITLE_RADIUS) + ": $RADIUS");
             }
             return;
         }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/NormalizedReferenceCurveGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/NormalizedReferenceCurveGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -8,6 +8,8 @@
 
 package org.dive4elements.river.exports;
 
+import org.dive4elements.artifacts.CallContext;
+
 /**
  * An OutGenerator that generates reference curves.
  */
@@ -25,7 +27,7 @@
 
     /** Get default chart title. */
     @Override
-    protected String getDefaultChartTitle() {
+    protected String getDefaultChartTitle(final CallContext context) {
         return msg(
             I18N_NORMALIZED_CHART_TITLE,
             I18N_NORMALIZED_CHART_TITLE_DEFAULT);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -9,6 +9,7 @@
 package org.dive4elements.river.exports;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.model.WW;
 import org.dive4elements.river.artifacts.model.WW.ApplyFunctionIterator;
@@ -86,12 +87,12 @@
 
     /** Get default chart title. */
     @Override
-    protected String getDefaultChartTitle() {
+    protected String getDefaultChartTitle(final CallContext context) {
         return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
     }
 
     @Override
-    protected String getDefaultChartSubtitle() {
+    protected String getDefaultChartSubtitle(final CallContext context) {
         Object[] args = new Object[] {
             getRiverName(),
         };
@@ -102,14 +103,14 @@
 
     /** True if axis is in cm (because at gauge). */
     protected boolean getInCm(int index) {
-        Object obj = context.getContextValue("reference.curve.axis.scale");
+        Object obj = getContext().getContextValue("reference.curve.axis.scale");
         return obj instanceof WWAxisTypes && ((WWAxisTypes)obj).getInCm(index);
     }
 
 
     /** Get Label for X-axis (W). */
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(final CallContext context) {
         return msg(getInCm(0) ? I18N_X_AXIS_IN_CM : I18N_X_AXIS_IN_M);
     }
 
@@ -149,6 +150,8 @@
             return;
         }
 
+        final CallContext context = getContext();
+        
         if (name.equals(facetName())) {
             doReferenceOut(artifactFacet.getData(context), theme, visible);
         }
@@ -194,7 +197,7 @@
     ) {
         WW ww = (WW)data;
 
-        Object obj = context.getContextValue("reference.curve.axis.scale");
+        Object obj = getContext().getContextValue("reference.curve.axis.scale");
 
         WWAxisTypes wwat = obj instanceof WWAxisTypes
             ? (WWAxisTypes)obj
@@ -236,7 +239,7 @@
     /** Set the tick units for given axis. */
     protected void setAxisTickUnit(double tick, ValueAxis axis) {
         TickUnits units = new TickUnits();
-        units.add(new NumberTickUnit(tick, Formatter.getWaterlevelW(context)));
+        units.add(new NumberTickUnit(tick, Formatter.getWaterlevelW(getContext())));
         axis.setStandardTickUnits(units);
         axis.setAutoTickUnitSelection(true);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -9,12 +9,12 @@
 package org.dive4elements.river.exports;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.jfree.Bounds;
 import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
 import org.dive4elements.river.jfree.DoubleBounds;
 import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.AnnotationHelper;
 import org.dive4elements.river.jfree.StyledTimeSeries;
 import org.dive4elements.river.jfree.TimeBounds;
 import org.dive4elements.river.jfree.AxisDataset;
@@ -93,14 +93,12 @@
         attributes = new HashMap<String, String>();
     }
 
-
-
     @Override
-    public JFreeChart generateChart() {
+    protected JFreeChart generateChart(CallContext context) {
         log.info("Generate Timeseries Chart.");
 
         JFreeChart chart = ChartFactory.createTimeSeriesChart(
-            getChartTitle(),
+            getChartTitle(context),
             getXAxisLabel(),
             getYAxisLabel(0),
             null,
@@ -113,8 +111,8 @@
         chart.setBackgroundPaint(Color.WHITE);
         plot.setBackgroundPaint(Color.WHITE);
 
-        addSubtitles(chart);
-        addMetadataSubtitle( chart );
+        addSubtitles(context, chart);
+        addMetadataSubtitle( context, chart );
         adjustPlot(plot);
         addDatasets(plot);
         adjustAxes(plot);
@@ -126,11 +124,8 @@
 
         consumeAxisSettings(plot);
 
-        AnnotationHelper.addAnnotationsToRenderer(
-            annotations,
-            plot,
-            getChartSettings(),
-            datasets);
+        addAnnotationsToRenderer(plot);
+        
         addLogo(plot);
         aggregateLegendEntries(plot);
         return chart;
@@ -629,7 +624,7 @@
         catch(JSONException ex) {
             String str = array.getString(0);
             DateFormat df = DateFormat.getDateInstance(
-                    DateFormat.MEDIUM, Resources.getLocale(context.getMeta()));
+                    DateFormat.MEDIUM, Resources.getLocale(getContext().getMeta()));
             return df.parse(str);
         }
     }
@@ -856,7 +851,7 @@
      * setLowerTimeRange */
     @Override
     protected List<AxisSection> buildXAxisSections() {
-        List<AxisSection> axisSections = new ArrayList<AxisSection>();
+        List<AxisSection> axisSections = new ArrayList<>();
 
         String identifier = "X";
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/TypeSection.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/TypeSection.java	Tue Jun 05 19:21:16 2018 +0200
@@ -123,26 +123,25 @@
     }
 
 
-    public void setBooleanValue(String key, boolean value) {
-        Attribute attr = getAttribute(key);
-        if (attr == null) {
-            attr = new BooleanAttribute(key, value, true);
-            addAttribute(key, attr);
-        }
-        else {
+    protected final void setBooleanValue(String key, boolean value) {
+        final Attribute attr = getAttribute(key);
+        if (attr == null)
+            addAttribute(key, new BooleanAttribute(key, value, true));
+        else 
             attr.setValue(value);
-        }
     }
 
-
-    public Boolean getBooleanValue(String key) {
-        Attribute attr = getAttribute(key);
+    protected final boolean getBooleanValue(final String key, boolean defaultValue) {
+        final Attribute attr = getAttribute(key);
 
         if (attr instanceof BooleanAttribute) {
-            return (Boolean) attr.getValue();
+            final Boolean value = (Boolean) attr.getValue();
+            if( value == null )
+                return defaultValue;
+            
+            return value;
         }
 
-        return null;
+        return defaultValue;
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -40,6 +40,7 @@
 import org.json.JSONException;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.jfree.Bounds;
 import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
 import org.dive4elements.river.jfree.DoubleBounds;
@@ -47,7 +48,6 @@
 import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.jfree.AxisDataset;
-import org.dive4elements.river.jfree.AnnotationHelper;
 import org.dive4elements.river.themes.ThemeDocument;
 
 
@@ -102,11 +102,11 @@
      * Generate the chart anew (including localized axis and all).
      */
     @Override
-    public JFreeChart generateChart() {
+    protected JFreeChart generateChart(CallContext context) {
         log.debug("XYChartGenerator.generateChart");
 
         JFreeChart chart = ChartFactory.createXYLineChart(
-            getChartTitle(),
+            getChartTitle(context),
             getXAxisLabel(),
             getYAxisLabel(0),
             null,
@@ -121,8 +121,8 @@
 
         chart.setBackgroundPaint(Color.WHITE);
         plot.setBackgroundPaint(Color.WHITE);
-        addSubtitles(chart);
-        addMetadataSubtitle(chart);
+        addSubtitles(context, chart);
+        addMetadataSubtitle(context, chart);
         adjustPlot(plot);
 
         //debugAxis(plot);
@@ -994,10 +994,5 @@
     public void setInverted(boolean inverted) {
         this.inverted = inverted;
     }
-
-    protected void addAnnotationsToRenderer(XYPlot plot) {
-        AnnotationHelper.addAnnotationsToRenderer(annotations, plot,
-                getChartSettings(), datasets);
-    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -19,6 +19,7 @@
 import org.jfree.data.xy.XYSeries;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.model.DateRange;
@@ -114,7 +115,7 @@
     ) {
         log.debug("doExtremeCurveBaseOut");
         ExtremeCurveFacet facet = (ExtremeCurveFacet) aaf.getFacet();
-        Curve curve = (Curve) facet.getData(aaf.getArtifact(), context);
+        Curve curve = (Curve) facet.getData(aaf.getArtifact(), getContext());
         if (curve == null) {
             log.warn("doExtremeCurveBaseOut: Facet does not contain Curve");
             return;
@@ -155,7 +156,7 @@
     ) {
         log.debug("doExtremeCurveOut");
         ExtremeCurveFacet facet = (ExtremeCurveFacet) aaf.getFacet();
-        Curve curve = (Curve) facet.getData(aaf.getArtifact(), context);
+        Curve curve = (Curve) facet.getData(aaf.getArtifact(), getContext());
         if (curve == null) {
             log.warn("doExtremeCurveOut: Facet does not contain Curve");
             return;
@@ -189,29 +190,29 @@
 
 
     @Override
-    protected String getChartTitle() {
+    protected String getChartTitle(final CallContext context) {
         return Resources.format(
-                context.getMeta(),
+                getContext().getMeta(),
                 I18N_CHART_TITLE,
                 I18N_CHART_TITLE_DEFAULT,
-                context.getContextValue(CURRENT_KM));
+                getContext().getContextValue(CURRENT_KM));
     }
 
 
     @Override
-    protected String getDefaultChartTitle() {
+    protected String getDefaultChartTitle(final CallContext context) {
         return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
     }
 
     @Override
-    protected String getDefaultChartSubtitle() {
+    protected String getDefaultChartSubtitle(final CallContext context) {
         FixAnalysisAccess access = new FixAnalysisAccess(artifact);
         DateRange dateRange = access.getDateRange();
         DateRange refRange  = access.getReferencePeriod();
 
         if (dateRange != null && refRange != null) {
             return Resources.format(
-                    context.getMeta(),
+                    getContext().getMeta(),
                     I18N_CHART_SUBTITLE,
                     "",
                     access.getRiverName(),
@@ -225,8 +226,8 @@
     }
 
     @Override
-    protected void addSubtitles(JFreeChart chart) {
-        String defaultSubtitle = getDefaultChartSubtitle();
+    protected void addSubtitles(final CallContext context, JFreeChart chart) {
+        String defaultSubtitle = getDefaultChartSubtitle(context);
 
         if (defaultSubtitle == null || defaultSubtitle.length() == 0) {
             return;
@@ -236,13 +237,13 @@
     }
 
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(final CallContext context) {
         return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
     }
 
     @Override
     protected String getDefaultYAxisLabel(int pos) {
-        D4EArtifact flys = (D4EArtifact) master;
+        D4EArtifact flys = getArtifact();
 
         String unit = RiverUtils.getRiver(flys).getWstUnit().getName();
         if (pos == 0) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -106,24 +106,24 @@
 
 
     @Override
-    protected String getDefaultChartTitle() {
+    protected String getDefaultChartTitle(final CallContext context) {
         return msg(I18N_CHART_TITLE, I18N_CHART_TITLE);
     }
 
 
     @Override
-    protected String getChartTitle() {
+    protected String getChartTitle(final CallContext context) {
         return Resources.format(
                 context.getMeta(),
                 I18N_CHART_TITLE,
                 "",
                 FixChartGenerator
-                .getCurrentKmFromRequest(request).doubleValue());
+                .getCurrentKmFromRequest(getRequest()).doubleValue());
     }
 
 
     @Override
-    protected String getDefaultChartSubtitle() {
+    protected String getDefaultChartSubtitle(final CallContext context) {
         FixAnalysisAccess access = new FixAnalysisAccess(artifact);
         DateRange dateRange = access.getDateRange();
         DateRange refRange  = access.getReferencePeriod();
@@ -140,7 +140,7 @@
 
 
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(final CallContext context) {
         return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL);
     }
 
@@ -171,6 +171,8 @@
 
         this.artifact = (D4EArtifact)artifactFacet.getArtifact();
 
+        final CallContext context = getContext();
+        
         if (name.contains(FIX_SECTOR_AVERAGE_DWT)) {
             doSectorAverageOut(
                     (D4EArtifact) artifactFacet.getArtifact(),
@@ -388,10 +390,10 @@
             return;
         }
 
-        Locale locale = Resources.getLocale(context.getMeta());
+        Locale locale = Resources.getLocale(getContext().getMeta());
         NumberFormat nf = NumberFormat.getInstance(locale);
 
-        List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
+        List<XYTextAnnotation> textAnnos = new ArrayList<>();
 
         for (int[] idxs: annoIdxMap.values()) {
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java	Tue Jun 05 19:21:16 2018 +0200
@@ -28,6 +28,7 @@
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.StaticWKmsArtifact;
 import org.dive4elements.river.artifacts.WINFOArtifact;
@@ -115,7 +116,7 @@
 
     /** Returns value != 0 if the current km is not at a gauge. */
     public double getCurrentGaugeDatum() {
-        Object ckm = context.getContextValue(CURRENT_KM);
+        Object ckm = getContext().getContextValue(CURRENT_KM);
         if (ckm != null) {
             return DischargeCurveGenerator.getCurrentGaugeDatum(
                 (Double) ckm,
@@ -210,6 +211,8 @@
 
         this.artifact = (D4EArtifact) aaf.getArtifact();
 
+        final CallContext context = getContext();
+        
         if(name.startsWith(FIX_SECTOR_AVERAGE_WQ)) {
             doSectorAverageOut(aaf, doc, visible);
         }
@@ -286,7 +289,7 @@
     ) {
         log.debug("doSectorAverageOut");
 
-        QWDDateRange qwdd = (QWDDateRange) aaf.getData(context);
+        QWDDateRange qwdd = (QWDDateRange) aaf.getData(getContext());
         QWD qwd = qwdd != null ? qwdd.getQWD() : null;
 
         if(qwd != null) {
@@ -305,7 +308,7 @@
     ) {
         log.debug("doAnalysisEventsOut");
 
-        QWD qwd = (QWD)aaf.getData(context);
+        QWD qwd = (QWD)aaf.getData(getContext());
 
         if (qwd == null) {
             log.debug("doAnalysisEventsOut: qwd == null");
@@ -365,7 +368,7 @@
     ) {
         log.debug("doReferenceEventsOut");
 
-        QWI qwd = (QWI)aaf.getData(context);
+        QWI qwd = (QWI)aaf.getData(getContext());
         if (qwd == null) {
             log.debug("doReferenceEventsOut: qwds == null");
             return;
@@ -421,7 +424,7 @@
         boolean       visible
     ) {
         XYSeries series = new StyledXYSeries(title, theme);
-        Double ckm = (Double) context.getContextValue(CURRENT_KM);
+        Double ckm = (Double) getContext().getContextValue(CURRENT_KM);
         if (wqkms == null || wqkms.getKms().length == 0 || ckm == null) {
             log.info("addPointFromWQKms: No event data to show.");
             return;
@@ -458,7 +461,7 @@
     ) {
         log.debug("doEventsOut");
         // Find W/Q at km.
-        addPointFromWQKms((WQKms) aaf.getData(context),
+        addPointFromWQKms((WQKms) aaf.getData(getContext()),
             aaf.getFacetDescription(), doc, visible);
     }
 
@@ -472,7 +475,7 @@
 
         FixWQCurveFacet facet = (FixWQCurveFacet)aaf.getFacet();
         FixFunction func = (FixFunction)facet.getData(
-                aaf.getArtifact(), context);
+                aaf.getArtifact(), getContext());
 
         if (func == null) {
             log.warn("doWQCurveOut: Facet does not contain FixFunction");
@@ -527,7 +530,7 @@
     ) {
         log.debug("doOutlierOut");
 
-        QWI[] qws = (QWI[])aaf.getData(context);
+        QWI[] qws = (QWI[])aaf.getData(getContext());
         addQWSeries(qws, aaf, doc, visible);
     }
 
@@ -543,7 +546,7 @@
             return;
         }
 
-        Object qsectorsObj = aaf.getData(context);
+        Object qsectorsObj = aaf.getData(getContext());
         if (qsectorsObj == null || !(qsectorsObj instanceof List)) {
             log.warn("No QSectors coming from data.");
             return;
@@ -631,7 +634,7 @@
             log.debug("Got WKms");
             WKms data = (WKms) wqkms;
 
-            Double ckm = (Double) context.getContextValue(CURRENT_KM);
+            Double ckm = (Double) getContext().getContextValue(CURRENT_KM);
             double location = (ckm != null)
                     ? ckm.doubleValue()
                     : getRange()[0];
@@ -709,7 +712,7 @@
             // be delivered by the facet already (instead of in the Generator).
             log.debug("FixWQCurveGenerator: doWQOut: WQKms");
 
-            addPointFromWQKms((WQKms)aaf.getData(context),
+            addPointFromWQKms((WQKms)aaf.getData(getContext()),
                 aaf.getFacetDescription(), theme, visible);
         }
         else {
@@ -773,7 +776,7 @@
     }
 
     @Override
-    protected String getChartTitle() {
+    protected String getChartTitle(final CallContext context) {
         return Resources.format(
                 context.getMeta(),
                 I18N_CHART_TITLE,
@@ -782,12 +785,12 @@
     }
 
     @Override
-    protected String getDefaultChartTitle() {
+    protected String getDefaultChartTitle(final CallContext context) {
         return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
     }
 
     @Override
-    protected String getDefaultChartSubtitle() {
+    protected String getDefaultChartSubtitle(final CallContext context) {
         FixAnalysisAccess access = new FixAnalysisAccess(artifact);
         DateRange dateRange = access.getDateRange();
         DateRange refRange  = access.getReferencePeriod();
@@ -808,8 +811,8 @@
     }
 
     @Override
-    protected void addSubtitles(JFreeChart chart) {
-        String defaultSubtitle = getDefaultChartSubtitle();
+    protected void addSubtitles(final CallContext context, final JFreeChart chart) {
+        String defaultSubtitle = getDefaultChartSubtitle(getContext());
 
         if (defaultSubtitle == null || defaultSubtitle.length() == 0) {
             return;
@@ -841,13 +844,13 @@
     }
 
     @Override
-    protected String getDefaultXAxisLabel() {
+    protected String getDefaultXAxisLabel(final CallContext context) {
         return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
     }
 
     @Override
     protected String getDefaultYAxisLabel(int pos) {
-        D4EArtifact flys = (D4EArtifact) master;
+        D4EArtifact flys = getArtifact();
 
         String unit = pos == 0
             ? "cm"
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/AnnotationProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/AnnotationProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -38,7 +38,7 @@
             // Nothing to do
             return;
         }
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         if (!(bundle.getData(context) instanceof RiverAnnotation)) {
             // Just a bit defensive should not happen
             log.error("Incompatible facet in doOut");
@@ -62,7 +62,7 @@
             // Nothing to do
             return;
         }
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         if (!(bundle.getData(context) instanceof RiverAnnotation)) {
             // Just a bit defensive should not happen
             log.error("Incompatible facet in doOut");
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/AreaProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/AreaProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -83,7 +83,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         String seriesName = bundle.getFacetDescription();
         StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme);
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -43,7 +43,7 @@
             ThemeDocument    theme,
             boolean          visible
     ) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
         Map<String, String> metaData = bundle.getFacet().getMetaData(
             bundle.getArtifact(), context);
@@ -74,7 +74,7 @@
             boolean visible,
             int index
     ) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
 
         if (!(data instanceof double[][])) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -42,7 +42,7 @@
             ThemeDocument    theme,
             boolean          visible
     ) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Map<String, String> metaData =
             bundle.getFacet().getMetaData(bundle.getArtifact(), context);
         yAxisLabel = metaData.get("Y");
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedHeightProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedHeightProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -50,7 +50,7 @@
             ThemeDocument    theme,
             boolean          visible) {
         XYSeries series = prepareSeries(
-            bundle, theme, generator.getCallContext());
+            bundle, theme, generator.getContext());
         if (series != null) {
             generator.addAxisSeries(series, axisName, visible);
         }
@@ -65,7 +65,7 @@
             int index
     ) {
         XYSeries series = prepareSeries(
-            bundle, theme, generator.getCallContext());
+            bundle, theme, generator.getContext());
         if (series != null) {
             generator.addAxisSeries(series, index, visible);
         }
@@ -120,7 +120,7 @@
         D4EArtifact flys = (D4EArtifact) generator.getMaster();
         String unit = new RiverAccess(flys).getRiver().getWstUnit().getName();
 
-        CallMeta meta = generator.getCallContext().getMeta();
+        CallMeta meta = generator.getContext().getMeta();
 
         if (yAxisLabel != null && !yAxisLabel.isEmpty()) {
             return Resources.getMsg(
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityDensityProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityDensityProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -37,7 +37,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
                 theme);
         Map<String, String> metaData = bundle.getFacet().getMetaData();
@@ -69,7 +69,7 @@
     @Override
     public String getAxisLabel(DiagramGenerator generator) {
 
-        CallMeta meta = generator.getCallContext().getMeta();
+        CallMeta meta = generator.getContext().getMeta();
 
         if (yAxisLabel != null && !yAxisLabel.isEmpty()) {
             return Resources.getMsg(meta, yAxisLabel);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityDiameterProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityDiameterProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -37,7 +37,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Map<String, String> metaData = bundle.getFacet().getMetaData();
         StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
                 theme);
@@ -71,7 +71,7 @@
     @Override
     public String getAxisLabel(DiagramGenerator generator) {
 
-        CallMeta meta = generator.getCallContext().getMeta();
+        CallMeta meta = generator.getContext().getMeta();
 
         if (yAxisLabel != null && !yAxisLabel.isEmpty()) {
             return Resources.getMsg(meta, yAxisLabel);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityPorosityProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityPorosityProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -37,7 +37,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Map<String, String> metaData = bundle.getFacet().getMetaData();
         StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
                 theme);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedWidthProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedWidthProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -41,7 +41,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Map<String, String> metaData = bundle.getFacet().getMetaData();
         StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
                 theme);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/DeltaWProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/DeltaWProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -88,7 +88,7 @@
     private void doSectorAverageOut(DiagramGenerator generator,
             ArtifactAndFacet bundle,
             ThemeDocument doc, boolean visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         int index = bundle.getFacet().getIndex();
         int sectorNdx = index & 3;
 
@@ -118,7 +118,7 @@
 
     private void doReferenceEventsOut(DiagramGenerator generator,
             ArtifactAndFacet bundle, ThemeDocument doc, boolean visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
 
         KMIndex<QWD> kms =
                 (KMIndex<QWD>)bundle.getData(context);
@@ -148,7 +148,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument doc,
             boolean visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
 
         KMIndex<QWD> kms =
                 (KMIndex<QWD>)bundle.getData(context);
@@ -178,7 +178,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument doc,
             boolean visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
 
         int index = bundle.getFacet().getIndex();
         int sectorNdx = index & 3;
@@ -221,7 +221,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument doc,
             boolean visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
 
         KMIndex<double[]> kms =
                 (KMIndex<double[]>)bundle.getData(context);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/DischargeProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/DischargeProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -60,7 +60,7 @@
             ThemeDocument theme,
             boolean visible
     ) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
         if (data instanceof WQKms) {
             doDischargeOut(
@@ -99,7 +99,7 @@
         ThemeDocument theme,
         boolean       visible
     ) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Map<String, String> metaData = bundle.getFacet().getMetaData(
             bundle.getArtifact(), context);
         WQKms data = (WQKms)bundle.getData(context);
@@ -123,7 +123,7 @@
 
     @Override
     public String getAxisLabel(DiagramGenerator generator) {
-        CallMeta meta = generator.getCallContext().getMeta();
+        CallMeta meta = generator.getContext().getMeta();
         RiverAccess access =
             new RiverAccess((D4EArtifact)generator.getMaster());
         String unit = access.getRiver().getWstUnit().getName();
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/FixDeltaWAProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/FixDeltaWAProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -39,7 +39,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
 
         XYSeries series = new StyledXYSeries(
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/FixDeltaWProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/FixDeltaWProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -39,7 +39,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
 
         XYSeries series = new StyledXYSeries(
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/FixDerivedProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/FixDerivedProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -48,12 +48,12 @@
         ThemeDocument theme,
         boolean visible
     ) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Map<String, String> metaData = bundle.getFacet().getMetaData(
             bundle.getArtifact(), context);
         FixDerivateFacet facet = (FixDerivateFacet)bundle.getFacet();
         FixFunction func = (FixFunction)facet.getData(
-                bundle.getArtifact(), generator.getCallContext());
+                bundle.getArtifact(), generator.getContext());
 
         yAxisLabel = metaData.get("Y");
 
@@ -86,7 +86,7 @@
 
     @Override
     public String getAxisLabel(DiagramGenerator generator) {
-        CallMeta meta = generator.getCallContext().getMeta();
+        CallMeta meta = generator.getContext().getMeta();
         RiverAccess access =
             new RiverAccess((D4EArtifact)generator.getMaster());
         String unit = access.getRiver().getWstUnit().getName();
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/FixWQProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/FixWQProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -111,7 +111,7 @@
     ) {
         log.debug("doSectorAverageOut");
         QWDDateRange qwdd = (QWDDateRange)bundle.getData(
-            generator.getCallContext());
+            generator.getContext());
         QWD qwd = qwdd != null ? qwdd.getQWD() : null;
 
         if(qwd != null) {
@@ -154,7 +154,7 @@
     ) {
         log.debug("doAnalysisEventsOut");
 
-        QWD qwd = (QWD)bundle.getData(generator.getCallContext());
+        QWD qwd = (QWD)bundle.getData(generator.getContext());
 
         if (qwd == null) {
             log.debug("doAnalysisEventsOut: qwd == null");
@@ -206,7 +206,7 @@
         boolean visible) {
         log.debug("doReferenceEventsOut");
 
-        QWI qwd = (QWI)bundle.getData(generator.getCallContext());
+        QWI qwd = (QWI)bundle.getData(generator.getContext());
         if (qwd == null) {
             log.debug("doReferenceEventsOut: qwds == null in "
                 + bundle.getFacetDescription());
@@ -259,7 +259,7 @@
 
         FixWQCurveFacet facet = (FixWQCurveFacet)bundle.getFacet();
         FixFunction func = (FixFunction)facet.getData(
-                bundle.getArtifact(), generator.getCallContext());
+                bundle.getArtifact(), generator.getContext());
 
         if (func == null) {
             log.warn("doWQCurveOut: Facet does not contain FixFunction");
@@ -292,7 +292,7 @@
     ) {
         log.debug("doOutlierOut");
 
-        QWI[] qws = (QWI[])bundle.getData(generator.getCallContext());
+        QWI[] qws = (QWI[])bundle.getData(generator.getContext());
         if(qws != null) {
             XYSeries series = new StyledXYSeries(
                 bundle.getFacetDescription(),
@@ -338,7 +338,7 @@
             return;
         }
 
-        Object qsectorsObj = bundle.getData(generator.getCallContext());
+        Object qsectorsObj = bundle.getData(generator.getContext());
         if (qsectorsObj == null || !(qsectorsObj instanceof List)) {
             log.warn("No QSectors coming from data.");
             return;
@@ -405,7 +405,7 @@
         ThemeDocument    theme,
         boolean          visible
     ) {
-        Object data = bundle.getData(generator.getCallContext());
+        Object data = bundle.getData(generator.getContext());
         List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
         if (data instanceof double[][]) {
             log.debug("Got double[][]");
@@ -430,7 +430,7 @@
             WKms wkms = (WKms) data;
 
             Double ckm =
-                (Double)generator.getCallContext().getContextValue(
+                (Double)generator.getContext().getContextValue(
                     FixChartGenerator.CURRENT_KM);
             double location = (ckm != null)
                     ? ckm.doubleValue()
@@ -454,14 +454,14 @@
         ThemeDocument    theme,
         boolean          visible
     ) {
-        Object data = bundle.getData(generator.getCallContext());
+        Object data = bundle.getData(generator.getContext());
         if (data instanceof WQKms) {
             WQKms wqkms = (WQKms)data;
             // TODO As in doEventsOut, the value-searching should
             // be delivered by the facet already
             XYSeries series = new StyledXYSeries(
                 bundle.getFacetDescription(), theme);
-            Double ckm = (Double) generator.getCallContext()
+            Double ckm = (Double) generator.getContext()
                 .getContextValue(CURRENT_KM);
 
             if (wqkms == null || wqkms.getKms().length == 0 || ckm == null) {
@@ -525,7 +525,7 @@
 
     @Override
     public String getAxisLabel(DiagramGenerator generator) {
-        CallMeta meta = generator.getCallContext().getMeta();
+        CallMeta meta = generator.getContext().getMeta();
 
         RiverAccess access = new RiverAccess((D4EArtifact)generator
             .getMaster());
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/FlowVelocityProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/FlowVelocityProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -41,7 +41,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Map<String, String> metaData = bundle.getFacet().getMetaData();
         StyledXYSeries series = new StyledXYSeries(
             bundle.getFacetDescription(),
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/ManualPointsProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/ManualPointsProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -40,7 +40,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         String seriesName = bundle.getFacetDescription();
         XYSeries series = new StyledXYSeries(seriesName, theme);
         String jsonData = (String) bundle.getData(context);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/MiddleBedHeightProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/MiddleBedHeightProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -43,7 +43,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Map<String, String> metaData = bundle.getFacet().getMetaData();
         yAxisLabel = metaData.get("Y");
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/MiscDischargeProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/MiscDischargeProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -66,7 +66,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
         if (HISTORICAL_DISCHARGE_WQ_Q.equals(bundle.getFacetName())) {
             doHistoricalDischargeOutQ(generator, bundle, theme, visible);
@@ -108,7 +108,7 @@
             boolean visible,
             int axisIndex
     ) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
         /* TODO: Remove the first case.*/
         if (bundle.getFacetName().equals(STATIC_WQ)) {
@@ -145,7 +145,7 @@
     ) {
         XYSeries series = new StyledXYSeries(
             bundle.getFacetDescription(), theme);
-        Object wq = bundle.getData(generator.getCallContext());
+        Object wq = bundle.getData(generator.getContext());
         if (wq instanceof double[][]) {
             double [][] data = (double [][]) wq;
             StyledSeriesBuilder.addPoints(series, data, true);
@@ -278,7 +278,7 @@
         boolean visible
     ) {
         double value = Double.valueOf(
-            bundle.getData(generator.getCallContext()).toString());
+            bundle.getData(generator.getContext()).toString());
         generator.addDomainMarker(
             new StyledValueMarker(value, theme), visible);
     }
@@ -290,7 +290,7 @@
         boolean visible
     ) {
         double value = Double.valueOf(
-            bundle.getData(generator.getCallContext()).toString());
+            bundle.getData(generator.getContext()).toString());
         generator.addValueMarker(
             new StyledValueMarker(value, theme), visible);
     }
@@ -373,7 +373,7 @@
 
     @Override
     public String getAxisLabel(DiagramGenerator generator) {
-        CallMeta meta = generator.getCallContext().getMeta();
+        CallMeta meta = generator.getContext().getMeta();
         RiverAccess access = new RiverAccess((D4EArtifact)generator
             .getMaster());
         String unit = access.getRiver().getWstUnit().getName();
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -47,7 +47,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
         XYSeries series = new StyledXYSeries(
             bundle.getFacetDescription(), theme);
@@ -81,7 +81,7 @@
             boolean          visible,
             int              index)
     {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         WQKms wqkms = (WQKms) bundle.getData(context);
 
         XYSeries series = new StyledXYSeries(
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/SQRelationProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/SQRelationProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -47,7 +47,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         String facetName = bundle.getFacetName();
         StyledXYSeries series;
         Object data = bundle.getData(context);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentDensityProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentDensityProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -41,7 +41,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
                 theme);
         Object data = bundle.getData(context);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentLoadLSProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentLoadLSProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -31,7 +31,7 @@
             ThemeDocument    theme,
             boolean          visible) {
         log.debug("doOut " + bundle.getFacetName());
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
                 false, // Handle NaN
                 theme);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentLoadProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/SedimentLoadProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -41,7 +41,7 @@
             ThemeDocument    theme,
             boolean          visible) {
 
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
                 false, // Handle NaN
                 theme);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/ShearStressProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/ShearStressProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -37,7 +37,7 @@
             ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible) {
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
                 theme);
         String facetName = bundle.getFacetName();
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java	Tue Jun 05 19:10:38 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java	Tue Jun 05 19:21:16 2018 +0200
@@ -50,7 +50,7 @@
             boolean          visible
     ) {
         log.debug("Processing facet: " + bundle.getFacetName());
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
         Object data = bundle.getData(context);
 
         XYSeries series = new StyledXYSeries(
@@ -128,7 +128,7 @@
     {
         log.debug("doOut");
 
-        CallContext context = generator.getCallContext();
+        CallContext context = generator.getContext();
 
         XYSeries series = new StyledXYSeries(
             bundle.getFacetDescription(), theme);
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java	Tue Jun 05 19:10:38 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,413 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-package org.dive4elements.river.jfree;
-
-import org.dive4elements.river.themes.ThemeDocument;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.SortedMap;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Font;
-
-import org.jfree.ui.TextAnchor;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.LegendItem;
-import org.jfree.chart.LegendItemCollection;
-import org.jfree.chart.annotations.XYTextAnnotation;
-import org.jfree.chart.annotations.XYLineAnnotation;
-import org.jfree.chart.renderer.xy.XYItemRenderer;
-
-import org.dive4elements.river.themes.LineStyle;
-import org.dive4elements.river.themes.TextStyle;
-import org.dive4elements.river.exports.ChartSettings;
-import org.dive4elements.river.exports.LegendSection;
-import org.dive4elements.river.exports.ChartArea;
-
-import org.apache.log4j.Logger;
-
-/** Annotation helper class, handles plotting of annotations. */
-public class AnnotationHelper {
-    private static final Logger log = Logger.getLogger(AnnotationHelper.class);
-
-    protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f;
-
-    /* arr this would be better in chartsettings */
-    public static final int    DEFAULT_FONT_SIZE       = 12;
-    public static final String DEFAULT_FONT_NAME       = "Tahoma";
-
-
-    public static void addYAnnotationsToRenderer(
-        SortedMap<Integer, RiverAnnotation> yAnnotations,
-        XYPlot plot,
-        ChartSettings settings,
-        Map<Integer, AxisDataset> datasets
-    ) {
-        List<RiverAnnotation> annotations = new ArrayList<RiverAnnotation>();
-
-        for (Map.Entry<Integer, RiverAnnotation> entry:
-                 yAnnotations.entrySet()) {
-            int axis = entry.getKey();
-            AxisDataset dataset = datasets.get(new Integer(axis));
-
-            if (dataset == null || dataset.getRange() == null) {
-                log.warn("No dataset available and active for axis " + axis);
-            }
-            else {
-                RiverAnnotation ya = entry.getValue();
-                for (StickyAxisAnnotation sta: ya.getAxisTextAnnotations()) {
-                    sta.setAxisSymbol(axis);
-                }
-                annotations.add(ya);
-            }
-        }
-
-        addAnnotationsToRenderer(annotations, plot, settings, datasets);
-    }
-
-    /**
-     * Add annotations (Sticky, Text and hyk zones) to a plot.
-     * @param annotations Annotations to add
-     * @param plot XYPlot to add annotations to.
-     * @param settings ChartSettings object for settings.
-     * @param datasets Map of axis index and datasets
-     */
-    public static void addAnnotationsToRenderer(
-        List<RiverAnnotation> annotations,
-        XYPlot plot,
-        ChartSettings settings,
-        Map<Integer, AxisDataset> datasets
-    ) {
-        if (annotations == null || annotations.isEmpty()) {
-            log.debug("addAnnotationsToRenderer: no annotations.");
-            return;
-        }
-
-        // OPTMIMIZE: Pre-calculate positions
-        ChartArea area = new ChartArea(
-            plot.getDomainAxis(0),
-            plot.getRangeAxis());
-
-        // Walk over all Annotation sets.
-        for (RiverAnnotation fa: annotations) {
-
-            // Access text styling, if any.
-            ThemeDocument theme = fa.getTheme();
-            TextStyle textStyle = null;
-            LineStyle lineStyle = null;
-
-            // Get Theming information and add legend item.
-            if (theme != null) {
-                textStyle = theme.parseComplexTextStyle();
-                lineStyle = theme.parseComplexLineStyle();
-                if (fa.getLabel() != null) {
-                    // Legend handling, maybe misplaced?
-                    LegendItemCollection lic = new LegendItemCollection();
-                    LegendItemCollection old = plot.getFixedLegendItems();
-
-                    Color color = theme.parseLineColorField();
-                    if (color == null) {
-                        color = Color.BLACK;
-                    }
-
-                    Color textColor = theme.parseTextColor();
-                    if (textColor == null) {
-                        textColor = Color.BLACK;
-                    }
-
-                    LegendItem newItem = new LegendItem(fa.getLabel(), color);
-                    LegendSection ls = (settings != null ?
-                            settings.getLegendSection() : null);
-                    newItem.setLabelFont (new Font(
-                        DEFAULT_FONT_NAME,
-                        Font.PLAIN,
-                        ls != null ? ls.getFontSize() : null)
-                    );
-
-                    newItem.setLabelPaint(textColor);
-
-                    lic.add(newItem);
-                    // (Re-)Add prior legend entries.
-                    if (old != null) {
-                        old.addAll(lic);
-                    }
-                    else {
-                        old = lic;
-                    }
-                    plot.setFixedLegendItems(old);
-                }
-            }
-
-            // The 'Sticky' Annotations (at axis, with line and text).
-            for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) {
-                addStickyAnnotation(
-                    sta, plot, area, lineStyle, textStyle, theme,
-                    datasets.get(new Integer(sta.getAxisSymbol())));
-            }
-
-            // Other Text Annotations (e.g. labels of (manual) points).
-            for (XYTextAnnotation ta: fa.getTextAnnotations()) {
-                // Style the text.
-                if (textStyle != null) {
-                    textStyle.apply(ta);
-                }
-                ta.setY(area.above(0.05d, ta.getY()));
-                plot.getRenderer().addAnnotation(
-                    ta, org.jfree.ui.Layer.FOREGROUND);
-            }
-        }
-    }
-
-    /**
-     * Add a text and a line annotation.
-     * @param area convenience to determine positions in plot.
-     * @param theme (optional) theme document
-     */
-    public static void addStickyAnnotation(
-        StickyAxisAnnotation annotation,
-        XYPlot plot,
-        ChartArea area,
-        LineStyle lineStyle,
-        TextStyle textStyle,
-        ThemeDocument theme,
-        AxisDataset dataset
-    ) {
-        // OPTIMIZE pre-calculate area-related values
-        final float TEXT_OFF = 0.03f;
-
-        XYLineAnnotation lineAnnotation = null;
-        XYTextAnnotation textAnnotation = null;
-
-        int axisIndex = annotation.getAxisSymbol();
-        XYItemRenderer renderer = null;
-        if (dataset != null && dataset.getDatasets().length > 0) {
-            renderer = plot.getRendererForDataset(dataset.getDatasets()[0]);
-        }
-        else {
-            renderer = plot.getRenderer();
-        }
-
-        if (annotation.atX()) {
-            textAnnotation = new CollisionFreeXYTextAnnotation(
-                annotation.getText(),
-                annotation.getPos(),
-                area.ofGround(TEXT_OFF));
-            // OPTIMIZE externalize the calculation involving PI.
-            //textAnnotation.setRotationAngle(270f*Math.PI/180f);
-            lineAnnotation = createGroundStickAnnotation(
-                area, annotation.getPos(), lineStyle);
-            textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT);
-            textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT);
-        }
-        else {
-            // Stick to the "right" (opposed to left) Y-Axis.
-            if (axisIndex != 0 && plot.getRangeAxis(axisIndex) != null) {
-                // OPTIMIZE: Pass a different area to this function,
-                //           do the adding to renderer outside (let this
-                //           function return the annotations).
-                //           Note that this path is travelled rarely.
-                textAnnotation = new CollisionFreeXYTextAnnotation(
-                    annotation.getText(),
-                    area.ofRight(TEXT_OFF),
-                    annotation.getPos()
-                );
-                textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT);
-                textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT);
-                lineAnnotation = createRightStickAnnotation(
-                    area, annotation.getPos(), lineStyle);
-
-                // hit-lines for duration curve
-                ChartArea area2 = new ChartArea(
-                    plot.getDomainAxis(), plot.getRangeAxis(axisIndex));
-                if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
-                    // New line annotation to hit curve.
-                    if (theme.parseShowVerticalLine()) {
-                        XYLineAnnotation hitLineAnnotation =
-                            createStickyLineAnnotation(
-                                StickyAxisAnnotation.SimpleAxis.X_AXIS,
-                                annotation.getHitPoint(), annotation.getPos(),
-                                // annotation.getHitPoint(),
-                                area2, lineStyle);
-                        renderer.addAnnotation(hitLineAnnotation,
-                            org.jfree.ui.Layer.BACKGROUND);
-                    }
-                    if (theme.parseShowHorizontalLine()) {
-                        XYLineAnnotation lineBackAnnotation =
-                            createStickyLineAnnotation(
-                                StickyAxisAnnotation.SimpleAxis.Y_AXIS2,
-                                annotation.getPos(), annotation.getHitPoint(),
-                                area2, lineStyle);
-                        renderer.addAnnotation(lineBackAnnotation,
-                            org.jfree.ui.Layer.BACKGROUND);
-                    }
-                }
-            }
-            else { // Stick to the left y-axis.
-                textAnnotation = new CollisionFreeXYTextAnnotation(
-                    annotation.getText(),
-                    area.ofLeft(TEXT_OFF),
-                    annotation.getPos());
-                textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT);
-                textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT);
-                lineAnnotation = createLeftStickAnnotation(
-                    area, annotation.getPos(), lineStyle);
-                if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
-                    // New line annotation to hit curve.
-                    if (theme.parseShowHorizontalLine()) {
-                        XYLineAnnotation hitLineAnnotation =
-                            createStickyLineAnnotation(
-                                StickyAxisAnnotation.SimpleAxis.Y_AXIS,
-                                annotation.getPos(), annotation.getHitPoint(),
-                                area, lineStyle);
-                        renderer.addAnnotation(hitLineAnnotation,
-                            org.jfree.ui.Layer.BACKGROUND);
-                    }
-                    if (theme.parseShowVerticalLine()) {
-                        XYLineAnnotation lineBackAnnotation =
-                            createStickyLineAnnotation(
-                                StickyAxisAnnotation.SimpleAxis.X_AXIS,
-                                annotation.getHitPoint(), annotation.getPos(),
-                                area, lineStyle);
-                        renderer.addAnnotation(lineBackAnnotation,
-                            org.jfree.ui.Layer.BACKGROUND);
-                    }
-                }
-            }
-        }
-
-        // Style the text.
-        if (textStyle != null) {
-            textStyle.apply(textAnnotation);
-        }
-
-        // Add the Annotations to renderer.
-        renderer.addAnnotation(textAnnotation, org.jfree.ui.Layer.FOREGROUND);
-        renderer.addAnnotation(lineAnnotation, org.jfree.ui.Layer.FOREGROUND);
-    }
-
-   /**
-     * Create annotation that sticks to "ground" (X) axis.
-     * @param area helper to calculate coordinates
-     * @param pos one-dimensional position (distance from axis)
-     * @param lineStyle the line style to use for the line.
-     */
-    public static XYLineAnnotation createGroundStickAnnotation(
-        ChartArea area, float pos, LineStyle lineStyle
-    ) {
-        // Style the line.
-        if (lineStyle != null) {
-            return new XYLineAnnotation(
-                pos, area.atGround(),
-                pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET),
-                new BasicStroke(lineStyle.getWidth()),lineStyle.getColor());
-        }
-        else {
-            return new XYLineAnnotation(
-                pos, area.atGround(),
-                pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET));
-        }
-    }
-
-
-    /**
-     * Create annotation that sticks to the second Y axis ("right").
-     * @param area helper to calculate coordinates
-     * @param pos one-dimensional position (distance from axis)
-     * @param lineStyle the line style to use for the line.
-     */
-    public static XYLineAnnotation createRightStickAnnotation(
-        ChartArea area, float pos, LineStyle lineStyle
-    ) {
-        // Style the line.
-        if (lineStyle != null) {
-            return new XYLineAnnotation(
-                area.atRight(), pos,
-                area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos,
-                new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
-        }
-        else {
-            return new XYLineAnnotation(
-                area.atRight(), pos,
-                area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos);
-        }
-    }
-    /**
-     * Create annotation that sticks to the first Y axis ("left").
-     * @param area helper to calculate coordinates
-     * @param pos one-dimensional position (distance from axis)
-     * @param lineStyle the line style to use for the line.
-     */
-    public static XYLineAnnotation createLeftStickAnnotation(
-        ChartArea area, float pos, LineStyle lineStyle
-    ) {
-        // Style the line.
-        if (lineStyle != null) {
-            return new XYLineAnnotation(
-                area.atLeft(), pos,
-                area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos,
-                new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
-        }
-        else {
-            return new XYLineAnnotation(
-                area.atLeft(), pos,
-                area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos);
-        }
-    }
-
-
-    /**
-     * Create a line from a axis to a given point.
-     * @param axis   The "simple" axis.
-     * @param fromD1 from-location in first dimension.
-     * @param toD2   to-location in second dimension.
-     * @param area   helper to calculate offsets.
-     * @param lineStyle optional line style.
-     */
-    public static XYLineAnnotation createStickyLineAnnotation(
-        StickyAxisAnnotation.SimpleAxis axis, float fromD1, float toD2,
-        ChartArea area, LineStyle lineStyle
-    ) {
-        double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d;
-        switch(axis) {
-            case X_AXIS:
-                anchorX1 = fromD1;
-                anchorX2 = fromD1;
-                anchorY1 = area.atGround();
-                anchorY2 = toD2;
-                break;
-            case Y_AXIS:
-                anchorX1 = area.atLeft();
-                anchorX2 = toD2;
-                anchorY1 = fromD1;
-                anchorY2 = fromD1;
-                break;
-            case Y_AXIS2:
-                anchorX1 = area.atRight();
-                anchorX2 = toD2;
-                anchorY1 = fromD1;
-                anchorY2 = fromD1;
-                break;
-        }
-        // Style the line.
-        if (lineStyle != null) {
-            return new XYLineAnnotation(
-                anchorX1, anchorY1,
-                anchorX2, anchorY2,
-                new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
-        }
-        else {
-            return new XYLineAnnotation(
-                anchorX1, anchorY1,
-                anchorX2, anchorY2);
-        }
-    }
-
-};

http://dive4elements.wald.intevation.org