org.geomajas.plugin.print.component.impl.LegendViaUrlComponentImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.geomajas.plugin.print.component.impl.LegendViaUrlComponentImpl.java

Source

/*
 * This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
 *
 * Copyright 2008-2014 Geosparc nv, http://www.geosparc.com/, Belgium.
 *
 * The program is available in open source according to the GNU Affero
 * General Public License. All contributions in this program are covered
 * by the Geomajas Contributors License Agreement. For full licensing
 * details, see LICENSE.txt in the project root.
 */

package org.geomajas.plugin.print.component.impl;

import java.awt.Font;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.geomajas.plugin.print.component.LegendComponent;
import org.geomajas.plugin.print.component.LegendViaUrlComponent;
import org.geomajas.plugin.print.component.PageComponent;
import org.geomajas.plugin.print.component.PdfContext;
import org.geomajas.plugin.print.component.PrintComponent;
import org.geomajas.plugin.print.component.PrintComponentVisitor;
import org.geomajas.plugin.print.component.dto.LegendComponentInfo;
import org.geomajas.plugin.print.component.dto.LegendViaUrlComponentInfo;
import org.geomajas.plugin.print.parser.FontConverter;
import org.geomajas.service.DispatcherUrlService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.lowagie.text.BadElementException;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.Utilities;
import com.lowagie.text.pdf.codec.PngImage;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import com.thoughtworks.xstream.annotations.XStreamOmitField;

/**
 * Rendering of the legend for a certain layer in a printed document via an image specified via a URL.
 * 
 * @author An Buyle
 */
@Component()
@Scope(value = "prototype")
public class LegendViaUrlComponentImpl extends AbstractPrintComponent<LegendViaUrlComponentInfo>
        implements LegendViaUrlComponent {

    private static final String BUNDLE_NAME = "org/geomajas/plugin/print/PrintMessages"; //$NON-NLS-1$
    @XStreamOmitField
    private final Logger log = LoggerFactory.getLogger(LegendViaUrlComponentImpl.class);

    // do not make this static, different requests might need different bundles
    @XStreamOmitField
    private ResourceBundle resourceBundle;

    private static final int DPI_FOR_IMAGE = 288;

    private static final String NOT_VISIBLE_MSG = "INVISIBLE_FOR_SCALE";

    private static final float MARGIN_LEFT_IMAGE_RELATIVE_TO_FONTSIZE = 0.25f;

    private static final float MARGIN_TOP_IMAGE_RELATIVE_TO_FONTSIZE = 0.25f;

    /**
     * The font for error text
     */
    @XStreamConverter(FontConverter.class)
    private Font font = new Font(LegendComponentInfo.DEFAULT_LEGEND_FONT_FAMILY, Font.PLAIN, 9); // Default font

    /**
     * Color of the text.
     */
    private String fontColor = "0x000000";

    @Autowired
    private DispatcherUrlService dispatcherUrlService;

    private String layerId;

    private URL legendImageServiceUrl;

    private boolean visible = true;

    private Image image;

    private String legendImageServiceUrlAsString;

    private String warning;

    /** Constructor. */

    public LegendViaUrlComponentImpl() {
    }

    /**
     * Call back visitor.
     * 
     * @param visitor
     *         visitor
     */
    public void accept(PrintComponentVisitor visitor) {
    }

    @Override
    public void calculateSize(PdfContext context) {

        if (null == getLegendImageServiceUrl()) {
            log.error("getLegendImageServiceUrl() returns unexpectedly with NULL");
            setBounds(new Rectangle(0, 0));
            return; // Abort
        }

        String locale = getLocale();
        try {
            if (null != locale && !locale.isEmpty()) {
                resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME, new Locale(locale));
            } else {
                resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
            }
        } catch (MissingResourceException e) {
            resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME, new Locale("en'"));
        }

        if (getConstraint().getMarginX() <= 0.0f) {
            getConstraint().setMarginX(MARGIN_LEFT_IMAGE_RELATIVE_TO_FONTSIZE * getLegend().getFont().getSize());
        }
        if (getConstraint().getMarginY() <= 0.0f) {
            getConstraint().setMarginY(MARGIN_TOP_IMAGE_RELATIVE_TO_FONTSIZE * getLegend().getFont().getSize());
        }

        @SuppressWarnings("deprecation")
        float width = getConstraint().getWidth();
        @SuppressWarnings("deprecation")
        float height = getConstraint().getHeight();

        // Retrieve legend image from URL if not yet retrieved
        if (null == image && visible && null == warning) {
            if (getLegendImageServiceUrl().contains("=image/png")) {
                // Its approx. 2 times faster to use PngImage.getImage() instead of Image.getInstance()
                // since the latter will retrieve the URL twice!
                try {
                    image = PngImage.getImage(new URL(getLegendImageServiceUrl()));
                    // Image.getInstance(new URL(getLegendImageServiceUrl()));
                    image.setDpi(DPI_FOR_IMAGE, DPI_FOR_IMAGE); // Increase the precision
                } catch (MalformedURLException e) {
                    log.error("Error in Image.getInstance() for URL " + getLegendImageServiceUrl(), e);
                    e.printStackTrace();
                } catch (IOException e) {
                    // This exception is OK if no legend image is generated because out of scale range
                    // for a dynamic layer, then a text message which indicates an invisible legend is referred
                    // to by the URL 
                    visible = !hasInVisibleResponse();
                    if (visible) {
                        log.warn("Unexpected IOException for Image.getInstance() for URL "
                                + getLegendImageServiceUrl(), e);
                    }
                }
            } else {
                try {
                    image = Image.getInstance(new URL(getLegendImageServiceUrl()));
                    image.setDpi(DPI_FOR_IMAGE, DPI_FOR_IMAGE); // Increase the precision
                } catch (BadElementException e) {
                    log.error("Error in Image.getInstance() for URL " + getLegendImageServiceUrl(), e);
                    e.printStackTrace();
                } catch (MalformedURLException e) {
                    log.error("Error in Image.getInstance() for URL " + getLegendImageServiceUrl(), e);
                    e.printStackTrace();
                } catch (IOException e) {
                    // This exception is OK if no legend image is generated because out of scale range
                    // for a dynamic layer, then a text message which indicates an invisible legend is referred
                    // to by the URL 
                    visible = !hasInVisibleResponse();
                    if (visible) {
                        log.warn("Unexpected IOException for Image.getInstance() for URL "
                                + getLegendImageServiceUrl(), e);
                    }
                }

            }
        }
        if (!visible) {
            setBounds(new Rectangle(0, 0));
        } else if (null == image) {
            generateWarningMessage(context);
        } else {

            if (width <= 0 && height <= 0) {
                // when / 2.0f: The image is generated with a scale of 1:0.5 (but looks awful!)
                width = image.getWidth(); // 2.0f;
                height = image.getHeight(); // 2.0f;
            } else if (width <= 0) {
                width = image.getWidth() / image.getHeight() * height;
            } else if (height <= 0) {
                height = image.getHeight() / image.getWidth() * width;
            }

            setBounds(new Rectangle(width, height)); // excluding the marginX
        }
    }

    protected LegendComponent getLegend() {
        @SuppressWarnings("deprecation")
        PrintComponent<?> ancestor = getParent();

        while (null != ancestor && !(ancestor instanceof LegendComponent)) {
            ancestor = ancestor.getParent();
        }
        if (null != ancestor && ancestor instanceof LegendComponent) {
            return (LegendComponent) ancestor;
        } else {
            return null;
        }

    }

    protected String getLocale() {
        PrintComponent<?> ancestor = getParent();

        while (null != ancestor && !(ancestor instanceof PageComponent)) {
            ancestor = ancestor.getParent();
        }
        if (null != ancestor && ancestor instanceof PageComponent) {
            return ((PageComponent) ancestor).getLocale();
        } else {
            return null;
        }

    }

    @Override
    public boolean isVisible() {
        return this.visible;
    }

    @SuppressWarnings("deprecation")
    @Override
    public void render(PdfContext context) {
        if (null == image && visible && null == warning) {
            calculateSize(context);
        }
        if (visible) {
            if (null != warning) {
                context.drawText(warning, getFont(), getSize(), context.getColor(getFontColor(), 1f));
            } else {
                context.drawImage(image, getSize(), null);
            }
        }
    }

    public String getClientLayerId() {
        return layerId; // purely informative
    }

    public void setClientLayerId(String layerId) {
        this.layerId = layerId; // purely informative
    }

    @Override
    public String getLegendImageServiceUrl() {
        calculateLegendImageServiceUrl();
        return this.legendImageServiceUrl.toExternalForm();
    }

    @Override
    public void setLegendImageServiceUrl(String legendImageServiceUrlAsString) {
        this.image = null; // remove the cached image
        this.legendImageServiceUrl = null; // Clear the cached value

        URL absoluteUrl = null;

        if (!legendImageServiceUrlAsString.startsWith("http:")
                && !legendImageServiceUrlAsString.startsWith("https:")) {

            try {
                String baseUrlAsString = dispatcherUrlService.getLocalDispatcherUrl();
                log.debug("BaseURL: {}", baseUrlAsString);
                URL baseUrl = new URL(baseUrlAsString);
                absoluteUrl = new URL(baseUrl, "../" + legendImageServiceUrlAsString);
                log.debug("AbsoluteUrl: {}", absoluteUrl);
            } catch (MalformedURLException e) {
                // Should never happen...
                log.error("Error converting URL " + legendImageServiceUrlAsString + " to absolute URL", e);
                e.printStackTrace();
            }
        } else {
            try {
                absoluteUrl = new URL(legendImageServiceUrlAsString);
            } catch (MalformedURLException e) {
                // Should never happen...
                log.error("Error converting URL " + legendImageServiceUrlAsString + " to absolute URL", e);
            }
        }
        this.legendImageServiceUrlAsString = absoluteUrl.toExternalForm();
    }

    public Font getFont() {
        return font;
    }

    public void setFont(Font font) {
        this.font = font;
    }

    public String getFontColor() {
        return fontColor;
    }

    public void setFontColor(String fontColor) {
        this.fontColor = fontColor;
    }

    @SuppressWarnings("deprecation")
    @Override
    public void fromDto(LegendViaUrlComponentInfo info) {
        super.fromDto(info);
        visible = true;
        warning = null;

        setLegendImageServiceUrl(info.getLegendImageServiceUrl());
        setClientLayerId(info.getClientLayerId());
    }

    private boolean hasInVisibleResponse() {
        boolean inVisibleResponse = false;

        URL url = null;
        try {
            url = Utilities.toURL(getLegendImageServiceUrl());
        } catch (MalformedURLException e1) {
            // Should never happen!
            log.error("Exception in  Utilities.toURL() for URL " + getLegendImageServiceUrl(), e1);
            e1.printStackTrace();
        }
        if (null == url) {
            log.error("Error in  Utilities.toURL() for URL " + getLegendImageServiceUrl());
            return false; // Unexpected error, ABORT
        }

        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();
        } catch (IOException e2) {
            log.error("Exception in url.openConnection() for URL " + getLegendImageServiceUrl(), e2);
            e2.printStackTrace();
        }
        if (null == connection) {
            log.error("Error in  url.openConnection() for URL " + getLegendImageServiceUrl());
            return false; // Unexpected error, ABORT
        }

        try {
            connection.connect();
        } catch (IOException e1) {
            log.error("Exception in connection.connect for URL " + getLegendImageServiceUrl(), e1);
            e1.printStackTrace();
            return false; // ABORT
        }

        String contentType = connection.getContentType();
        if (contentType.startsWith("text/")) {
            // Something went wrong (normally the URL returns an image
            // Read the response text
            InputStream is = null;
            try {
                is = url.openStream();
                byte[] messageByteArray = new byte[19];
                is.read(messageByteArray, 0, 19);

                is.close();
                is = null;

                String message = new String(messageByteArray);
                if (NOT_VISIBLE_MSG.equals(message)) {
                    inVisibleResponse = true;
                }

            } catch (IOException e1) {
                e1.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }
        return inVisibleResponse;
    }

    private void calculateLegendImageServiceUrl() {
        if (null == this.legendImageServiceUrl) {
            try {

                this.legendImageServiceUrl = new URL(legendImageServiceUrlAsString);
            } catch (MalformedURLException e) {
                // Should never happen
                e.printStackTrace();
                this.legendImageServiceUrl = null;
            }
        }
    }

    @SuppressWarnings("deprecation")
    private void generateWarningMessage(PdfContext context) {
        warning = resourceBundle.getString("ErrorRetrievingLegend");

        Rectangle textSize = context.getTextSize(warning, getFont());
        float margin = 0.5f * getFont().getSize();

        setBounds(new Rectangle(textSize.getWidth() + 2.0f * margin, textSize.getHeight() + 2 * margin));
    }

}