de.willuhn.jameica.hbci.server.KontoauszugPdfUtil.java Source code

Java tutorial

Introduction

Here is the source code for de.willuhn.jameica.hbci.server.KontoauszugPdfUtil.java

Source

/**********************************************************************
 *
 * Copyright (c) 2004 Olaf Willuhn
 * All rights reserved.
 * 
 * This software is copyrighted work licensed under the terms of the
 * Jameica License.  Please consult the file "LICENSE" for details. 
 *
 **********************************************************************/

package de.willuhn.jameica.hbci.server;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.kapott.hbci.GV_Result.GVRKontoauszug.Format;

import de.willuhn.datasource.GenericIterator;
import de.willuhn.datasource.rmi.DBIterator;
import de.willuhn.io.FileCopy;
import de.willuhn.io.IOUtil;
import de.willuhn.jameica.hbci.HBCI;
import de.willuhn.jameica.hbci.MetaKey;
import de.willuhn.jameica.hbci.Settings;
import de.willuhn.jameica.hbci.messaging.MessagingAvailableConsumer;
import de.willuhn.jameica.hbci.messaging.ObjectChangedMessage;
import de.willuhn.jameica.hbci.rmi.HBCIDBService;
import de.willuhn.jameica.hbci.rmi.Konto;
import de.willuhn.jameica.hbci.rmi.Kontoauszug;
import de.willuhn.jameica.hbci.rmi.Protokoll;
import de.willuhn.jameica.hbci.server.BPDUtil.Support;
import de.willuhn.jameica.messaging.QueryMessage;
import de.willuhn.jameica.messaging.StatusBarMessage;
import de.willuhn.jameica.services.VelocityService;
import de.willuhn.jameica.system.Application;
import de.willuhn.jameica.util.DateUtil;
import de.willuhn.logging.Logger;
import de.willuhn.util.ApplicationException;
import de.willuhn.util.I18N;
import de.willuhn.util.TypedProperties;

/**
 * Hilfsklasse mit verschiedenen Util-Funktionen fuer die Kontoauszuege.
 */
public class KontoauszugPdfUtil {
    private final static I18N i18n = Application.getPluginLoader().getPlugin(HBCI.class).getResources().getI18N();

    private final static String CHANNEL = "hibiscus.kontoauszuege";

    /**
     * Liefert das File-Objekt fuer diesen Kontoauszug.
     * Wenn er direkt im Filesystem gespeichert ist, wird dieses geliefert.
     * Wurde er jedoch per Messaging gespeichert, dann ruft die Funktion ihn
     * vom Archiv ab und erzeugt eine Temp-Datei mit dem Kontoauszug.
     * @param ka der Kontoauszug.
     * @return die Datei.
     * @throws ApplicationException
     */
    public static File getFile(Kontoauszug ka) throws ApplicationException {
        if (ka == null)
            throw new ApplicationException(i18n.tr("Bitte whlen Sie den zu ffnenden Kontoauszug"));

        try {
            // Wenn ein Pfad und Dateiname angegeben ist, dann sollte die Datei
            // dort auch liegen
            final String path = StringUtils.trimToNull(ka.getPfad());
            final String name = StringUtils.trimToNull(ka.getDateiname());

            if (path != null && name != null) {
                File file = new File(path, name);

                Logger.info("trying to open pdf file from: " + file);
                if (!file.exists()) {
                    Logger.error("file does not exist (anymore): " + file);
                    throw new ApplicationException(i18n
                            .tr("Datei \"{0}\" existiert nicht mehr. Wurde sie gelscht?", file.getAbsolutePath()));
                }

                if (!file.canRead()) {
                    Logger.error("cannot read file: " + file);
                    throw new ApplicationException(i18n.tr("Datei \"{0}\" nicht lesbar", file.getAbsolutePath()));
                }

                return file;
            }

            final String uuid = StringUtils.trimToNull(ka.getUUID());

            Logger.info("trying to open pdf file using messaging, uuid: " + uuid);

            // Das kann eigentlich nicht sein. Dann wuerde ja alles fehlen
            if (uuid == null)
                throw new ApplicationException(i18n.tr("Ablageort des Kontoauszuges unbekannt"));

            QueryMessage qm = new QueryMessage(uuid, null);
            Application.getMessagingFactory().getMessagingQueue("jameica.messaging.get").sendSyncMessage(qm);
            byte[] data = (byte[]) qm.getData();
            if (data == null) {
                Logger.error("got no data from messaging for uuid: " + uuid);
                throw new ApplicationException(
                        i18n.tr("Datei existiert nicht mehr im Archiv. Wurde sie gelscht?"));
            }
            Logger.info("got " + data.length + " bytes from messaging for uuid: " + uuid);

            File file = File.createTempFile("kontoauszug-" + RandomStringUtils.randomAlphanumeric(5), ".pdf");
            file.deleteOnExit();

            OutputStream os = null;

            try {
                os = new BufferedOutputStream(new FileOutputStream(file));
                IOUtil.copy(new ByteArrayInputStream(data), os);
            } finally {
                IOUtil.close(os);
            }

            Logger.info("copied messaging data into temp file: " + file);
            return file;
        } catch (ApplicationException ae) {
            throw ae;
        } catch (Exception e) {
            Logger.error("unable to open file", e);
            throw new ApplicationException(i18n.tr("Fehler beim ffnen des Kontoauszuges: {0}", e.getMessage()));
        }
    }

    /**
     * Speichert den Kontoauszug in einer Datei.
     * @param ka der Kontoauszug.
     * @param target die Datei, in der der Kontoauszug gespeichert werden soll.
     * @throws ApplicationException
     */
    public static void store(Kontoauszug ka, File target) throws ApplicationException {
        if (ka == null)
            throw new ApplicationException(i18n.tr("Bitte whlen Sie den zu speichernden Kontoauszug"));

        if (target == null)
            throw new ApplicationException(i18n.tr("Bitte whlen Sie die Zieldatei aus"));

        try {
            // Wenn ein Pfad und Dateiname angegeben ist, dann sollte die Datei
            // dort auch liegen
            final String path = StringUtils.trimToNull(ka.getPfad());
            final String name = StringUtils.trimToNull(ka.getDateiname());

            if (path != null && name != null) {
                File file = new File(path, name);

                Logger.info("trying to open pdf file from: " + file);
                if (!file.exists()) {
                    Logger.error("file does not exist (anymore): " + file);
                    throw new ApplicationException(i18n
                            .tr("Datei \"{0}\" existiert nicht mehr. Wurde sie gelscht?", file.getAbsolutePath()));
                }

                if (!file.canRead()) {
                    Logger.error("cannot read file: " + file);
                    throw new ApplicationException(i18n.tr("Datei \"{0}\" nicht lesbar", file.getAbsolutePath()));
                }

                FileCopy.copy(file, target, true);
                Logger.info("copied " + file + " to " + target);
                return;
            }

            final String uuid = StringUtils.trimToNull(ka.getUUID());

            Logger.info("trying to open pdf file using messaging, uuid: " + uuid);

            // Das kann eigentlich nicht sein. Dann wuerde ja alles fehlen
            if (uuid == null)
                throw new ApplicationException(i18n.tr("Ablageort des Kontoauszuges unbekannt"));

            QueryMessage qm = new QueryMessage(uuid, null);
            Application.getMessagingFactory().getMessagingQueue("jameica.messaging.get").sendSyncMessage(qm);
            byte[] data = (byte[]) qm.getData();
            if (data == null) {
                Logger.error("got no data from messaging for uuid: " + uuid);
                throw new ApplicationException(
                        i18n.tr("Datei existiert nicht mehr im Archiv. Wurde sie gelscht?"));
            }
            Logger.info("got " + data.length + " bytes from messaging for uuid: " + uuid);

            OutputStream os = null;

            try {
                os = new BufferedOutputStream(new FileOutputStream(target));
                IOUtil.copy(new ByteArrayInputStream(data), os);
            } finally {
                IOUtil.close(os);
            }

            Logger.info("copied messaging data into file: " + target);
        } catch (ApplicationException ae) {
            throw ae;
        } catch (Exception e) {
            Logger.error("unable to open file", e);
            throw new ApplicationException(i18n.tr("Fehler beim ffnen des Kontoauszuges: {0}", e.getMessage()));
        }
    }

    /**
     * Speichert den Kontoauszug im Dateisystem bzw. Messaging.
     * @param k der Kontoauszug. Er muss eine ID besitzen - also bereits gespeichert worden sein.
     * @param data die rohen Binaer-Daten.
     * @throws RemoteException
     * @throws ApplicationException
     */
    public static void receive(Kontoauszug k, byte[] data) throws RemoteException, ApplicationException {
        if (k == null)
            throw new ApplicationException(i18n.tr("Kein Kontoauszug angegeben"));

        if (data == null || data.length == 0)
            throw new ApplicationException(i18n.tr("Kein Daten angegeben"));

        final Konto konto = k.getKonto();
        if (konto == null)
            throw new ApplicationException(i18n.tr("Kein Konto angegeben"));

        // Per Messaging speichern?
        if (MessagingAvailableConsumer.haveMessaging()
                && Boolean.parseBoolean(MetaKey.KONTOAUSZUG_STORE_MESSAGING.get(konto))) {
            QueryMessage qm = new QueryMessage(CHANNEL, data);
            Application.getMessagingFactory().getMessagingQueue("jameica.messaging.put").sendSyncMessage(qm);
            k.setUUID(qm.getData().toString());
            k.store();
            Logger.info("stored account statement data in messaging archive [id: " + k.getID() + ", uuid: "
                    + k.getUUID() + "]");
            return;
        }

        // Im Dateisystem speichern
        String path = createPath(konto, k);
        try {
            File file = new File(path).getCanonicalFile();
            Logger.info("storing account statement data in file [id: " + k.getID() + ", file: " + file + "]");

            File dir = file.getParentFile();
            if (!dir.exists()) {
                Logger.info("auto-creating parent dir: " + dir);
                if (!dir.mkdirs())
                    throw new ApplicationException(
                            i18n.tr("Erstellen des Ordners fehlgeschlagen. Ordner-Berechtigungen korrekt?"));
            }

            if (!dir.canWrite())
                throw new ApplicationException(i18n.tr("Kein Schreibzugriff in {0}", dir.toString()));

            OutputStream os = null;

            try {
                File target = file;

                int i = 0;
                while (i < 10000) {
                    // Checken, ob die Datei schon existiert. Wenn ja, haengen wir einen Zaehler hinten drin.
                    // Um sicherzugehen, dass wir die Datei nicht ueberschreiben.
                    if (!target.exists())
                        break;

                    // OK, die Datei gibts schon. Wir haengen den Counter hinten an
                    i++;
                    target = indexedFile(file, i);
                }
                os = new BufferedOutputStream(new FileOutputStream(target));
                os.write(data);
                k.setPfad(target.getParent());
                k.setDateiname(target.getName());
                k.store();
            } finally {
                IOUtil.close(os);
            }
        } catch (IOException e) {
            Logger.error("unable to store account statement data in file: " + path, e);
            throw new ApplicationException(
                    i18n.tr("Speichern des Kontoauszuges fehlgeschlagen: {0}", e.getMessage()));
        }
    }

    /**
     * Haengt die Nummer an den Dateinamen an.
     * @param f die Datei.
     * @param i die Nummer.
     * @return die neue Datei.
     */
    private static File indexedFile(File f, int i) {
        String name = f.getName();
        int dot = name.lastIndexOf('.');

        name = name.substring(0, dot) + "-" + String.format("%05d", i) + name.substring(dot);
        return new File(f.getParentFile(), name);
    }

    /**
     * Erzeugt den Pfad fuer den zu speichernden Kontoauszug.
     * @param k das Konto.
     * @param ka der Kontoauszug. Optional. Wenn er fehlt, werden Default-Werte verwendet.
     * @return der Pfad.
     * @throws RemoteException
     * @throws ApplicationException
     */
    public static String createPath(Konto k, Kontoauszug ka) throws RemoteException, ApplicationException {
        if (k == null)
            throw new ApplicationException(i18n.tr("Kein Konto angegeben"));

        final String path = MetaKey.KONTOAUSZUG_STORE_PATH.get(k);
        final String folder = MetaKey.KONTOAUSZUG_TEMPLATE_PATH.get(k);
        final String name = MetaKey.KONTOAUSZUG_TEMPLATE_NAME.get(k);
        return createPath(k, ka, path, folder, name);
    }

    /**
     * Erzeugt den Pfad fuer den zu speichernden Kontoauszug.
     * @param k das Konto.
     * @param ka der Kontoauszug. Optional. Wenn er fehlt, werden Default-Werte verwendet.
     * @param path Ordner, in dem die Kontoauszuege gespeichert werden.
     * @param folder Template fuer den Unterordner.
     * @param name Template fuer den Dateinamen.
     * @return der Pfad.
     * @throws RemoteException
     * @throws ApplicationException
     */
    public static String createPath(Konto k, Kontoauszug ka, String path, String folder, String name)
            throws RemoteException, ApplicationException {
        if (k == null)
            throw new ApplicationException(i18n.tr("Kein Konto angegeben"));

        Map<String, Object> ctx = new HashMap<String, Object>();

        {
            String iban = StringUtils.trimToNull(k.getIban());
            if (iban == null)
                iban = StringUtils.trimToEmpty(k.getKontonummer());

            ctx.put("iban", iban.replaceAll(" ", ""));
        }

        {
            String bic = StringUtils.trimToNull(k.getBic());
            if (bic == null)
                bic = StringUtils.trimToEmpty(k.getBLZ());

            ctx.put("bic", bic.replaceAll(" ", ""));
        }

        {
            Calendar cal = Calendar.getInstance();
            if (ka != null) {
                if (ka.getErstellungsdatum() != null)
                    cal.setTime(ka.getErstellungsdatum());
                else if (ka.getAusfuehrungsdatum() != null)
                    cal.setTime(ka.getAusfuehrungsdatum());
            }

            Integer i = ka != null && ka.getJahr() != null ? ka.getJahr() : null;
            ctx.put("jahr", i != null ? i.toString() : Integer.toString(cal.get(Calendar.YEAR)));
            ctx.put("monat", String.format("%02d", cal.get(Calendar.MONTH) + 1));
            ctx.put("tag", String.format("%02d", cal.get(Calendar.DATE)));
            ctx.put("stunde", String.format("%02d", cal.get(Calendar.HOUR_OF_DAY)));
            ctx.put("minute", String.format("%02d", cal.get(Calendar.MINUTE)));
        }

        {
            Integer i = ka != null && ka.getNummer() != null ? ka.getNummer() : null;
            ctx.put("nummer", String.format("%03d", i != null ? i.intValue() : 1));
        }

        VelocityService velocity = Application.getBootLoader().getBootable(VelocityService.class);
        StringBuilder sb = new StringBuilder();

        /////////////////////////////
        // Pfad
        {
            if (path == null || path.length() == 0)
                path = Application.getPluginLoader().getPlugin(HBCI.class).getResources().getWorkPath();
            sb.append(path);

            if (!path.endsWith(File.separator))
                sb.append(File.separator);
        }
        //
        /////////////////////////////

        /////////////////////////////
        // Unter-Ordner
        {
            if (folder != null && folder.length() > 0) {
                try {
                    // Velocity-Escaping machen wir. Das sollte der User nicht selbst machen muessen
                    // Eigentlich wird hier nur "\$" gegen "\\$" ersetzt. Die zusaetzlichen
                    // Die extra Escapings sind fuer Java selbst in String-Literalen.
                    folder = folder.replace("\\$", "\\\\$");
                    folder = velocity.merge(folder, ctx);
                } catch (Exception e) {
                    Logger.error("folder template invalid: \"" + folder + "\"", e);
                }
                sb.append(folder);
                if (!folder.endsWith(File.separator))
                    sb.append(File.separator);
            }
        }
        //
        /////////////////////////////

        /////////////////////////////
        // Dateiname
        {
            if (name == null || name.length() == 0 && ka != null)
                name = ka.getDateiname();

            if (name == null || name.length() == 0)
                name = MetaKey.KONTOAUSZUG_TEMPLATE_NAME.getDefault();

            try {
                name = velocity.merge(name, ctx);
            } catch (Exception e) {
                Logger.error("name template invalid: \"" + name + "\"", e);
            }
            sb.append(name);

            // Dateiendung noch anhaengen.
            Format f = Format.find(ka != null ? ka.getFormat() : null);
            if (f == null)
                f = Format.PDF;

            sb.append(".");
            sb.append(f.getExtention());
        }

        return sb.toString();
    }

    /**
     * Liefert die Liste der noch ungelesenen Kontoauszuege.
     * @return die Liste der noch ungelesenen Kontoauszuege, chronologisch nach Erstellungsdatum sortiert.
     * Neueste zuerst.
     * @throws RemoteException
     */
    public static GenericIterator<Kontoauszug> getUnread() throws RemoteException {
        HBCIDBService service = (HBCIDBService) Settings.getDBService();
        DBIterator it = service.createList(Kontoauszug.class);
        it.addFilter("gelesen_am is null");
        it.setOrder("order by " + service.getSQLTimestamp("erstellungsdatum") + " desc");
        return it;
    }

    /**
     * Liefert eine gefilterte Liste von Kontoauszuegen.
     * @param konto das optionale Konto. Kann auch der Name einer Kontogruppe sein.
     * @param from das optionale Start-Datum.
     * @param to das optionale End-Datum.
     * @param unread true, wenn nur ungelesene Kontoauszuege geliefert werden sollen.
     * @return die Liste der passenden Kontoauszuege.
     * @throws RemoteException
     */
    public static GenericIterator<Kontoauszug> getList(Object konto, Date from, Date to, boolean unread)
            throws RemoteException {
        HBCIDBService service = (HBCIDBService) Settings.getDBService();
        DBIterator it = service.createList(Kontoauszug.class);

        // Bei HKEKP in Segment-Version 1 wird gar kein Zeitraum mitgeliefert.
        // Daher nehmen wir dort das Abrufdatum
        if (from != null) {
            java.sql.Date d = new java.sql.Date(DateUtil.startOfDay(from).getTime());
            it.addFilter(
                    "(von >= ? OR erstellungsdatum >= ? OR (von IS NULL AND erstellungsdatum IS NULL AND ausgefuehrt_am >= ?))",
                    d, d, d);
        }
        if (to != null) {
            java.sql.Date d = new java.sql.Date(DateUtil.endOfDay(to).getTime());
            it.addFilter(
                    "(bis <= ? OR erstellungsdatum <= ? OR (bis IS NULL AND erstellungsdatum IS NULL AND ausgefuehrt_am <= ?))",
                    d, d, d);
        }

        if (konto != null && (konto instanceof Konto))
            it.addFilter("konto_id = " + ((Konto) konto).getID());
        else if (konto != null && (konto instanceof String))
            it.addFilter("konto_id in (select id from konto where kategorie = ?)", (String) konto);

        if (unread)
            it.addFilter("gelesen_am is null");

        it.setOrder("order by jahr desc, nummer desc, " + service.getSQLTimestamp("erstellungsdatum") + " desc, "
                + service.getSQLTimestamp("von") + " desc, " + service.getSQLTimestamp("ausgefuehrt_am") + " desc");
        return it;
    }

    /**
     * Loescht die angegebenen Kontoauszuege und bei Bedarf auch die Dateien.
     * @param deleteFiles true, wenn auch die Dateien geloescht werden sollen.
     * @param list die zu loeschenden Kontoauszuege.
     */
    public static void delete(boolean deleteFiles, Kontoauszug... list) {
        if (list == null || list.length == 0)
            return;

        Kontoauszug tx = null;

        int count = 0;

        try {
            for (Kontoauszug k : list) {
                if (tx == null) {
                    tx = k;
                    tx.transactionBegin();
                }

                if (deleteFiles) {
                    String uuid = k.getUUID();
                    if (uuid != null && uuid.length() > 0) {
                        QueryMessage qm = new QueryMessage(uuid, null);
                        Application.getMessagingFactory().getMessagingQueue("jameica.messaging.del")
                                .sendSyncMessage(qm);
                    } else {
                        final String pfad = k.getPfad();
                        final String name = k.getDateiname();
                        if (pfad == null || pfad.length() == 0 || name == null || name.length() == 0) {
                            Logger.warn("filename or path missing for account statements, skipping");
                        } else {
                            File file = new File(pfad, name);
                            if (file.exists() && file.canWrite()) {
                                if (!file.delete())
                                    Logger.warn("deleting of file failed: " + file);
                            } else {
                                Logger.info("file does not exist, skipping: " + file);
                            }
                        }
                    }
                }

                Konto konto = k.getKonto();
                konto.addToProtokoll(i18n.tr("Elektronischen Kontoauszug gelscht"), Protokoll.TYP_SUCCESS);
                k.delete();
                count++;
            }

            if (tx != null)
                tx.transactionCommit();

            if (count == 1)
                Application.getMessagingFactory().sendMessage(
                        new StatusBarMessage(i18n.tr("Kontoauszug gelscht."), StatusBarMessage.TYPE_SUCCESS));
            else
                Application.getMessagingFactory().sendMessage(
                        new StatusBarMessage(i18n.tr("{0} Kontoauszge gelscht.", Integer.toString(count)),
                                StatusBarMessage.TYPE_SUCCESS));
        } catch (Exception e) {
            Logger.error("deleting account statements failed", e);

            if (tx != null) {
                try {
                    tx.transactionRollback();
                } catch (RemoteException re) {
                    Logger.error("tx rollback failed", re);
                }
            }
        }
    }

    /**
     * Markiert die Liste der angegebenen Kontoauszuege als gelesen.
     * Jedoch nur, wenn sie nicht bereits als gelesen markiert sind.
     * @param read true, wenn die Kontoauszuege als gelesen werden sollen. Sonst false.
     * @param list die Liste der als gelesen zu markierenden Kontoauszuege.
     */
    public static void markRead(boolean read, Kontoauszug... list) {
        if (list == null || list.length == 0)
            return;

        Kontoauszug tx = null;

        try {
            for (Kontoauszug k : list) {
                if (k.isNewObject())
                    continue;

                if (tx == null) {
                    tx = k;
                    tx.transactionBegin();
                }

                Date d = k.getGelesenAm();

                if (d == null && !read) {
                    Logger.info("account statement already marked as unread, skipping [id: " + k.getID() + "]");
                    continue;
                }
                if (d != null && read) {
                    Logger.info("account statement already marked as read, skipping [id: " + k.getID() + ", date: "
                            + d + "]");
                    continue;
                }

                d = read ? new Date() : null;
                Logger.info("mark account statements as " + (read ? "read" : "unread") + " [id: " + k.getID()
                        + ", date: " + d + "]");
                k.setGelesenAm(d);
                k.store();
                Application.getMessagingFactory().sendMessage(new ObjectChangedMessage(k));
            }

            if (tx != null)
                tx.transactionCommit();
        } catch (Exception e) {
            Logger.error("marking account statements as read failed", e);

            if (tx != null) {
                try {
                    tx.transactionRollback();
                } catch (RemoteException re) {
                    Logger.error("tx rollback failed", re);
                }
            }
        }
    }

    /**
     * Prueft, ob elektronische Kontoauszuege im PDF-Format fuer dieses Konto unterstuetzt werden.
     * @param k das zu pruefende Konto.
     * @return true, wenn es unterstuetzt wird.
     */
    public static boolean supported(Konto k) {
        if (k == null) {
            Logger.warn("no account given");
            return false;
        }

        try {
            Logger.debug("checking HKEKP/HKEKA support for account: " + (k != null ? k.getIban() : "<none>"));
        } catch (RemoteException re) {
            Logger.error("unable to determine iban");
        }

        // Wenn HKEKP unterstuetzt wird, haben wir auf jeden Fall PDF
        Support support = BPDUtil.getSupport(k, BPDUtil.Query.KontoauszugPdf);
        if (support != null && support.isSupported()) {
            Logger.debug("HKEKP supported");
            return true;
        }

        boolean ignoreSupport = false;
        try {
            ignoreSupport = Boolean.parseBoolean(MetaKey.KONTOAUSZUG_IGNORE_FORMAT.get(k));
        } catch (RemoteException re) {
            Logger.error("unable to determine account meta key value", re);
        }

        // Checken, ob wir HKEKA mit dem Format BPD haben
        support = BPDUtil.getSupport(k, BPDUtil.Query.Kontoauszug);

        if (support == null || !support.getBpdSupport())
            return conditionalSupport("HKEKA not supported according to BPD", ignoreSupport);

        if (!support.getUpdSupport())
            return conditionalSupport("HKEKA not supported according to UPD", ignoreSupport);

        // Wenn nur HKEKA unterstuetzt wird, muessen wir uns die angeboteten Dateiformate anschauen
        TypedProperties props = support.getBpd();
        if (props == null || props.size() == 0)
            return conditionalSupport("no BPD cache data found for HKEKA", ignoreSupport);

        List<Format> formats = getFormats(props);
        if (formats.size() == 0)
            return conditionalSupport("BPD cache contains no information regarding supported formats of HKEKA",
                    ignoreSupport);

        if (formats.contains(Format.PDF)) {
            Logger.debug("HKEKA with PDF supported");
            return true;
        }

        return conditionalSupport("HKEKA does not support PDF according to BPD", ignoreSupport);
    }

    /**
     * Ermittelt die Liste der unterstuetzten Formate aus den BPD.
     * @param bpd die BPD.
     * @return die Liste der Formate. Nie NULL sondern hoechstens eine leere Liste.
     */
    public static List<Format> getFormats(TypedProperties bpd) {
        List<Format> result = new ArrayList<Format>();
        if (bpd == null || bpd.size() == 0)
            return result;

        String[] formats = bpd.getList("format", null);

        // Checken, ob eventuell nur ein Format drin steht
        if (formats == null || formats.length == 0) {
            String format = bpd.getProperty("format", null);
            if (format != null)
                formats = new String[] { format };
        }

        if (formats == null || formats.length == 0)
            return result;

        // Checken, ob PDF dabei ist
        for (String f : formats) {
            Format gf = Format.find(f);
            if (gf != null)
                result.add(gf);
        }

        return result;
    }

    /**
     * Forciert den Support per optionalem Setting, auch wenn das Konto es laut BPD nicht kann.
     * @param reason ein Begruendungstext, warum der Support eigentlich nicht vorhanden ist.
     * @param ignoreSupport true, wenn die Kontoauszuege auch dann abgerufen werden sollen, wenn es eigentlich nicht unterstuetzt wird.
     * @return true, wenn der forcierte Support aktiviert ist, sonst false.
     */
    private static boolean conditionalSupport(String reason, boolean ignoreSupport) {
        Logger.debug(reason + " - support forced: " + ignoreSupport);
        return ignoreSupport;
    }
}