ReportsManager.java

/*
** Module   : ReportsManager.java
** Abstract : ReportsManager provides external API to generate reports.
**
** Copyright (c) 2017-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- ----------------Description----------------------
** 001 SBI 20170601 Created initial version.
** 002 TJD 20220504 Java 11 compatibility related minor changes
** 003 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
** 
** Additional terms under GNU Affero GPL version 3 section 7:
** 
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
** 
**   0. Attribution Requirement.
** 
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
** 
**   1. No License To Use Trademarks.
** 
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
** 
**   2. No Misrepresentation of Affiliation.
** 
**     You may not represent yourself as Golden Code Development Corporation or FWD.
** 
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
** 
**   3. No Misrepresentation of Source or Origin.
** 
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/


package com.goldencode.p2j.admin.server.reports;

import java.io.*;
import java.util.*;

import com.goldencode.p2j.util.logging.*;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.common.*;
import org.apache.pdfbox.pdmodel.font.*;

import com.goldencode.p2j.admin.AdminServerImpl;
import com.goldencode.p2j.admin.server.reports.PdfReportBuilder.*;
import com.goldencode.p2j.admin.server.reports.ReportBuilder.*;
import com.goldencode.p2j.admin.server.reports.TextReportBuilder.*;
import com.goldencode.p2j.admin.shared.*;

/**
 * Reports manager hides internal reports implementations. Provides external api to generate
 * reports.
 */
public class ReportsManager
{
   /** Logger */
   private static final CentralLogger LOG = CentralLogger.get(ReportsManager.class);
   
   /** The standard reports factory */
   private static ReportsFactory reportsFactory;
   
   /** The reports factory to build extended reports */
   private static ReportsFactory extReportsFactory;
   
   /** The extended reports factory class */
   private static Class<? extends ReportsFactory> extReportFactoryClass;
   
   /**
    * Gets the standard reports factory.
    * 
    * @return   The standard reports factory
    */
   private static ReportsFactory getReportsFactory()
   {
      if (reportsFactory == null)
      {
         reportsFactory = new ReportsFactoryImpl();
      }
      
      return reportsFactory;
   }
   
   /**
    * Gets the extended reports factory.
    * 
    * @return   The extended reports factory
    */
   private static ReportsFactory getExtReportsFactory()
   {
      if (extReportsFactory == null && extReportFactoryClass != null)
      {
         try
         {
            extReportsFactory = (ReportsFactory) extReportFactoryClass.getDeclaredConstructor().newInstance();
         }
         catch (ReflectiveOperationException e)
         {
            LOG.severe("Get ext reports factory exception", e);
         }
      }
      
      return extReportsFactory;
   }
   
   /**
    * Sets the report factory class to instantiate the extended report factory.
    *  
    * @param    reportFactoryClass
    *           The report factory class
    */
   public static synchronized void setExtReportFactoryClass(Class<? extends ReportsFactory> reportFactoryClass)
   {
      ReportsManager.extReportFactoryClass = reportFactoryClass;
   }
   
   /**
    * Gets the requested report factory.
    * 
    * @param    request
    *           The given report request
    * 
    * @return   The requested report factory
    */
   public static synchronized ReportsFactory getReportsFactory(ReportRequest request)
   {
      String ext = request.getReportParameters().getOrDefault(
               ReportParameters.EXTENSION.getParameter(), "");
      ReportsFactory factory;
      if (ext.isEmpty())
      {
         factory = getReportsFactory();
      }
      else
      {
         factory = getExtReportsFactory();
      }
      
      if (factory == null)
      {
         factory = getReportsFactory();
      }
      
      return factory;
   }
   
   /**
    * Builds the requested PDF report.
    * 
    * @param    request
    *           The report request
    * 
    * @return   The pdf document given by PDDocument
    * 
    * @throws   IOException
    *           Throws this exception iff this IO operation is failed
    */
   public static PDDocument buildPDFReport(ReportRequest request)
   throws IOException
   {
      PdfDocument doc = buildPDFReportDocument(request);
      
      return doc.getDocument();
   }
   
   /**
    * Builds the requested PDF report.
    * 
    * @param    request
    *           The report request
    * 
    * @return   The pdf document holder
    * 
    * @throws   IOException
    *           Throws this exception iff this IO operation is failed
    */
   private static PdfDocument buildPDFReportDocument(ReportRequest request)
   throws IOException
   {
      ReportsFactory factory = getReportsFactory(request);
      
      return factory.createPDFReportBuilder(request).buildReport(
               buildReportParameters(request));
   }
   
   /**
    * Builds the requested PDF page report.
    * 
    * @param    request
    *           The report request
    * @param    pageNumber
    *           The page number to preview its pdf report
    *  
    * @return   The pdf document given by PDDocument
    * 
    * @throws   IOException
    *           Throws this exception iff this IO operation is failed
    */
   public static PdfDocument buildPDFPagePreview(ReportRequest request, int pageNumber)
   throws IOException
   {
      request.getReportParameters().put(ReportParameters.PREVIEW_PAGE.getParameter(),
               String.valueOf(pageNumber));
      
      return buildPDFReportDocument(request);
   }
   
   /**
    * Builds the requested report.
    * 
    * @param    request
    *           The report request
    * 
    * @return   The report document
    * 
    * @throws   IOException
    *           Throws this exception iff this IO operation is failed
    */
   public static Document buildReport(ReportRequest request)
   throws IOException
   {
      ReportsFactory factory = getReportsFactory(request);
      
      String type = request.getReportParameters().getOrDefault(
               ReportParameters.REPORT_TYPE.getParameter(), DocumentType.PDF.name());
      if (DocumentType.get(type) == DocumentType.PDF)
      {
         return factory.createPDFReportBuilder(request).buildReport(buildReportParameters(request));
      }
      else
      {
         return factory.createTextReportBuilder(request).buildReport(buildTextReportParameters(request));
      }
   }
   
   /**
    * Builds the requested report settings for the text report type.
    * 
    * @param    request
    *           The text report request
    * 
    * @return   The text report settings
    */
   public static TextReportSettings buildTextReportParameters(ReportRequest request)
   {
      return new TextReportSettings()
      {
      };
   }

   /**
    * Builds the requested report settings for the PDF report type.
    * 
    * @param    request
    *           The PDF report request
    * 
    * @return   The PDF report settings
    */
   public static PdfReportSettings buildReportParameters(ReportRequest request)
   {
      final Map<String, String> parameters = request.getReportParameters();
      
      final PaperOrientation orient = PaperOrientation.valueOf(
               parameters.get(ReportParameters.PAPER_ORIENT.getParameter()));
      
      final PaperFormat format = PaperFormat.valueOf(
               parameters.get(ReportParameters.PAPER_FORMAT.getParameter()));
      
      // these server report parameters can't be changed on the client side
      Map<String, Object> map = AdminServerImpl.getReportParametersMap();
      
      Integer maxPagesNumber = (Integer) map.get(ReportParameters.MAX_PAGES_NUMBER.getParameter());
      
      //report geometry settings
      float[] margin = new float[4];
      margin[0] = (Float) map.get(ReportParameters.LEFT_PAGE_MARGIN.getParameter());
      margin[1] = (Float) map.get(ReportParameters.TOP_PAGE_MARGIN.getParameter());
      margin[2] = (Float) map.get(ReportParameters.RIGHT_PAGE_MARGIN.getParameter());
      margin[3] = (Float) map.get(ReportParameters.BOTTOM_PAGE_MARGIN.getParameter());
      
      Float cellMargin = (Float) map.get(ReportParameters.CELL_MARGIN.getParameter());
      Float cellPadding = (Float) map.get(ReportParameters.CELL_PADDING.getParameter());
      Float headerMargin = (Float) map.get(ReportParameters.HEADER_MARGIN.getParameter());
      Float footerMargin = (Float) map.get(ReportParameters.FOOTER_MARGIN.getParameter());
      
      //report font settings
      Integer fontSize = (Integer) map.get(ReportParameters.FONT_SIZE.getParameter());
      Integer columnFontSize = (Integer) map.get(ReportParameters.COLUMN_FONT_SIZE.getParameter());
      Integer headerFontSize = (Integer) map.get(ReportParameters.HEADER_FONT_SIZE.getParameter());
      Integer footerFontSize = (Integer) map.get(ReportParameters.FOOTER_FONT_SIZE.getParameter());
      
      //TODO the font family selection
      //String fontName = (String) map.get(ReportParameters.FONT_NAME.getParameter());
      //String boldFontName = (String) map.get(ReportParameters.BOLD_FONT_NAME.getParameter());

      /**
       * The anonymous implementation of PDF report settings
       */
      return new PdfReportSettings() {

         /**
          * Gets the report rectangle.
          *  
          * @return   The report rectangle
          */
         @Override
         public PDRectangle getReportSize()
         {
            switch(format)
            {
               case LETTER:
                  return PDRectangle.LETTER;
               case LEGAL:
                  return PDRectangle.LEGAL;
               case A0:
                  return PDRectangle.A0;
               case A1:
                  return PDRectangle.A1;
               case A2:
                  return PDRectangle.A2;
               case A3:
                  return PDRectangle.A3;
               case A4:
                  return PDRectangle.A4;
               case A5:
                  return PDRectangle.A5;
               case A6:
                  return PDRectangle.A6;
               default:
                  return PDRectangle.A4;
            }
         }

         /**
          * Gets the report paper orientation.
          * 
          * @return   The paper orientation
          */
         @Override
         public PaperOrientation getPaperOrientation()
         {
            return orient;
         }

         /**
          * Gets the normal report font.
          * 
          * @return   The normal report font
          */
         @Override
         public PDFont getFont()
         {
            return PDType1Font.COURIER;
         }

         /**
          * Gets the bold report font.
          * 
          * @return   The bold report font
          */
         @Override
         public PDFont getBoldFont()
         {
            return PDType1Font.COURIER_BOLD;
         }

         /**
          * Gets the base font size.
          * 
          * @return   The base font size
          */
         @Override
         public int getFontSize()
         {
            return fontSize.intValue();
         }

         /**
          * Gets the column header font size.
          * 
          * @return   The column header font size
          */
         @Override
         public int getColumnFontSize()
         {
            return columnFontSize.intValue();
         }

         /**
          * Gets the cell font size.
          * 
          * @return   The cell font size
          */
         @Override
         public int getCellFontSize()
         {
            return getFontSize();
         }

         /**
          * Gets the page header font size.
          * 
          * @return   The page header font size
          */
         @Override
         public int getHeaderFontSize()
         {
            return headerFontSize.intValue();
         }

         /**
          * Gets the page footer font size.
          * 
          * @return   The page footer font size
          */
         @Override
         public int getFooterFontSize()
         {
            return footerFontSize.intValue();
         }

         /**
          * Gets the page margin in this order: left, top, right and bottom. The values are
          * measured by points relative to the user space.
          * 
          * @return   The page margin
          */
         @Override
         public float[] getPageMargin()
         {
            return margin;
         }

         /**
          * Gets the header margin measured by points relative to the user space.
          * 
          * @return   The header margin
          */
         @Override
         public float getHeaderMargin()
         {
            return headerMargin.floatValue();
         }

         /**
          * Gets the footer margin measured by points relative to the user space.
          * 
          * @return   The footer margin
          */
         @Override
         public float getFooterMargin()
         {
            return footerMargin.floatValue();
         }

         /**
          * Gets the cell margin measured by points relative to the user space.
          * 
          * @return   The cell margin
          */
         @Override
         public float getCellMargin()
         {
            return cellMargin.floatValue();
         }

         /**
          * Gets the cell padding measured by points relative to the user space.
          * 
          * @return   The cell padding
          */
         @Override
         public float getCellPadding()
         {
            return cellPadding.floatValue();
         }

         /**
          * Gets the maximal pages number.
          * 
          * @return   The maximal pages number
          */
         @Override
         public int getMaxPagesNumber()
         {
            return maxPagesNumber;
         }
      };
   }
}