001: /*
002: * Copyright 2006-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.module.gl.util;
017:
018: import java.awt.Color;
019: import java.io.FileOutputStream;
020: import java.text.DecimalFormat;
021: import java.text.SimpleDateFormat;
022: import java.util.Date;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.Set;
026: import java.util.TreeSet;
027:
028: import org.kuali.core.util.KualiDecimal;
029:
030: import com.lowagie.text.Chunk;
031: import com.lowagie.text.Document;
032: import com.lowagie.text.DocumentException;
033: import com.lowagie.text.Element;
034: import com.lowagie.text.Font;
035: import com.lowagie.text.FontFactory;
036: import com.lowagie.text.PageSize;
037: import com.lowagie.text.Paragraph;
038: import com.lowagie.text.Phrase;
039: import com.lowagie.text.pdf.PdfPCell;
040: import com.lowagie.text.pdf.PdfPTable;
041: import com.lowagie.text.pdf.PdfWriter;
042:
043: /**
044: * This class represents the functionality needed to generate a Poster Output Summary Report
045: */
046: public class PosterOutputSummaryReport {
047: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
048: .getLogger(PosterOutputSummaryReport.class);
049: public static final String PDF_FILE_EXTENSION = ".pdf";
050:
051: private Font headerFont = FontFactory.getFont(FontFactory.COURIER,
052: Font.DEFAULTSIZE, Font.BOLD);
053: private Font textFont = FontFactory.getFont(FontFactory.COURIER,
054: Font.DEFAULTSIZE, Font.NORMAL);
055: private Font hiddenFieldFont = FontFactory.getFont(
056: FontFactory.COURIER, 1, Font.NORMAL, new Color(0xFF, 0xFF,
057: 0xFF));
058:
059: private static final int TYPE_DETAIL = 1;
060: private static final int TYPE_YEAR_PERIOD_BALANCE_SUBTOTAL = 2;
061: private static final int TYPE_YEAR_BALANCE_SUBTOTAL = 3;
062: private static final int TYPE_BALANCE_SUBTOTAL = 4;
063: private static final int TYPE_TOTAL = 5;
064:
065: /**
066: * This method generates report based on the given map of entries
067: *
068: * @param posterInputSummaryEntryHolder the given entry map
069: * @param reportingDate the reporting date
070: * @param title the report title
071: * @param fileprefix the prefix of the generated report file
072: * @param destinationDirectory the directory where the report is located
073: */
074: public void generateReport(
075: Map<String, PosterOutputSummaryEntry> data,
076: Date reportingDate, String title, String fileprefix,
077: String destinationDirectory) {
078: LOG.debug("generateReport() started");
079: Document document = new Document(PageSize.A4.rotate());
080:
081: PDFPageHelper pageHelper = new PDFPageHelper();
082: pageHelper.setRunDate(reportingDate);
083: pageHelper.setHeaderFont(headerFont);
084: pageHelper.setTitle(title);
085:
086: try {
087: String filename = destinationDirectory + "/" + fileprefix
088: + "_";
089: SimpleDateFormat sdf = new SimpleDateFormat(
090: "yyyyMMdd_HHmmss");
091: filename = filename + sdf.format(reportingDate);
092: filename = filename + PDF_FILE_EXTENSION;
093:
094: PdfWriter writer = PdfWriter.getInstance(document,
095: new FileOutputStream(filename));
096: writer.setPageEvent(pageHelper);
097:
098: document.open();
099:
100: if (data.size() == 0) {
101: document.add(buildEmptyTable());
102: } else {
103: printReport(data, document);
104: }
105: } catch (Exception de) {
106: LOG.error("generateReport() Error creating PDF report", de);
107: de.printStackTrace();
108: throw new RuntimeException("Report Generation Failed");
109: } finally {
110: closeDocument(document);
111: }
112: }
113:
114: /**
115: * This method returns a new PDF Table for this report
116: *
117: * @return PdfPTable a new table for this report
118: */
119: private PdfPTable newTable() {
120: PdfPTable entryTable = new PdfPTable(new float[] { 5, 5, 5, 7,
121: 7, 7, 7 });
122: entryTable.setHeaderRows(1);
123: entryTable.setWidthPercentage(100);
124:
125: addHeader(entryTable, headerFont);
126: return entryTable;
127: }
128:
129: /**
130: * This method populates the information for the poster output summary report
131: *
132: * @param data map containing poster output summary entries
133: * @param document document object representing actually PDF
134: * @throws DocumentException if there are any problems creating actual PDF document
135: */
136: private void printReport(
137: Map<String, PosterOutputSummaryEntry> data,
138: Document document) throws DocumentException {
139:
140: PdfPTable entryTable = newTable();
141:
142: PosterOutputSummaryEntry subTotalPeriodBalanceYear = new PosterOutputSummaryEntry();
143: PosterOutputSummaryEntry subTotalBalanceYear = new PosterOutputSummaryEntry();
144: PosterOutputSummaryEntry subTotalBalance = new PosterOutputSummaryEntry();
145: PosterOutputSummaryEntry total = new PosterOutputSummaryEntry();
146:
147: Set sortedSet = new TreeSet(data.keySet());
148:
149: boolean first = true;
150: for (Iterator reportIter = sortedSet.iterator(); reportIter
151: .hasNext();) {
152: String key = (String) reportIter.next();
153: PosterOutputSummaryEntry entry = data.get(key);
154:
155: if (first) {
156: first = false;
157: subTotalPeriodBalanceYear.setUniversityFiscalYear(entry
158: .getUniversityFiscalYear());
159: subTotalPeriodBalanceYear.setBalanceTypeCode(entry
160: .getBalanceTypeCode());
161: subTotalPeriodBalanceYear.setFiscalPeriodCode(entry
162: .getFiscalPeriodCode());
163:
164: subTotalBalanceYear.setUniversityFiscalYear(entry
165: .getUniversityFiscalYear());
166: subTotalBalanceYear.setBalanceTypeCode(entry
167: .getBalanceTypeCode());
168:
169: subTotalBalance.setBalanceTypeCode(entry
170: .getBalanceTypeCode());
171:
172: Paragraph paragraph = new Paragraph("Fiscal Year: "
173: + entry.getUniversityFiscalYear()
174: + " Balance Type Code: "
175: + entry.getBalanceTypeCode(), headerFont);
176: paragraph.setSpacingAfter(10);
177:
178: document.add(paragraph);
179: }
180:
181: // Do we need to print a subtotal?
182: String balanceTypeCode = entry.getBalanceTypeCode();
183: Integer fiscalYear = entry.getUniversityFiscalYear();
184: String fiscalPeriod = entry.getFiscalPeriodCode();
185:
186: boolean newPage = false;
187:
188: if ((!fiscalPeriod.equals(subTotalPeriodBalanceYear
189: .getFiscalPeriodCode()))
190: || (!balanceTypeCode
191: .equals(subTotalPeriodBalanceYear
192: .getBalanceTypeCode()))
193: || (!fiscalYear.equals(subTotalPeriodBalanceYear
194: .getUniversityFiscalYear()))) {
195: addRow(
196: entryTable,
197: subTotalPeriodBalanceYear,
198: headerFont,
199: PosterOutputSummaryReport.TYPE_YEAR_PERIOD_BALANCE_SUBTOTAL);
200: subTotalPeriodBalanceYear = new PosterOutputSummaryEntry();
201: subTotalPeriodBalanceYear.setUniversityFiscalYear(entry
202: .getUniversityFiscalYear());
203: subTotalPeriodBalanceYear.setBalanceTypeCode(entry
204: .getBalanceTypeCode());
205: subTotalPeriodBalanceYear.setFiscalPeriodCode(entry
206: .getFiscalPeriodCode());
207: }
208:
209: if ((!balanceTypeCode.equals(subTotalBalanceYear
210: .getBalanceTypeCode()))
211: || (!fiscalYear.equals(subTotalBalanceYear
212: .getUniversityFiscalYear()))) {
213: addRow(
214: entryTable,
215: subTotalBalanceYear,
216: headerFont,
217: PosterOutputSummaryReport.TYPE_YEAR_BALANCE_SUBTOTAL);
218: subTotalBalanceYear = new PosterOutputSummaryEntry();
219: subTotalBalanceYear.setUniversityFiscalYear(fiscalYear);
220: subTotalBalanceYear.setBalanceTypeCode(balanceTypeCode);
221:
222: newPage = true;
223: }
224: if (!balanceTypeCode.equals(subTotalBalance
225: .getBalanceTypeCode())) {
226: addRow(entryTable, subTotalBalance, headerFont,
227: PosterOutputSummaryReport.TYPE_BALANCE_SUBTOTAL);
228: subTotalBalance = new PosterOutputSummaryEntry();
229: subTotalBalance.setBalanceTypeCode(balanceTypeCode);
230: }
231: if (newPage) {
232: document.add(entryTable);
233: document.add(Chunk.NEXTPAGE);
234:
235: Paragraph paragraph = new Paragraph("Fiscal Year: "
236: + entry.getUniversityFiscalYear()
237: + " Balance Type Code: "
238: + entry.getBalanceTypeCode(), headerFont);
239: paragraph.setSpacingAfter(10);
240:
241: document.add(paragraph);
242:
243: entryTable = newTable();
244: }
245:
246: addRow(entryTable, entry, textFont,
247: PosterOutputSummaryReport.TYPE_DETAIL);
248: total.add(entry);
249: subTotalBalance.add(entry);
250: subTotalBalanceYear.add(entry);
251: subTotalPeriodBalanceYear.add(entry);
252: }
253:
254: addRow(
255: entryTable,
256: subTotalPeriodBalanceYear,
257: headerFont,
258: PosterOutputSummaryReport.TYPE_YEAR_PERIOD_BALANCE_SUBTOTAL);
259: addRow(entryTable, subTotalBalanceYear, headerFont,
260: PosterOutputSummaryReport.TYPE_YEAR_BALANCE_SUBTOTAL);
261: addRow(entryTable, subTotalBalance, headerFont,
262: PosterOutputSummaryReport.TYPE_BALANCE_SUBTOTAL);
263: addRow(entryTable, total, headerFont,
264: PosterOutputSummaryReport.TYPE_TOTAL);
265:
266: document.add(entryTable);
267: }
268:
269: /**
270: * Returns an empty table
271: *
272: * @return PdfPTable an empty table
273: */
274: private PdfPTable buildEmptyTable() {
275: float[] tableWidths = { 100 };
276:
277: PdfPTable ledgerEntryTable = new PdfPTable(tableWidths);
278: ledgerEntryTable.setWidthPercentage(100);
279: PdfPCell cell = new PdfPCell(new Phrase("No entries found!",
280: headerFont));
281: ledgerEntryTable.addCell(cell);
282:
283: return ledgerEntryTable;
284: }
285:
286: /**
287: * Adds a table header
288: *
289: * @param entryTable PdfPTable
290: * @param headerFont
291: */
292: private void addHeader(PdfPTable entryTable, Font headerFont) {
293:
294: PdfPCell cell = new PdfPCell(new Phrase("Fiscal Year",
295: headerFont));
296: entryTable.addCell(cell);
297:
298: cell = new PdfPCell(new Phrase("Period Code", headerFont));
299: entryTable.addCell(cell);
300:
301: cell = new PdfPCell(new Phrase("Fund Group", headerFont));
302: entryTable.addCell(cell);
303:
304: cell = new PdfPCell(new Phrase("Debit Amount", headerFont));
305: entryTable.addCell(cell);
306:
307: cell = new PdfPCell(new Phrase("Credit Amount", headerFont));
308: entryTable.addCell(cell);
309:
310: cell = new PdfPCell(new Phrase("Budget Amount", headerFont));
311: entryTable.addCell(cell);
312:
313: cell = new PdfPCell(new Phrase("Net Amount", headerFont));
314: entryTable.addCell(cell);
315: }
316:
317: /**
318: * Add a row with the given ledger entry into PDF table
319: *
320: * @param entryTable PdfPTable where row is being added too
321: * @param entry poster output summary entry
322: * @param textFont font for text
323: * @param type total type (i.e. year period balance subtotal, year balance subtotal, balance subtotal, total, or detail)
324: */
325: private void addRow(PdfPTable entryTable,
326: PosterOutputSummaryEntry entry, Font textFont, int type) {
327: PdfPCell cell = null;
328:
329: if (type == PosterOutputSummaryReport.TYPE_YEAR_PERIOD_BALANCE_SUBTOTAL) {
330: cell = new PdfPCell(new Phrase("Subtotal("
331: + entry.getFiscalPeriodCode() + ", "
332: + entry.getUniversityFiscalYear() + ", "
333: + entry.getBalanceTypeCode() + ")", textFont));
334: cell.setColspan(3);
335: entryTable.addCell(cell);
336: } else if (type == PosterOutputSummaryReport.TYPE_YEAR_BALANCE_SUBTOTAL) {
337: cell = new PdfPCell(new Phrase("Subtotal("
338: + entry.getUniversityFiscalYear() + ", "
339: + entry.getBalanceTypeCode() + ")", textFont));
340: cell.setColspan(3);
341: entryTable.addCell(cell);
342: } else if (type == PosterOutputSummaryReport.TYPE_BALANCE_SUBTOTAL) {
343: cell = new PdfPCell(new Phrase("Subtotal("
344: + entry.getBalanceTypeCode() + ")", textFont));
345: cell.setColspan(3);
346: entryTable.addCell(cell);
347: } else if (type == PosterOutputSummaryReport.TYPE_TOTAL) {
348: cell = new PdfPCell(new Phrase("Total", textFont));
349: cell.setColspan(3);
350: entryTable.addCell(cell);
351: } else if (type == PosterOutputSummaryReport.TYPE_DETAIL) {
352: Integer fiscalYear = entry.getUniversityFiscalYear();
353: String stringFiscalYear = (fiscalYear != null) ? fiscalYear
354: .toString() : "";
355: cell = new PdfPCell(new Phrase(stringFiscalYear, textFont));
356: entryTable.addCell(cell);
357:
358: cell = new PdfPCell(new Phrase(entry.getFiscalPeriodCode(),
359: textFont));
360: entryTable.addCell(cell);
361:
362: cell = new PdfPCell(new Phrase(entry.getFundGroup(),
363: textFont));
364: entryTable.addCell(cell);
365: }
366:
367: cell = new PdfPCell(new Phrase(this .formatNumber(entry
368: .getDebitAmount()), textFont));
369: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
370: entryTable.addCell(cell);
371:
372: cell = new PdfPCell(new Phrase(this .formatNumber(entry
373: .getCreditAmount()), textFont));
374: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
375: entryTable.addCell(cell);
376:
377: cell = new PdfPCell(new Phrase(this .formatNumber(entry
378: .getBudgetAmount()), textFont));
379: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
380: entryTable.addCell(cell);
381:
382: cell = new PdfPCell(new Phrase(this .formatNumber(entry
383: .getNetAmount()), textFont));
384: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
385: entryTable.addCell(cell);
386: }
387:
388: /**
389: * Format the given number based on its type: Integer or BigDecimal
390: * @param number
391: * @return String formatted BigDecimal or Integer as a String
392: */
393: private String formatNumber(Number number) {
394: DecimalFormat decimalFormat = new DecimalFormat();
395:
396: if (number instanceof Integer) {
397: decimalFormat.applyPattern("###,###");
398: } else if (number instanceof KualiDecimal) {
399: decimalFormat.applyPattern("###,###,###,##0.00");
400: }
401: return decimalFormat.format(number);
402: }
403:
404: /**
405: * Close the document and release the resource
406: *
407: * @param document document to be closed
408: */
409: private void closeDocument(Document document) {
410: try {
411: if ((document != null) && document.isOpen()) {
412: document.close();
413: }
414: } catch (Throwable t) {
415: LOG.error("generateReport() Exception closing report", t);
416: }
417: }
418: }
|