package org.phoenixctms.ctsms.pdf; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; import org.apache.pdfbox.pdmodel.font.PDFont; import org.phoenixctms.ctsms.enumeration.PaymentMethod; import org.phoenixctms.ctsms.pdf.ReimbursementsPDFBlock.BlockType; import org.phoenixctms.ctsms.util.CommonUtil; import org.phoenixctms.ctsms.util.CoreUtil; import org.phoenixctms.ctsms.util.L10nUtil; import org.phoenixctms.ctsms.util.L10nUtil.Locales; import org.phoenixctms.ctsms.util.Settings; import org.phoenixctms.ctsms.util.Settings.Bundle; import org.phoenixctms.ctsms.vo.MoneyTransferByBankAccountSummaryDetailVO; import org.phoenixctms.ctsms.vo.MoneyTransferByCostTypeSummaryDetailVO; import org.phoenixctms.ctsms.vo.MoneyTransferByPaymentMethodSummaryDetailVO; import org.phoenixctms.ctsms.vo.MoneyTransferSummaryVO; import org.phoenixctms.ctsms.vo.ProbandAddressOutVO; import org.phoenixctms.ctsms.vo.ProbandOutVO; import org.phoenixctms.ctsms.vo.ReimbursementsPDFVO; import org.phoenixctms.ctsms.vo.TrialOutVO; import org.phoenixctms.ctsms.vo.TrialTagValueOutVO; public class ReimbursementsPDFPainter extends PDFPainterBase implements PDFOutput { private static abstract class MoneyTransferByCostTypeSummaryDetailVOIterator<T> implements Iterator<T> { private MoneyTransferByCostTypeSummaryDetailVO current; private Iterator<MoneyTransferByCostTypeSummaryDetailVO> byCostTypesIt; private Iterator<String> commentsIt; private float heightLimit; protected MoneyTransferByCostTypeSummaryDetailVOIterator(T detail, float heightLimit) { byCostTypesIt = getByCostTypes(detail).iterator(); this.heightLimit = heightLimit; } protected abstract T createNewDetail(); protected abstract float getBlockHeight(T newDetail); protected abstract Collection<MoneyTransferByCostTypeSummaryDetailVO> getByCostTypes(T detail); @Override public boolean hasNext() { return byCostTypesIt.hasNext() || (commentsIt != null && commentsIt.hasNext()); } @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); } T result = createNewDetail(); while (hasNext() && getBlockHeight(result) < heightLimit) { MoneyTransferByCostTypeSummaryDetailVO next = new MoneyTransferByCostTypeSummaryDetailVO(); if (current == null || !commentsIt.hasNext()) { current = byCostTypesIt.next(); commentsIt = current.getComments().iterator(); next.setTotal(current.getTotal()); } next.setCostType(current.getCostType()); next.setCount(current.getCount()); next.setDecrypted(current.getDecrypted()); getByCostTypes(result).add(next); while (commentsIt.hasNext() && getBlockHeight(result) < heightLimit) { next.getComments().add(commentsIt.next()); } } return result; } @Override public void remove() { throw new UnsupportedOperationException(); } } protected static Iterator<MoneyTransferByBankAccountSummaryDetailVO> createByCostTypeIterator( final MoneyTransferByBankAccountSummaryDetailVO detail, final ReimbursementsPDFBlockCursor cursor) { return new MoneyTransferByCostTypeSummaryDetailVOIterator<MoneyTransferByBankAccountSummaryDetailVO>(detail, Settings.getFloat(ReimbursementsPDFSettingCodes.PAYMENT_TABLE_ROW_HEIGHT_LIMIT, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.PAYMENT_TABLE_ROW_HEIGHT_LIMIT)) { @Override protected MoneyTransferByBankAccountSummaryDetailVO createNewDetail() { MoneyTransferByBankAccountSummaryDetailVO newDetail = new MoneyTransferByBankAccountSummaryDetailVO(); newDetail.setId(detail.getId()); newDetail.setBankAccount(detail.getBankAccount()); newDetail.setCount(detail.getCount()); newDetail.setTotal(detail.getTotal()); return newDetail; } @Override protected float getBlockHeight(MoneyTransferByBankAccountSummaryDetailVO newDetail) { try { ReimbursementsPDFBlock block = new ReimbursementsPDFBlock(newDetail, false, true); block.setNewPage(true); return block.getHeight(cursor); } catch (Exception e) { return 0.0f; } } @Override protected Collection<MoneyTransferByCostTypeSummaryDetailVO> getByCostTypes(MoneyTransferByBankAccountSummaryDetailVO detail) { return detail.getByCostTypes(); } }; } protected static Iterator<MoneyTransferByPaymentMethodSummaryDetailVO> createByCostTypeIterator( final MoneyTransferByPaymentMethodSummaryDetailVO detail, final ReimbursementsPDFBlockCursor cursor) { return new MoneyTransferByCostTypeSummaryDetailVOIterator<MoneyTransferByPaymentMethodSummaryDetailVO>(detail, Settings.getFloat(ReimbursementsPDFSettingCodes.PAYMENT_TABLE_ROW_HEIGHT_LIMIT, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.PAYMENT_TABLE_ROW_HEIGHT_LIMIT)) { @Override protected MoneyTransferByPaymentMethodSummaryDetailVO createNewDetail() { MoneyTransferByPaymentMethodSummaryDetailVO newDetail = new MoneyTransferByPaymentMethodSummaryDetailVO(); newDetail.setMethod(detail.getMethod()); newDetail.setCount(detail.getCount()); newDetail.setTotal(detail.getTotal()); return newDetail; } @Override protected float getBlockHeight(MoneyTransferByPaymentMethodSummaryDetailVO newDetail) { try { ReimbursementsPDFBlock block = new ReimbursementsPDFBlock(newDetail, false, true); block.setNewPage(true); return block.getHeight(cursor); } catch (Exception e) { return 0.0f; } } @Override protected Collection<MoneyTransferByCostTypeSummaryDetailVO> getByCostTypes(MoneyTransferByPaymentMethodSummaryDetailVO detail) { return detail.getByCostTypes(); } }; } protected int blockIndex; protected ArrayList<ReimbursementsPDFBlock> blocks; protected ReimbursementsPDFBlockCursor cursor; protected ReimbursementsPDFVO pdfVO; protected TrialOutVO trialVO; protected Collection<TrialTagValueOutVO> trialTagValueVOs; protected Collection<ProbandOutVO> probandVOs; protected HashMap<Long, MoneyTransferSummaryVO> summaryMap; protected HashMap<Long, ProbandAddressOutVO> addressVOMap; protected float pageWidth; protected float pageHeight; protected PDFont fontA; protected PDFont fontB; protected PDFont fontC; protected PDFont fontD; protected PDFont fontE; protected PDFont fontF; protected final static PDRectangle DEFAULT_PAGE_SIZE = PDPage.PAGE_SIZE_A4; protected static final String REIMBURSEMENTS_PDF_FILENAME_PREFIX = "reimbursements_"; protected static final String REIMBURSEMENTS_PDF_FILENAME_TRIAL = "trial_"; protected static final String REIMBURSEMENTS_PDF_FILENAME_PROBAND = "proband_"; public ReimbursementsPDFPainter() { super(); blocks = new ArrayList<ReimbursementsPDFBlock>(); pdfVO = new ReimbursementsPDFVO(); cursor = new ReimbursementsPDFBlockCursor(this); setDrawPageNumbers(Settings.getBoolean(ReimbursementsPDFSettingCodes.SHOW_PAGE_NUMBERS, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.SHOW_PAGE_NUMBERS)); } @Override public void drawNextBlock(PDPageContentStream contentStream) throws Exception { ReimbursementsPDFBlock block = blocks.get(blockIndex); cursor.setBlockY(cursor.getBlockY() - block.renderBlock(contentStream, cursor)); blockIndex++; } @Override public void drawPage(PDPageContentStream contentStream) throws Exception { } @Override public void drawPageBreakNewPage(PDPageContentStream contentStream) throws Exception { ReimbursementsPDFBlock block = blocks.get(blockIndex); if (BlockType.PAYMENT_METHOD_TABLE_ROW.equals(block.getType()) || BlockType.BANK_ACCOUNT_TABLE_ROW.equals(block.getType())) { // paint ecrf header again cursor.setBlockY(cursor.getBlockY() - (new ReimbursementsPDFBlock(BlockType.MONEY_TRANSFER_TABLE_HEAD)).renderBlock(contentStream, cursor)); block.setNewPage(true); } } @Override public void drawPageBreakOldPage(PDPageContentStream contentStream) throws Exception { ReimbursementsPDFBlock block = blocks.get(blockIndex - 1); if (BlockType.PAYMENT_METHOD_TABLE_ROW.equals(block.getType()) || BlockType.BANK_ACCOUNT_TABLE_ROW.equals(block.getType())) { if (!block.isLastTableRow()) { PDFUtil.renderLine(contentStream, Settings.getColor(ReimbursementsPDFSettingCodes.PAYMENT_TABLE_FRAME_COLOR, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.PAYMENT_TABLE_FRAME_COLOR), cursor.getBlockX(), cursor.getBlockY(), cursor.getBlockX() + cursor.getBlockWidth(), cursor.getBlockY(), Settings.getFloat(ReimbursementsPDFSettingCodes.PAYMENT_TABLE_FRAME_LINE_WIDTH, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.PAYMENT_TABLE_FRAME_LINE_WIDTH)); } } } @Override protected void drawPageNumber(PDFImprinter writer, PDPage page, int pageNumber, int totalPages) throws IOException { PDPageContentStream contentStream = writer.openContentStream(page); PDFUtil.renderTextLine( contentStream, fontD, PDFUtil.FontSize.TINY, Settings.getColor(ReimbursementsPDFSettingCodes.TEXT_COLOR, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.TEXT_COLOR), L10nUtil.getReimbursementsPDFLabel(Locales.REIMBURSEMENTS_PDF, ReimbursementsPDFLabelCodes.PAGE_NUMBER, "", pageNumber, totalPages), Settings.getFloat(ReimbursementsPDFSettingCodes.PAGE_LEFT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.PAGE_LEFT_MARGIN) + (pageWidth - Settings.getFloat(ReimbursementsPDFSettingCodes.PAGE_LEFT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.PAGE_LEFT_MARGIN) - Settings .getFloat(ReimbursementsPDFSettingCodes.PAGE_RIGHT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.PAGE_RIGHT_MARGIN)) / 2.0f, Settings.getFloat(ReimbursementsPDFSettingCodes.PAGE_LOWER_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.PAGE_LOWER_MARGIN), PDFUtil.Alignment.BOTTOM_CENTER); writer.closeContentStream(); } @Override public PDRectangle getDefaultPageSize() { return DEFAULT_PAGE_SIZE; } public PDFont getFontA() { return fontA; } public PDFont getFontB() { return fontB; } public PDFont getFontC() { return fontC; } public PDFont getFontD() { return fontD; } public PDFont getFontE() { return fontE; } public PDFont getFontF() { return fontF; } @Override public PageOrientation getPageOrientation() { if (Settings.getBoolean(ReimbursementsPDFSettingCodes.LANDSCAPE, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.LANDSCAPE)) { return PageOrientation.LANDSCAPE; } else { return PageOrientation.PORTRAIT; } } public ReimbursementsPDFVO getPdfVO() { return pdfVO; } @Override public String getTemplateFileName() throws Exception { String key = L10nUtil.getDepartmentL10nKey(ReimbursementsPDFSettingCodes.TEMPLATE_FILE_NAME, cursor.getTrial()); if (Settings.containsKey(key, Bundle.REIMBURSEMENTS_PDF)) { return Settings.getPDFTemplateFilename(key, Bundle.REIMBURSEMENTS_PDF, null); } return Settings.getPDFTemplateFilename(ReimbursementsPDFSettingCodes.TEMPLATE_FILE_NAME, Bundle.REIMBURSEMENTS_PDF, null); } @Override public boolean hasNextBlock() { return blockIndex < blocks.size(); } @Override public void loadFonts(PDDocument doc) throws Exception { fontA = PDFUtil.loadFont(Settings.getPDFFontName(ReimbursementsPDFSettingCodes.FONT_A, Bundle.REIMBURSEMENTS_PDF, null), doc, DEFAULT_BASE_FONT); fontB = PDFUtil.loadFont(Settings.getPDFFontName(ReimbursementsPDFSettingCodes.FONT_B, Bundle.REIMBURSEMENTS_PDF, null), doc, DEFAULT_BASE_FONT); fontC = PDFUtil.loadFont(Settings.getPDFFontName(ReimbursementsPDFSettingCodes.FONT_C, Bundle.REIMBURSEMENTS_PDF, null), doc, DEFAULT_BASE_FONT); fontD = PDFUtil.loadFont(Settings.getPDFFontName(ReimbursementsPDFSettingCodes.FONT_D, Bundle.REIMBURSEMENTS_PDF, null), doc, DEFAULT_BASE_FONT); fontE = PDFUtil.loadFont(Settings.getPDFFontName(ReimbursementsPDFSettingCodes.FONT_E, Bundle.REIMBURSEMENTS_PDF, null), doc, DEFAULT_BASE_FONT); fontF = PDFUtil.loadFont(Settings.getPDFFontName(ReimbursementsPDFSettingCodes.FONT_F, Bundle.REIMBURSEMENTS_PDF, null), doc, DEFAULT_BASE_FONT); } @Override public void loadImages(PDDocument doc) { } @Override public boolean nextBlockFitsOnPage() throws Exception { ReimbursementsPDFBlock block = blocks.get(blockIndex); if (blockIndex > 0 && (BlockType.NEW_REIMBURSEMENT.equals(block.getType()) || BlockType.NEW_PAGE.equals(block.getType()))) { return false; } else { return (cursor.getBlockY() - block.getHeight(cursor)) > Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_LOWER_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_LOWER_MARGIN); } } @Override public void populateBlocks() { blocks.clear(); if (probandVOs != null && addressVOMap != null && summaryMap != null) { Iterator<ProbandOutVO> probandIt = probandVOs.iterator(); while (probandIt.hasNext()) { ProbandOutVO probandVO = probandIt.next(); ProbandAddressOutVO addressVO = addressVOMap.get(probandVO.getId()); MoneyTransferSummaryVO summary = summaryMap.get(probandVO.getId()); if (summary != null && summary.getCount() > 0l) { blocks.add(new ReimbursementsPDFBlock(trialVO, probandVO, BlockType.NEW_REIMBURSEMENT)); blocks.add(new ReimbursementsPDFBlock(addressVO, probandVO, BlockType.RECIPIENT_ADDRESS)); blocks.add(new ReimbursementsPDFBlock(now, BlockType.FIRST_PAGE_DATE)); blocks.add(new ReimbursementsPDFBlock(trialVO, BlockType.FIRST_PAGE_SUBJECT)); blocks.add(new ReimbursementsPDFBlock(probandVO, BlockType.SALUTATION)); if (trialVO != null) { blocks.add(new ReimbursementsPDFBlock(trialVO, BlockType.TRIAL_TITLE)); } for (int i = 0; i < Settings.getInt(ReimbursementsPDFSettingCodes.COPIES, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.COPIES); i++) { blocks.add(new ReimbursementsPDFBlock(BlockType.NEW_PAGE)); blocks.add(new ReimbursementsPDFBlock(addressVO, probandVO, BlockType.SENDER_ADDRESS)); blocks.add(new ReimbursementsPDFBlock(trialVO, BlockType.RETURN_ADDRESS)); blocks.add(new ReimbursementsPDFBlock(now, BlockType.SECOND_PAGE_DATE)); blocks.add(new ReimbursementsPDFBlock(trialVO, probandVO, BlockType.SECOND_PAGE_SUBJECT)); blocks.add(new ReimbursementsPDFBlock()); if (trialTagValueVOs != null && trialTagValueVOs.size() > 0) { blocks.add(new ReimbursementsPDFBlock(trialTagValueVOs)); blocks.add(new ReimbursementsPDFBlock()); } blocks.add(new ReimbursementsPDFBlock(BlockType.MONEY_TRANSFER_TABLE_HEAD)); Iterator it = summary.getTotalsByBankAccounts().iterator(); while (it.hasNext()) { MoneyTransferByBankAccountSummaryDetailVO detail = (MoneyTransferByBankAccountSummaryDetailVO) it.next(); if (detail.getCount() > 0l) { Iterator<MoneyTransferByBankAccountSummaryDetailVO> pager = createByCostTypeIterator(detail, cursor); boolean first = true; while (pager.hasNext()) { blocks.add(new ReimbursementsPDFBlock(pager.next(), first, !pager.hasNext())); first = false; } } } it = summary.getTotalsByPaymentMethods().iterator(); while (it.hasNext()) { MoneyTransferByPaymentMethodSummaryDetailVO detail = (MoneyTransferByPaymentMethodSummaryDetailVO) it.next(); if (detail.getCount() > 0l && !PaymentMethod.WIRE_TRANSFER.equals(detail.getMethod().getPaymentMethod())) { Iterator<MoneyTransferByPaymentMethodSummaryDetailVO> pager = createByCostTypeIterator(detail, cursor); boolean first = true; while (pager.hasNext()) { blocks.add(new ReimbursementsPDFBlock(pager.next(), first, !pager.hasNext())); first = false; } } } blocks.add(new ReimbursementsPDFBlock(probandVO, summary)); } } } } } @Override public void reset() { super.reset(); blockIndex = 0; pageWidth = DEFAULT_PAGE_SIZE.getWidth(); pageHeight = DEFAULT_PAGE_SIZE.getHeight(); cursor.setBlockY(pageHeight - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_UPPER_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_UPPER_MARGIN)); cursor.setBlockX(Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_LEFT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_LEFT_MARGIN)); cursor.setBlockWidth(pageWidth - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_RIGHT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_RIGHT_MARGIN) - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_LEFT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_LEFT_MARGIN)); cursor.setProband(null); cursor.setTrial(null); fontA = null; fontB = null; fontC = null; fontD = null; fontE = null; fontF = null; updateReimbursementsPDFVO(); } @Override public boolean save(ByteArrayOutputStream pdfStream) throws Exception { byte[] documentData = pdfStream.toByteArray(); pdfVO.setMd5(CommonUtil.getHex(MessageDigest.getInstance("MD5").digest(documentData))); pdfVO.setSize(documentData.length); pdfVO.setDocumentDatas(documentData); return true; } public void setAddressVOMap(HashMap<Long, ProbandAddressOutVO> addressVOMap) { this.addressVOMap = addressVOMap; } @Override public void setPageHeight(float pageHeight) { this.pageHeight = pageHeight; cursor.setBlockY(pageHeight - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_UPPER_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_UPPER_MARGIN)); } @Override public void setPageWidth(float pageWidth) { this.pageWidth = pageWidth; cursor.setBlockWidth(pageWidth - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_RIGHT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_RIGHT_MARGIN) - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_LEFT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_LEFT_MARGIN)); } public void setProbandVOs(Collection<ProbandOutVO> probandVOs) { this.probandVOs = probandVOs; } public void setSummaryMap(HashMap<Long, MoneyTransferSummaryVO> summaryMap) { this.summaryMap = summaryMap; } public void setTrialTagValueVOs(Collection<TrialTagValueOutVO> trialTagValueVOs) { this.trialTagValueVOs = trialTagValueVOs; } public void setTrialVO(TrialOutVO trialVO) { this.trialVO = trialVO; } @Override public void startNewPage() { super.startNewPage(!hasNextBlock() || BlockType.NEW_REIMBURSEMENT.equals(blocks.get(blockIndex).getType()), !hasNextBlock() || BlockType.NEW_REIMBURSEMENT.equals(blocks.get(blockIndex).getType()) || BlockType.NEW_PAGE.equals(blocks.get(blockIndex).getType())); cursor.setBlockY(pageHeight - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_UPPER_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_UPPER_MARGIN)); cursor.setBlockX(Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_LEFT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_LEFT_MARGIN)); cursor.setBlockWidth(pageWidth - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_RIGHT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_RIGHT_MARGIN) - Settings.getFloat(ReimbursementsPDFSettingCodes.BLOCKS_LEFT_MARGIN, Bundle.REIMBURSEMENTS_PDF, ReimbursementsPDFDefaultSettings.BLOCKS_LEFT_MARGIN)); } @Override public void updateCursor() { ReimbursementsPDFBlock block = blocks.get(blockIndex); if (BlockType.NEW_REIMBURSEMENT.equals(block.getType())) { cursor.setProband(block.getProband()); cursor.setTrial(block.getTrial()); } } protected void updateReimbursementsPDFVO() { pdfVO.setContentTimestamp(now); pdfVO.setContentType(CoreUtil.getPDFMimeType()); pdfVO.setProbands(probandVOs); pdfVO.setTrial(trialVO); StringBuilder fileName = new StringBuilder(REIMBURSEMENTS_PDF_FILENAME_PREFIX); if (trialVO != null) { fileName.append(REIMBURSEMENTS_PDF_FILENAME_TRIAL); fileName.append(trialVO.getId()); fileName.append("_"); } if (probandVOs != null && probandVOs.size() == 1) { fileName.append(REIMBURSEMENTS_PDF_FILENAME_PROBAND); fileName.append(probandVOs.iterator().next().getId()); fileName.append("_"); } fileName.append(CommonUtil.formatDate(now, CommonUtil.DIGITS_ONLY_DATETIME_PATTERN)); fileName.append("."); fileName.append(CoreUtil.PDF_FILENAME_EXTENSION); pdfVO.setFileName(fileName.toString()); } }