aheinecke@7044: /* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde aheinecke@7044: * Software engineering by Intevation GmbH aheinecke@7044: * aheinecke@7044: * This file is Free Software under the GNU AGPL (>=v3) aheinecke@7044: * and comes with ABSOLUTELY NO WARRANTY! Check out the aheinecke@7044: * documentation coming with Dive4Elements River for details. aheinecke@7044: */ aheinecke@7044: aheinecke@7044: package org.dive4elements.river.exports; aheinecke@7044: aheinecke@7044: import java.awt.Color; aheinecke@7044: import java.awt.Font; aheinecke@7044: import java.text.NumberFormat; aheinecke@7044: import java.util.ArrayList; aheinecke@7044: import java.util.HashMap; aheinecke@7044: import java.util.List; aheinecke@7044: import java.util.Map; aheinecke@7044: aheinecke@7044: import javax.swing.ImageIcon; aheinecke@7044: aheinecke@7044: import org.apache.log4j.Logger; aheinecke@7044: import org.jfree.chart.ChartFactory; aheinecke@7044: import org.jfree.chart.JFreeChart; aheinecke@7044: import org.jfree.chart.LegendItem; aheinecke@7044: import org.jfree.chart.annotations.XYAnnotation; aheinecke@7044: import org.jfree.chart.annotations.XYImageAnnotation; aheinecke@7044: import org.jfree.chart.annotations.XYTextAnnotation; aheinecke@7044: import org.jfree.chart.axis.NumberAxis; aheinecke@7044: import org.jfree.chart.axis.ValueAxis; aheinecke@7044: import org.jfree.chart.axis.LogarithmicAxis; aheinecke@7044: import org.jfree.chart.plot.Marker; aheinecke@7044: import org.jfree.chart.plot.PlotOrientation; aheinecke@7044: import org.jfree.chart.plot.XYPlot; aheinecke@7044: import org.jfree.data.Range; aheinecke@7044: import org.jfree.data.general.Series; aheinecke@7044: import org.jfree.data.xy.XYDataset; aheinecke@7044: import org.jfree.data.xy.XYSeries; aheinecke@7044: import org.jfree.data.xy.XYSeriesCollection; aheinecke@7044: aheinecke@7044: import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; aheinecke@7044: import org.dive4elements.artifactdatabase.state.Facet; aheinecke@7068: import org.dive4elements.river.artifacts.D4EArtifact; aheinecke@7059: import org.dive4elements.river.exports.process.Processor; aheinecke@7044: import org.dive4elements.river.jfree.AxisDataset; aheinecke@7044: import org.dive4elements.river.jfree.AnnotationHelper; aheinecke@7044: import org.dive4elements.river.jfree.Bounds; aheinecke@7044: import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; aheinecke@7044: import org.dive4elements.river.jfree.DoubleBounds; aheinecke@7044: import org.dive4elements.river.jfree.RiverAnnotation; aheinecke@7044: import org.dive4elements.river.jfree.StyledAreaSeriesCollection; aheinecke@7044: import org.dive4elements.river.jfree.StyledXYSeries; aheinecke@7044: import org.dive4elements.river.themes.ThemeDocument; aheinecke@7044: teichmann@7051: import org.w3c.dom.Element; teichmann@7051: aheinecke@7044: /** aheinecke@7044: * The main diagram creation class. aheinecke@7044: * aheinecke@7044: * This class is the glue between output processors and facets. aheinecke@7044: * The generator creates one diagram and calls the appropiate aheinecke@7044: * processors for the state and aheinecke@7044: * aheinecke@7044: * With respect to datasets, ranges and axis, there are following requirements: aheinecke@7044: * aheinecke@7044: */ aheinecke@7068: public class DiagramGenerator extends ChartGenerator2 { aheinecke@7044: aheinecke@7044: public static final int AXIS_SPACE = 5; aheinecke@7044: aheinecke@7044: /** The logger that is used in this generator. */ aheinecke@7044: private static Logger logger = Logger.getLogger(DiagramGenerator.class); aheinecke@7044: aheinecke@7044: protected List domainMarkers = new ArrayList(); aheinecke@7044: aheinecke@7044: protected List valueMarkers = new ArrayList(); aheinecke@7044: aheinecke@7044: /** The max X range to include all X values of all series for each axis. */ aheinecke@7044: protected Map xBounds; aheinecke@7044: aheinecke@7044: /** The max Y range to include all Y values of all series for each axis. */ aheinecke@7044: protected Map yBounds; aheinecke@7044: aheinecke@7044: /** Whether or not the plot is inverted (left-right). */ aheinecke@7044: private boolean inverted; aheinecke@7044: teichmann@7051: protected DiagramAttributes diagramAttributes; teichmann@7051: aheinecke@7044: public DiagramGenerator() { aheinecke@7044: super(); aheinecke@7044: aheinecke@7044: xBounds = new HashMap(); aheinecke@7044: yBounds = new HashMap(); aheinecke@7044: } aheinecke@7044: teichmann@7051: @Override teichmann@7087: public void setup(Object config) { teichmann@7087: Element cfg = (Element)config; teichmann@7051: logger.debug("DiagramGenerator.setup"); teichmann@7086: // TODO: XML DOM is not thread safe! We have to re-factor this. teichmann@7087: synchronized (cfg.getOwnerDocument()) { teichmann@7087: diagramAttributes = new DiagramAttributes(cfg); teichmann@7086: } teichmann@7051: } teichmann@7051: aheinecke@7044: /** aheinecke@7044: * Generate the chart anew (including localized axis and all). aheinecke@7044: */ aheinecke@7044: @Override aheinecke@7044: public JFreeChart generateChart() { aheinecke@7044: logger.debug("DiagramGenerator.generateChart"); aheinecke@7044: aheinecke@7044: JFreeChart chart = ChartFactory.createXYLineChart( aheinecke@7044: getChartTitle(), aheinecke@7084: "", aheinecke@7084: "", aheinecke@7044: null, aheinecke@7044: PlotOrientation.VERTICAL, aheinecke@7044: isLegendVisible(), aheinecke@7044: false, aheinecke@7044: false); aheinecke@7044: aheinecke@7044: XYPlot plot = (XYPlot) chart.getPlot(); aheinecke@7044: ValueAxis axis = createXAxis(getXAxisLabel()); aheinecke@7044: plot.setDomainAxis(axis); aheinecke@7044: aheinecke@7044: chart.setBackgroundPaint(Color.WHITE); aheinecke@7044: plot.setBackgroundPaint(Color.WHITE); aheinecke@7044: addSubtitles(chart); aheinecke@7044: adjustPlot(plot); aheinecke@7044: aheinecke@7044: //debugAxis(plot); aheinecke@7044: aheinecke@7044: addDatasets(plot); aheinecke@7044: aheinecke@7044: //debugDatasets(plot); aheinecke@7044: aheinecke@7044: addMarkers(plot); aheinecke@7044: aheinecke@7044: recoverEmptyPlot(plot); aheinecke@7044: preparePointRanges(plot); aheinecke@7044: aheinecke@7044: //debugAxis(plot); aheinecke@7044: aheinecke@7044: localizeAxes(plot); aheinecke@7044: adjustAxes(plot); aheinecke@7044: if (!(axis instanceof LogarithmicAxis)) { aheinecke@7044: // XXX: aheinecke@7044: // The auto zoom without a range tries aheinecke@7044: // to include 0 in a logarithmic axis aheinecke@7044: // which triggers a bug in jfreechart that causes aheinecke@7044: // the values to be drawn carthesian aheinecke@7044: autoZoom(plot); aheinecke@7044: } aheinecke@7044: aheinecke@7044: //debugAxis(plot); aheinecke@7044: aheinecke@7044: // These have to go after the autozoom. aheinecke@7044: AnnotationHelper.addAnnotationsToRenderer(annotations, plot, aheinecke@7044: getChartSettings(), datasets); aheinecke@7044: aheinecke@7044: // Add a logo (maybe). aheinecke@7044: addLogo(plot); aheinecke@7044: aheinecke@7044: aggregateLegendEntries(plot); aheinecke@7044: aheinecke@7044: return chart; aheinecke@7044: } aheinecke@7044: aheinecke@7084: public String getOutName() { aheinecke@7084: return outName; aheinecke@7084: } aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Return left most data points x value (on first axis). aheinecke@7044: * Shortcut, especially to be overridden in (LS) charts where aheinecke@7044: * axis could be inverted. aheinecke@7044: */ aheinecke@7044: protected double getLeftX() { aheinecke@7044: return (Double)getXBounds(0).getLower(); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Return right most data points x value (on first axis). aheinecke@7044: * Shortcut, especially to be overridden in (LS) charts where aheinecke@7044: * axis could be inverted. aheinecke@7044: */ aheinecke@7044: protected double getRightX() { aheinecke@7044: return (Double)getXBounds(0).getUpper(); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** Add a logo as background annotation to plot. */ aheinecke@7044: protected void addLogo(XYPlot plot) { aheinecke@7044: String logo = showLogo(); aheinecke@7044: if (logo == null) { aheinecke@7044: logger.debug("No logo to show chosen"); aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: ImageIcon imageIcon = null; aheinecke@7044: if (logo.equals("none")) { aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: /* aheinecke@7044: If you want to add images, remember to change code in these places: aheinecke@7044: flys-artifacts: aheinecke@7044: DiagramGenerator.java aheinecke@7044: Timeseries*Generator.java and aheinecke@7044: in the flys-client projects Chart*Propert*Editor.java. aheinecke@7044: Also, these images have to be put in aheinecke@7044: flys-artifacts/src/main/resources/images/ aheinecke@7044: flys-client/src/main/webapp/images/ aheinecke@7044: */ aheinecke@7044: java.net.URL imageURL; aheinecke@7044: if (logo.equals("Intevation")) { aheinecke@7044: imageURL = DiagramGenerator.class.getResource("/images/intevation.png"); aheinecke@7044: } aheinecke@7044: else { // TODO else if ... aheinecke@7044: imageURL = DiagramGenerator.class.getResource("/images/bfg_logo.gif"); aheinecke@7044: } aheinecke@7044: imageIcon = new ImageIcon(imageURL); aheinecke@7044: aheinecke@7044: aheinecke@7044: double xPos = 0d, yPos = 0d; aheinecke@7044: aheinecke@7044: String placeh = logoHPlace(); aheinecke@7044: String placev = logoVPlace(); aheinecke@7044: aheinecke@7044: if (placev == null || placev.equals("none")) { aheinecke@7044: placev = "top"; aheinecke@7044: } aheinecke@7044: if (placev.equals("top")) { aheinecke@7044: yPos = (Double)getYBounds(0).getUpper(); aheinecke@7044: } aheinecke@7044: else if (placev.equals("bottom")) { aheinecke@7044: yPos = (Double)getYBounds(0).getLower(); aheinecke@7044: } aheinecke@7044: else if (placev.equals("center")) { aheinecke@7044: yPos = ((Double)getYBounds(0).getUpper() + (Double)getYBounds(0).getLower())/2d; aheinecke@7044: } aheinecke@7044: else { aheinecke@7044: logger.debug("Unknown place-v value: " + placev); aheinecke@7044: } aheinecke@7044: aheinecke@7044: if (placeh == null || placeh.equals("none")) { aheinecke@7044: placeh = "center"; aheinecke@7044: } aheinecke@7044: if (placeh.equals("left")) { aheinecke@7044: xPos = getLeftX(); aheinecke@7044: } aheinecke@7044: else if (placeh.equals("right")) { aheinecke@7044: xPos = getRightX(); aheinecke@7044: } aheinecke@7044: else if (placeh.equals("center")) { aheinecke@7044: xPos = ((Double)getXBounds(0).getUpper() + (Double)getXBounds(0).getLower())/2d; aheinecke@7044: } aheinecke@7044: else { aheinecke@7044: logger.debug("Unknown place-h value: " + placeh); aheinecke@7044: } aheinecke@7044: aheinecke@7044: logger.debug("logo position: " + xPos + "/" + yPos); aheinecke@7044: aheinecke@7044: org.jfree.ui.RectangleAnchor anchor aheinecke@7044: = org.jfree.ui.RectangleAnchor.TOP; aheinecke@7044: if (placev.equals("top")) { aheinecke@7044: if (placeh.equals("left")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.TOP_LEFT; aheinecke@7044: } aheinecke@7044: else if (placeh.equals("right")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.TOP_RIGHT; aheinecke@7044: } aheinecke@7044: else if (placeh.equals("center")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.TOP; aheinecke@7044: } aheinecke@7044: } aheinecke@7044: else if (placev.equals("bottom")) { aheinecke@7044: if (placeh.equals("left")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.BOTTOM_LEFT; aheinecke@7044: } aheinecke@7044: else if (placeh.equals("right")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.BOTTOM_RIGHT; aheinecke@7044: } aheinecke@7044: else if (placeh.equals("center")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.BOTTOM; aheinecke@7044: } aheinecke@7044: } aheinecke@7044: else if (placev.equals("center")) { aheinecke@7044: if (placeh.equals("left")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.LEFT; aheinecke@7044: } aheinecke@7044: else if (placeh.equals("right")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.RIGHT; aheinecke@7044: } aheinecke@7044: else if (placeh.equals("center")) { aheinecke@7044: anchor = org.jfree.ui.RectangleAnchor.CENTER; aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: XYAnnotation xyannotation = aheinecke@7044: new XYImageAnnotation(xPos, yPos, imageIcon.getImage(), anchor); aheinecke@7044: plot.getRenderer().addAnnotation(xyannotation, org.jfree.ui.Layer.BACKGROUND); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: protected NumberAxis createXAxis(String label) { aheinecke@7044: return new NumberAxis(label); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: @Override aheinecke@7044: protected Series getSeriesOf(XYDataset dataset, int idx) { aheinecke@7044: return ((XYSeriesCollection) dataset).getSeries(idx); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: @Override aheinecke@7044: protected AxisDataset createAxisDataset(int idx) { aheinecke@7044: logger.debug("Create new AxisDataset for index: " + idx); aheinecke@7044: return new AxisDataset(idx); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Put debug output about datasets. aheinecke@7044: */ aheinecke@7044: public void debugDatasets(XYPlot plot) { aheinecke@7044: logger.debug("Number of datasets: " + plot.getDatasetCount()); aheinecke@7044: for (int i = 0, P = plot.getDatasetCount(); i < P; i++) { aheinecke@7044: if (plot.getDataset(i) == null) { aheinecke@7044: logger.debug("Dataset #" + i + " is null"); aheinecke@7044: continue; aheinecke@7044: } aheinecke@7044: logger.debug("Dataset #" + i + ":" + plot.getDataset(i)); aheinecke@7044: XYSeriesCollection series = (XYSeriesCollection) plot.getDataset(i); aheinecke@7044: logger.debug("X-Extend of Dataset: " + series.getSeries(0).getMinX() aheinecke@7044: + " " + series.getSeries(0).getMaxX()); aheinecke@7044: logger.debug("Y-Extend of Dataset: " + series.getSeries(0).getMinY() aheinecke@7044: + " " + series.getSeries(0).getMaxY()); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Put debug output about axes. aheinecke@7044: */ aheinecke@7044: public void debugAxis(XYPlot plot) { aheinecke@7044: logger.debug("..............."); aheinecke@7044: for (int i = 0, P = plot.getRangeAxisCount(); i < P; i++) { aheinecke@7044: if (plot.getRangeAxis(i) == null) aheinecke@7044: logger.debug("Range-Axis #" + i + " == null"); aheinecke@7044: else { aheinecke@7044: logger.debug("Range-Axis " + i + " != null [" + aheinecke@7044: plot.getRangeAxis(i).getRange().getLowerBound() + aheinecke@7044: " " + plot.getRangeAxis(i).getRange().getUpperBound() + aheinecke@7044: "]"); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: for (int i = 0, P = plot.getDomainAxisCount(); i < P; i++) { aheinecke@7044: if (plot.getDomainAxis(i) == null) aheinecke@7044: logger.debug("Domain-Axis #" + i + " == null"); aheinecke@7044: else { aheinecke@7044: logger.debug("Domain-Axis " + i + " != null [" + aheinecke@7044: plot.getDomainAxis(i).getRange().getLowerBound() + aheinecke@7044: " " + plot.getDomainAxis(i).getRange().getUpperBound() + aheinecke@7044: "]"); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: logger.debug("..............."); aheinecke@7044: } aheinecke@7044: aheinecke@7068: /** aheinecke@7068: * Registers an area to be drawn. aheinecke@7068: * @param area Area to be drawn. aheinecke@7068: * @param axisName Name of the axis. aheinecke@7068: * @param visible Whether or not to be visible (important for range calculations). aheinecke@7068: */ aheinecke@7068: public void addAreaSeries(StyledAreaSeriesCollection area, String axisName, boolean visible) { aheinecke@7068: addAreaSeries(area, diagramAttributes.getAxisIndex(axisName), visible); aheinecke@7068: } aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Registers an area to be drawn. aheinecke@7044: * @param area Area to be drawn. aheinecke@7044: * @param index 'axis index' aheinecke@7044: * @param visible Whether or not to be visible (important for range calculations). aheinecke@7044: */ aheinecke@7044: public void addAreaSeries(StyledAreaSeriesCollection area, int index, boolean visible) { aheinecke@7044: if (area == null) { aheinecke@7044: logger.warn("Cannot yet render above/under curve."); aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: AxisDataset axisDataset = (AxisDataset) getAxisDataset(index); aheinecke@7044: aheinecke@7044: if (visible) { aheinecke@7044: axisDataset.addArea(area); aheinecke@7044: } aheinecke@7044: else { aheinecke@7044: /* No range merging, for areas extending to infinity this aheinecke@7044: * causes problems. */ aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Add given series if visible, if not visible adjust ranges (such that aheinecke@7044: * all points in data would be plotted once visible). aheinecke@7068: * @param series the data series to include in plot. aheinecke@7068: * @param index index of the axis. aheinecke@7068: * @param visible whether or not the data should be plotted. aheinecke@7044: */ aheinecke@7044: public void addAxisSeries(XYSeries series, int index, boolean visible) { aheinecke@7044: if (series == null) { aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: logger.debug("Y Range of XYSeries: " + aheinecke@7044: series.getMinY() + " | " + series.getMaxY()); aheinecke@7044: aheinecke@7044: addAxisDataset(new XYSeriesCollection(series), index, visible); aheinecke@7068: } aheinecke@7044: aheinecke@7068: /** aheinecke@7068: * Add given series if visible, if not visible adjust ranges (such that aheinecke@7068: * all points in data would be plotted once visible). aheinecke@7068: * @param series the data series to include in plot. aheinecke@7068: * @param axisName name of the axis. aheinecke@7068: * @param visible whether or not the data should be plotted. aheinecke@7068: */ aheinecke@7068: public void addAxisSeries(XYSeries series, String axisName, boolean visible) { aheinecke@7068: addAxisSeries(series, diagramAttributes.getAxisIndex(axisName), visible); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Add the given vertical marker to the chart. aheinecke@7044: */ aheinecke@7044: public void addDomainMarker(Marker marker) { aheinecke@7044: addDomainMarker(marker, true); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Add the given vertical marker to the chart.Note: the marker is aheinecke@7044: * added to the chart only if it is not null and if visible is true. aheinecke@7044: * @param marker The marker that should be added to the chart. aheinecke@7044: * @param visible The visibility of the marker. aheinecke@7044: */ aheinecke@7044: public void addDomainMarker(Marker marker, boolean visible) { aheinecke@7044: if (visible && marker != null) { aheinecke@7044: domainMarkers.add(marker); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Add the given vertical marker to the chart. aheinecke@7044: */ aheinecke@7044: public void addValueMarker(Marker marker) { aheinecke@7044: addValueMarker(marker, true); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Add the given horizontal marker to the chart.Note: the marker is aheinecke@7044: * added to the chart only if it is not null and if visible is true. aheinecke@7044: * @param marker The marker that should be added to the chart. aheinecke@7044: * @param visible The visibility of the marker. aheinecke@7044: */ aheinecke@7044: public void addValueMarker(Marker marker, boolean visible) { aheinecke@7044: if (visible && marker != null) { aheinecke@7044: valueMarkers.add(marker); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: protected void addMarkers(XYPlot plot) { aheinecke@7044: for(Marker marker : domainMarkers) { aheinecke@7044: plot.addDomainMarker(marker); aheinecke@7044: } aheinecke@7044: for(Marker marker : valueMarkers) { aheinecke@7044: plot.addRangeMarker(marker); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Effect: extend range of x axis to include given limits. aheinecke@7044: * aheinecke@7044: * @param bounds the given ("minimal") bounds. aheinecke@7044: * @param index index of axis to be merged. aheinecke@7044: */ aheinecke@7044: @Override aheinecke@7044: protected void combineXBounds(Bounds bounds, int index) { aheinecke@7044: if (!(bounds instanceof DoubleBounds)) { aheinecke@7044: logger.warn("Unsupported Bounds type: " + bounds.getClass()); aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: DoubleBounds dBounds = (DoubleBounds) bounds; aheinecke@7044: aheinecke@7044: if (dBounds == null aheinecke@7044: || Double.isNaN((Double) dBounds.getLower()) aheinecke@7044: || Double.isNaN((Double) dBounds.getUpper())) { aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: Bounds old = getXBounds(index); aheinecke@7044: aheinecke@7044: if (old != null) { aheinecke@7044: dBounds = (DoubleBounds) dBounds.combine(old); aheinecke@7044: } aheinecke@7044: aheinecke@7044: setXBounds(index, dBounds); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: @Override aheinecke@7044: protected void combineYBounds(Bounds bounds, int index) { aheinecke@7044: if (!(bounds instanceof DoubleBounds)) { aheinecke@7044: logger.warn("Unsupported Bounds type: " + bounds.getClass()); aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: DoubleBounds dBounds = (DoubleBounds) bounds; aheinecke@7044: aheinecke@7044: if (dBounds == null aheinecke@7044: || Double.isNaN((Double) dBounds.getLower()) aheinecke@7044: || Double.isNaN((Double) dBounds.getUpper())) { aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: Bounds old = getYBounds(index); aheinecke@7044: aheinecke@7044: if (old != null) { aheinecke@7044: dBounds = (DoubleBounds) dBounds.combine(old); aheinecke@7044: } aheinecke@7044: aheinecke@7044: setYBounds(index, dBounds); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * If no data is visible, draw at least empty axis. aheinecke@7044: */ aheinecke@7044: private void recoverEmptyPlot(XYPlot plot) { aheinecke@7044: if (plot.getRangeAxis() == null) { aheinecke@7044: logger.debug("debug: No range axis"); aheinecke@7044: plot.setRangeAxis(createYAxis(0)); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Expands X axes if only a point is shown. aheinecke@7044: */ aheinecke@7044: private void preparePointRanges(XYPlot plot) { aheinecke@7044: for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { aheinecke@7044: aheinecke@7044: Integer key = Integer.valueOf(i); aheinecke@7044: Bounds b = getXBounds(key); aheinecke@7044: aheinecke@7044: aheinecke@7044: if (b != null && b.getLower().equals(b.getUpper())) { aheinecke@7044: logger.debug("Check whether to expand a x axis.i ("+b.getLower() + "-" + b.getUpper()+")"); aheinecke@7044: setXBounds(key, ChartHelper.expandBounds(b, 5)); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * This method zooms the plot to the specified ranges in the attribute aheinecke@7044: * document or to the ranges specified by the min/max values in the aheinecke@7044: * datasets. Note: We determine the range manually if no zoom ranges aheinecke@7044: * are given, because JFreeCharts auto-zoom adds a margin to the left and aheinecke@7044: * right of the data area. aheinecke@7044: * aheinecke@7044: * @param plot The XYPlot. aheinecke@7044: */ aheinecke@7044: protected void autoZoom(XYPlot plot) { aheinecke@7044: logger.debug("Zoom to specified ranges."); aheinecke@7044: aheinecke@7044: Range xrange = getDomainAxisRange(); aheinecke@7044: Range yrange = getValueAxisRange(); aheinecke@7044: aheinecke@7044: ValueAxis xAxis = plot.getDomainAxis(); aheinecke@7044: aheinecke@7044: Range fixedXRange = getRangeForAxisFromSettings("X"); aheinecke@7044: if (fixedXRange != null) { aheinecke@7044: xAxis.setRange(fixedXRange); aheinecke@7044: } aheinecke@7044: else { aheinecke@7044: zoomX(plot, xAxis, getXBounds(0), xrange); aheinecke@7044: } aheinecke@7044: aheinecke@7044: for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { aheinecke@7044: ValueAxis yaxis = plot.getRangeAxis(i); aheinecke@7044: aheinecke@7044: if (yaxis instanceof IdentifiableNumberAxis) { aheinecke@7044: IdentifiableNumberAxis idAxis = (IdentifiableNumberAxis) yaxis; aheinecke@7044: aheinecke@7044: Range fixedRange = getRangeForAxisFromSettings(idAxis.getId()); aheinecke@7044: if (fixedRange != null) { aheinecke@7044: yaxis.setRange(fixedRange); aheinecke@7044: continue; aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: if (yaxis == null) { aheinecke@7044: logger.debug("Zoom problem: no Y Axis for index: " + i); aheinecke@7044: continue; aheinecke@7044: } aheinecke@7044: aheinecke@7044: logger.debug("Prepare zoom settings for y axis at index: " + i); aheinecke@7044: zoomY(plot, yaxis, getYBounds(Integer.valueOf(i)), yrange); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: protected Range getDomainAxisRange() { aheinecke@7044: String[] ranges = getDomainAxisRangeFromRequest(); aheinecke@7044: aheinecke@7044: if (ranges == null || ranges.length < 2) { aheinecke@7044: logger.debug("No zoom range for domain axis specified."); aheinecke@7044: return null; aheinecke@7044: } aheinecke@7044: aheinecke@7044: if (ranges[0].length() > 0 && ranges[1].length() > 0) { aheinecke@7044: try { aheinecke@7044: double from = Double.parseDouble(ranges[0]); aheinecke@7044: double to = Double.parseDouble(ranges[1]); aheinecke@7044: aheinecke@7044: if (from == 0 && to == 0) { aheinecke@7044: logger.debug("No range specified. Lower and upper X == 0"); aheinecke@7044: return null; aheinecke@7044: } aheinecke@7044: aheinecke@7044: if (from > to) { aheinecke@7044: double tmp = to; aheinecke@7044: to = from; aheinecke@7044: from = tmp; aheinecke@7044: } aheinecke@7044: aheinecke@7044: return new Range(from, to); aheinecke@7044: } aheinecke@7044: catch (NumberFormatException nfe) { aheinecke@7044: logger.warn("Wrong values for domain axis range."); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: return null; aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: protected Range getValueAxisRange() { aheinecke@7044: String[] ranges = getValueAxisRangeFromRequest(); aheinecke@7044: aheinecke@7044: if (ranges == null || ranges.length < 2) { aheinecke@7044: logger.debug("No range specified. Lower and upper Y == 0"); aheinecke@7044: return null; aheinecke@7044: } aheinecke@7044: aheinecke@7044: if (ranges[0].length() > 0 && ranges[1].length() > 0) { aheinecke@7044: try { aheinecke@7044: double from = Double.parseDouble(ranges[0]); aheinecke@7044: double to = Double.parseDouble(ranges[1]); aheinecke@7044: aheinecke@7044: if (from == 0 && to == 0) { aheinecke@7044: logger.debug("No range specified. Lower and upper Y == 0"); aheinecke@7044: return null; aheinecke@7044: } aheinecke@7044: aheinecke@7044: return from > to aheinecke@7044: ? new Range(to, from) aheinecke@7044: : new Range(from, to); aheinecke@7044: } aheinecke@7044: catch (NumberFormatException nfe) { aheinecke@7044: logger.warn("Wrong values for value axis range."); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: return null; aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) { aheinecke@7044: return zoom(plot, axis, bounds, x); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: protected boolean zoomY(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) { aheinecke@7044: return zoom(plot, axis, bounds, x); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Zooms the x axis to the range specified in the attribute document. aheinecke@7044: * aheinecke@7044: * @param plot The XYPlot. aheinecke@7044: * @param axis The axis the shoud be modified. aheinecke@7044: * @param bounds The whole range specified by a dataset. aheinecke@7044: * @param x A user defined range (null permitted). aheinecke@7044: * aheinecke@7044: * @return true, if a zoom range was specified, otherwise false. aheinecke@7044: */ aheinecke@7044: protected boolean zoom(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) { aheinecke@7044: aheinecke@7044: if (bounds == null) { aheinecke@7044: return false; aheinecke@7044: } aheinecke@7044: aheinecke@7044: if (x != null) { aheinecke@7044: Bounds computed = calculateZoom(bounds, x); aheinecke@7044: computed.applyBounds(axis, AXIS_SPACE); aheinecke@7044: aheinecke@7044: logger.debug("Zoom axis to: " + computed); aheinecke@7044: aheinecke@7044: return true; aheinecke@7044: } aheinecke@7044: aheinecke@7044: bounds.applyBounds(axis, AXIS_SPACE); aheinecke@7044: return false; aheinecke@7044: } aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Calculates the start and end km for zoomed charts. aheinecke@7044: * @param bounds The given total bounds (unzoomed). aheinecke@7044: * @param range The range specifying the zoom. aheinecke@7044: * aheinecke@7044: * @return The start and end km for the zoomed chart. aheinecke@7044: */ aheinecke@7044: protected Bounds calculateZoom(Bounds bounds, Range range) { aheinecke@7044: double min = bounds.getLower().doubleValue(); aheinecke@7044: double max = bounds.getUpper().doubleValue(); aheinecke@7044: aheinecke@7044: if (logger.isDebugEnabled()) { aheinecke@7044: logger.debug("Minimum is: " + min); aheinecke@7044: logger.debug("Maximum is: " + max); aheinecke@7044: logger.debug("Lower zoom is: " + range.getLowerBound()); aheinecke@7044: logger.debug("Upper zoom is: " + range.getUpperBound()); aheinecke@7044: } aheinecke@7044: aheinecke@7044: double diff = max > min ? max - min : min - max; aheinecke@7044: aheinecke@7044: DoubleBounds computed = new DoubleBounds( aheinecke@7044: min + range.getLowerBound() * diff, aheinecke@7044: min + range.getUpperBound() * diff); aheinecke@7044: return computed; aheinecke@7044: } aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Extract the minimum and maximum values for x and y axes aheinecke@7044: * which are stored in xRanges and yRanges. aheinecke@7044: * aheinecke@7044: * @param index The index of the y-Axis. aheinecke@7044: * aheinecke@7044: * @return a Range[] as follows: [x-Range, y-Range]. aheinecke@7044: */ aheinecke@7044: @Override aheinecke@7044: public Range[] getRangesForAxis(int index) { aheinecke@7044: logger.debug("getRangesForAxis " + index); aheinecke@7044: aheinecke@7044: Bounds rx = getXBounds(Integer.valueOf(0)); aheinecke@7044: Bounds ry = getYBounds(Integer.valueOf(index)); aheinecke@7044: aheinecke@7044: if (rx == null) { aheinecke@7044: logger.warn("Range for x axis not set." + aheinecke@7044: " Using default values: 0 - 1."); aheinecke@7044: rx = new DoubleBounds(0, 1); aheinecke@7044: } aheinecke@7044: if (ry == null) { aheinecke@7044: logger.warn("Range for y" + index + aheinecke@7044: " axis not set. Using default values: 0 - 1."); aheinecke@7044: ry = new DoubleBounds(0, 1); aheinecke@7044: } aheinecke@7044: aheinecke@7044: return new Range[] { aheinecke@7044: new Range(rx.getLower().doubleValue(), rx.getUpper().doubleValue()), aheinecke@7044: new Range(ry.getLower().doubleValue(), ry.getUpper().doubleValue()) aheinecke@7044: }; aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** Get X (usually horizontal) extent for given axis. */ aheinecke@7044: @Override aheinecke@7044: public Bounds getXBounds(int axis) { aheinecke@7044: return xBounds.get(axis); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** Set X (usually horizontal) extent for given axis. */ aheinecke@7044: @Override aheinecke@7044: protected void setXBounds(int axis, Bounds bounds) { aheinecke@7044: if (bounds.getLower() == bounds.getUpper()) { aheinecke@7044: xBounds.put(axis, ChartHelper.expandBounds(bounds, 5d)); aheinecke@7044: } aheinecke@7044: else { aheinecke@7044: xBounds.put(axis, bounds); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** Get Y (usually vertical) extent for given axis. */ aheinecke@7044: @Override aheinecke@7044: public Bounds getYBounds(int axis) { aheinecke@7044: return yBounds.get(axis); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** Set Y (usually vertical) extent for given axis. */ aheinecke@7044: @Override aheinecke@7044: protected void setYBounds(int axis, Bounds bounds) { aheinecke@7044: yBounds.put(axis, bounds); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Adjusts the axes of a plot. This method sets the labelFont of the aheinecke@7044: * X axis. aheinecke@7044: * aheinecke@7044: * (Duplicate in TimeseriesChartGenerator) aheinecke@7044: * aheinecke@7044: * @param plot The XYPlot of the chart. aheinecke@7044: */ aheinecke@7044: protected void adjustAxes(XYPlot plot) { aheinecke@7044: ValueAxis xaxis = plot.getDomainAxis(); aheinecke@7044: aheinecke@7044: ChartSettings chartSettings = getChartSettings(); aheinecke@7044: if (chartSettings == null) { aheinecke@7044: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: Font labelFont = new Font( aheinecke@7044: DEFAULT_FONT_NAME, aheinecke@7044: Font.BOLD, aheinecke@7044: getXAxisLabelFontSize()); aheinecke@7044: aheinecke@7044: xaxis.setLabelFont(labelFont); aheinecke@7044: xaxis.setTickLabelFont(labelFont); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * This method walks over all axes (domain and range) of plot and aheinecke@7044: * calls localizeDomainAxis() for domain axes or localizeRangeAxis() for aheinecke@7044: * range axes. aheinecke@7044: * aheinecke@7044: * @param plot The XYPlot. aheinecke@7044: */ aheinecke@7044: private void localizeAxes(XYPlot plot) { aheinecke@7044: for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) { aheinecke@7044: ValueAxis axis = plot.getDomainAxis(i); aheinecke@7044: aheinecke@7044: if (axis != null) { aheinecke@7044: localizeDomainAxis(axis); aheinecke@7044: } aheinecke@7044: else { aheinecke@7044: logger.warn("Domain axis at " + i + " is null."); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { aheinecke@7044: ValueAxis axis = plot.getRangeAxis(i); aheinecke@7044: aheinecke@7044: if (axis != null) { aheinecke@7044: localizeRangeAxis(axis); aheinecke@7044: } aheinecke@7044: else { aheinecke@7044: logger.warn("Range axis at " + i + " is null."); aheinecke@7044: } aheinecke@7044: } aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Overrides the NumberFormat with the NumberFormat for the current locale aheinecke@7044: * that is provided by getLocale(). aheinecke@7044: * aheinecke@7044: * @param domainAxis The domain axis that needs localization. aheinecke@7044: */ aheinecke@7044: protected void localizeDomainAxis(ValueAxis domainAxis) { aheinecke@7044: NumberFormat nf = NumberFormat.getInstance(getLocale()); aheinecke@7044: ((NumberAxis) domainAxis).setNumberFormatOverride(nf); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Overrides the NumberFormat with the NumberFormat for the current locale aheinecke@7044: * that is provided by getLocale(). aheinecke@7044: * aheinecke@7044: * @param rangeAxis The domain axis that needs localization. aheinecke@7044: */ aheinecke@7044: protected void localizeRangeAxis(ValueAxis rangeAxis) { aheinecke@7044: NumberFormat nf = NumberFormat.getInstance(getLocale()); aheinecke@7044: ((NumberAxis) rangeAxis).setNumberFormatOverride(nf); aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** aheinecke@7044: * Create a hash from a legenditem. aheinecke@7044: * This hash can then be used to merge legend items labels. aheinecke@7044: * @return hash for given legenditem to identify mergeables. aheinecke@7044: */ aheinecke@7044: public static String legendItemHash(LegendItem li) { aheinecke@7044: // TODO Do proper implementation. Ensure that only mergable sets are created. aheinecke@7044: // getFillPaint() aheinecke@7044: // getFillPaintTransformer() aheinecke@7044: // getLabel() aheinecke@7044: // getLine() aheinecke@7044: // getLinePaint() aheinecke@7044: // getLineStroke() aheinecke@7044: // getOutlinePaint() aheinecke@7044: // getOutlineStroke() aheinecke@7044: // Shape getShape() aheinecke@7044: // String getToolTipText() aheinecke@7044: // String getURLText() aheinecke@7044: // boolean isLineVisible() aheinecke@7044: // boolean isShapeFilled() aheinecke@7044: // boolean isShapeOutlineVisible() aheinecke@7044: // boolean isShapeVisible() aheinecke@7044: String hash = li.getLinePaint().toString(); aheinecke@7044: String label = li.getLabel(); aheinecke@7044: if (label.startsWith("W (") || label.startsWith("W(")) { aheinecke@7044: hash += "-W-"; aheinecke@7044: } aheinecke@7044: else if (label.startsWith("Q(") || label.startsWith("Q (")) { aheinecke@7044: hash += "-Q-"; aheinecke@7044: } aheinecke@7044: aheinecke@7044: // WQ.java holds example of using regex Matcher/Pattern. aheinecke@7044: aheinecke@7044: return hash; aheinecke@7044: } aheinecke@7044: aheinecke@7044: /** True if x axis has been inverted. */ aheinecke@7044: public boolean isInverted() { aheinecke@7044: return inverted; aheinecke@7044: } aheinecke@7044: aheinecke@7044: aheinecke@7044: /** Set to true if x axis has been inverted. */ aheinecke@7044: public void setInverted(boolean inverted) { aheinecke@7044: this.inverted = inverted; aheinecke@7044: } aheinecke@7044: aheinecke@7068: @Override aheinecke@7068: public String getDefaultChartTitle() { aheinecke@7068: DiagramAttributes.Title dTitle = diagramAttributes.getTitle(); aheinecke@7068: if (dTitle == null) { aheinecke@7068: return "Title not configured in conf.xml"; aheinecke@7068: } aheinecke@7068: aheinecke@7068: return dTitle.evaluate((D4EArtifact)getMaster(), context); aheinecke@7068: } aheinecke@7068: aheinecke@7068: @Override aheinecke@7068: public String getDefaultChartSubtitle() { aheinecke@7068: DiagramAttributes.Title dTitle = diagramAttributes.getSubtitle(); aheinecke@7068: if (dTitle == null) { aheinecke@7068: return "Subtitle not configured in conf.xml"; aheinecke@7068: } aheinecke@7068: aheinecke@7068: return dTitle.evaluate((D4EArtifact)getMaster(), context); aheinecke@7068: } aheinecke@7068: aheinecke@7068: /** aheinecke@7068: * Get internationalized label for the x axis. aheinecke@7068: */ aheinecke@7068: @Override aheinecke@7068: protected String getDefaultXAxisLabel() { aheinecke@7084: DiagramAttributes.Title dTitle = diagramAttributes.getDomainAxisTitle(); aheinecke@7084: if (dTitle == null) { aheinecke@7084: return "Domain Axis Title not configured in conf.xml"; aheinecke@7084: } aheinecke@7068: aheinecke@7084: return dTitle.evaluate((D4EArtifact)getMaster(), context); aheinecke@7068: } aheinecke@7068: aheinecke@7068: @Override aheinecke@7084: protected String getDefaultYAxisLabel(String axisName) { aheinecke@7084: Processor pr = diagramAttributes.getProcessorForAxisName(axisName); aheinecke@7084: return pr == null ? "" : pr.getAxisLabel(this); aheinecke@7068: } aheinecke@7068: aheinecke@7068: aheinecke@7068: /** aheinecke@7068: * Creates a list of Section for the chart's Y axes. aheinecke@7068: * aheinecke@7068: * @return a list of Y axis sections. aheinecke@7068: */ aheinecke@7068: protected List buildYAxisSections() { aheinecke@7068: List axisSections = new ArrayList(); aheinecke@7068: aheinecke@7068: List axesAttrs = diagramAttributes.getAxesAttributes(); aheinecke@7068: aheinecke@7068: for (int i = 0, n = axesAttrs.size(); i < n; i++) { aheinecke@7068: AxisSection ySection = new AxisSection(); aheinecke@7084: String axisName = diagramAttributes.getAxisName(i); aheinecke@7084: ySection.setIdentifier(axisName); aheinecke@7084: ySection.setLabel(getYAxisLabel(axisName)); aheinecke@7068: ySection.setFontSize(14); aheinecke@7068: ySection.setFixed(false); aheinecke@7068: aheinecke@7068: // XXX We are able to find better default ranges that [0,0], the aheinecke@7068: // only problem is, that we do NOT have a better range than [0,0] aheinecke@7068: // for each axis, because the initial chart will not have a dataset aheinecke@7068: // for each axis set! aheinecke@7068: ySection.setUpperRange(0d); aheinecke@7068: ySection.setLowerRange(0d); aheinecke@7068: aheinecke@7068: axisSections.add(ySection); aheinecke@7068: } aheinecke@7068: aheinecke@7068: return axisSections; aheinecke@7068: } aheinecke@7068: aheinecke@7084: /** aheinecke@7084: * Returns the Y-Axis label of a chart at position pos. aheinecke@7084: * aheinecke@7084: * @return the Y-Axis label of a chart at position 0. aheinecke@7084: */ aheinecke@7084: protected String getYAxisLabel(String axisName) { aheinecke@7084: ChartSettings chartSettings = getChartSettings(); aheinecke@7084: if (chartSettings == null) { aheinecke@7084: return getDefaultYAxisLabel(axisName); aheinecke@7084: } aheinecke@7084: AxisSection as = chartSettings.getAxisSection(axisName); aheinecke@7084: if (as != null) { aheinecke@7084: String label = as.getLabel(); aheinecke@7084: if (label != null) { aheinecke@7084: return label; aheinecke@7084: } aheinecke@7084: } aheinecke@7084: aheinecke@7084: return getDefaultYAxisLabel(axisName); aheinecke@7084: } aheinecke@7084: aheinecke@7068: protected String axisIndexToName(int index) { aheinecke@7068: return diagramAttributes.getAxisName(index); aheinecke@7068: } aheinecke@7068: aheinecke@7044: /** Add the acutal data to the diagram according to the processors. aheinecke@7044: * For every outable facets, this function is aheinecke@7044: * called and handles the data accordingly. */ aheinecke@7044: @Override teichmann@7052: public void doOut( teichmann@7052: ArtifactAndFacet bundle, teichmann@7052: ThemeDocument theme, teichmann@7052: boolean visible teichmann@7052: ) { aheinecke@7044: String facetName = bundle.getFacetName(); aheinecke@7044: Facet facet = bundle.getFacet(); aheinecke@7044: aheinecke@7044: /* A conservative security check */ aheinecke@7044: if (facetName == null || facet == null) { aheinecke@7044: /* Can't happen,.. */ aheinecke@7044: logger.error("doOut called with null facet."); teichmann@7052: return; aheinecke@7044: } aheinecke@7044: aheinecke@7044: logger.debug("DoOut for facet: " + facetName); aheinecke@7044: aheinecke@7096: boolean found = false; aheinecke@7096: List prL = diagramAttributes.getProcessors(); aheinecke@7096: for (Processor pr: prL) { aheinecke@7044: if (pr.canHandle(facetName)) { aheinecke@7096: found = true; aheinecke@7068: pr.doOut(this, bundle, theme, visible); aheinecke@7044: } aheinecke@7044: } aheinecke@7096: if (!found) { aheinecke@7096: logger.warn("No processor found for: " + facetName); aheinecke@7096: if (logger.isDebugEnabled()) { aheinecke@7096: logger.debug("Configured processors for this diagram are:"); aheinecke@7096: for (Processor pr: prL) { aheinecke@7096: logger.debug(pr.getClass().getName()); aheinecke@7096: } aheinecke@7096: } aheinecke@7096: } aheinecke@7044: } aheinecke@7044: }