Java tutorial
/* * Copyright 2013 Haulmont * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.haulmont.yarg.formatters.impl; import com.google.common.base.Preconditions; import com.haulmont.yarg.exception.OpenOfficeException; import com.haulmont.yarg.exception.ReportingException; import com.haulmont.yarg.formatters.factory.FormatterFactoryInput; import com.haulmont.yarg.formatters.impl.doc.OfficeComponent; import com.haulmont.yarg.formatters.impl.doc.OfficeOutputStream; import com.haulmont.yarg.formatters.impl.doc.TableManager; import com.haulmont.yarg.formatters.impl.doc.connector.NoFreePortsException; import com.haulmont.yarg.formatters.impl.doc.connector.OfficeIntegrationAPI; import com.haulmont.yarg.formatters.impl.doc.connector.OfficeResourceProvider; import com.haulmont.yarg.formatters.impl.doc.connector.OfficeTask; import com.haulmont.yarg.formatters.impl.inline.ContentInliner; import com.haulmont.yarg.structure.BandData; import com.haulmont.yarg.structure.ReportFieldFormat; import com.haulmont.yarg.structure.ReportOutputType; import com.sun.star.container.NoSuchElementException; import com.sun.star.container.XIndexAccess; import com.sun.star.frame.XDispatchHelper; import com.sun.star.io.IOException; import com.sun.star.lang.WrappedTargetException; import com.sun.star.lang.XComponent; import com.sun.star.table.XCell; import com.sun.star.text.*; import com.sun.star.util.XReplaceable; import com.sun.star.util.XSearchDescriptor; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import static com.haulmont.yarg.formatters.impl.doc.UnoConverter.as; /** * Document formatter for '.doc' and '.odt' file types */ public class DocFormatter extends AbstractFormatter { protected static final Logger log = LoggerFactory.getLogger(DocFormatter.class); protected static final String SEARCH_REGULAR_EXPRESSION = "SearchRegularExpression"; protected static final String PDF_OUTPUT_FILE = "writer_pdf_Export"; protected static final String MS_WORD_OUTPUT_FILE = "MS Word 97"; protected XComponent xComponent; protected OfficeComponent officeComponent; protected OfficeIntegrationAPI officeIntegration; public DocFormatter(FormatterFactoryInput formatterFactoryInput, OfficeIntegrationAPI officeIntegration) { super(formatterFactoryInput); Preconditions.checkNotNull("\"officeIntegration\" parameter can not be null", officeIntegration); this.officeIntegration = officeIntegration; supportedOutputTypes.add(ReportOutputType.doc); supportedOutputTypes.add(ReportOutputType.pdf); } public void renderDocument() { try { doCreateDocument(outputStream); } catch (Exception e) {//just try again if any exceptions occurred log.warn(String.format( "An error occurred while generating doc report [%s]. System will retry to generate report again.", reportTemplate.getDocumentName()), e); for (int i = 0; i < officeIntegration.getCountOfRetry(); i++) { try { doCreateDocument(outputStream); return; } catch (NoFreePortsException e1) { if (e instanceof NoFreePortsException) { throw (NoFreePortsException) e; } } } throw wrapWithReportingException("An error occurred while generating doc report.", e); } finally { IOUtils.closeQuietly(outputStream); } } protected void doCreateDocument(final OutputStream outputStream) throws NoFreePortsException { OfficeTask officeTask = new OfficeTask() { @Override public void processTaskInOpenOffice(OfficeResourceProvider ooResourceProvider) { try { loadDocument(ooResourceProvider); // Handling tables fillTables(ooResourceProvider.getXDispatchHelper()); // Handling text replaceAllAliasesInDocument(); replaceAllAliasesInDocument();//we do it second time to handle several open office bugs (page breaks in html, etc). Do not remove. // Saving document to output stream and closing saveAndClose(ooResourceProvider, xComponent, outputType, outputStream); } catch (Exception e) { throw wrapWithReportingException("An error occurred while running task in Open Office server", e); } } }; officeIntegration.runTaskWithTimeout(officeTask, officeIntegration.getTimeoutInSeconds()); } protected void loadDocument(OfficeResourceProvider ooResourceProvider) throws com.sun.star.lang.IllegalArgumentException, IOException { xComponent = ooResourceProvider.loadXComponent(reportTemplate.getDocumentContent()); officeComponent = new OfficeComponent(ooResourceProvider, xComponent); } protected void saveAndClose(OfficeResourceProvider ooResourceProvider, XComponent xComponent, ReportOutputType outputType, OutputStream outputStream) throws IOException { OfficeOutputStream ooos = new OfficeOutputStream(outputStream); String filterName; if (ReportOutputType.pdf.equals(outputType)) { filterName = PDF_OUTPUT_FILE; } else { filterName = MS_WORD_OUTPUT_FILE; } ooResourceProvider.saveXComponent(xComponent, ooos, filterName); ooResourceProvider.closeXComponent(xComponent); } protected void fillTables(XDispatchHelper xDispatchHelper) throws com.sun.star.uno.Exception { List<String> tablesNames = TableManager.getTablesNames(xComponent); for (String tableName : tablesNames) { TableManager tableManager = new TableManager(xComponent, tableName); BandFinder bandFinder = new BandFinder(tableManager).find(); BandData band = bandFinder.getBand(); String bandName = bandFinder.getBandName(); int numberOfRowWithAliases = tableManager.findRowWithAliases(); if (band != null && numberOfRowWithAliases > -1) { XTextTable xTextTable = tableManager.getXTextTable(); // try to select one cell without it workaround int columnCount = xTextTable.getColumns().getCount(); if (columnCount < 2) { xTextTable.getColumns().insertByIndex(columnCount, 1); } fillTable(band.getName(), band.getParentBand(), tableManager, xDispatchHelper, numberOfRowWithAliases); // end of workaround -> if (columnCount < 2) { xTextTable.getColumns().removeByIndex(columnCount, 1); } } else if (numberOfRowWithAliases > -1 && rootBand.getFirstLevelBandDefinitionNames() != null && rootBand.getFirstLevelBandDefinitionNames().contains(bandName)) { //if table is linked with band and has aliases on it, but no band data found - //we are removing the row tableManager.deleteRow(numberOfRowWithAliases); } } } protected void fillTable(String name, BandData parentBand, TableManager tableManager, XDispatchHelper xDispatchHelper, int numberOfRowWithAliases) throws com.sun.star.uno.Exception { // Lock clipboard, cause uno uses it to grow tables, relevant for desktops synchronized (clipboardLock) { XTextTable xTextTable = tableManager.getXTextTable(); if (officeIntegration.isDisplayDeviceAvailable()) { clearClipboard(); } List<BandData> childrenByName = parentBand.getChildrenByName(name); for (BandData ignored : childrenByName) { tableManager.copyRow(xDispatchHelper, as(XTextDocument.class, xComponent).getCurrentController(), numberOfRowWithAliases); } int i = numberOfRowWithAliases; for (BandData child : childrenByName) { if (name.equals(child.getName())) { fillRow(child, tableManager, i); i++; } } tableManager.deleteRow(i); } } protected void fillRow(BandData band, TableManager tableManager, int row) throws com.sun.star.lang.IndexOutOfBoundsException, NoSuchElementException, WrappedTargetException { List<String> cellNamesForTheRow = tableManager.getCellNamesForTheRow(row); for (int col = 0; col < cellNamesForTheRow.size(); col++) { fillCell(band, tableManager.getXCell(col, row)); } } protected void fillCell(BandData band, XCell xCell) throws NoSuchElementException, WrappedTargetException { XText xText = as(XText.class, xCell); String cellText = xText.getString(); cellText = cellText.replace("\r\n", "\n");//just a workaround for Windows \r\n break symbol List<String> parametersToInsert = new ArrayList<String>(); Matcher matcher = UNIVERSAL_ALIAS_PATTERN.matcher(cellText); while (matcher.find()) { parametersToInsert.add(unwrapParameterName(matcher.group())); } for (String parameterName : parametersToInsert) { XTextCursor xTextCursor = xText.createTextCursor(); String paramStr = "${" + parameterName + "}"; int index = cellText.indexOf(paramStr); while (index >= 0) { xTextCursor.gotoStart(false); xTextCursor.goRight((short) (index + paramStr.length()), false); xTextCursor.goLeft((short) paramStr.length(), true); insertValue(xText, xTextCursor, band, parameterName); cellText = formatCellText(xText.getString()); index = cellText.indexOf(paramStr); } } } /** * Replaces all aliases ${bandname.paramname} in document text. * * @throws com.haulmont.yarg.exception.ReportingException If there is not appropriate band or alias is bad */ protected void replaceAllAliasesInDocument() { XTextDocument xTextDocument = as(XTextDocument.class, xComponent); XReplaceable xReplaceable = as(XReplaceable.class, xTextDocument); XSearchDescriptor searchDescriptor = xReplaceable.createSearchDescriptor(); searchDescriptor.setSearchString(ALIAS_WITH_BAND_NAME_REGEXP); try { searchDescriptor.setPropertyValue(SEARCH_REGULAR_EXPRESSION, true); } catch (Exception e) { throw new OpenOfficeException("An error occurred while setting search properties in Open office", e); } XIndexAccess indexAccess = xReplaceable.findAll(searchDescriptor); for (int i = 0; i < indexAccess.getCount(); i++) { try { XTextRange textRange = as(XTextRange.class, indexAccess.getByIndex(i)); String alias = unwrapParameterName(textRange.getString()); BandPathAndParameterName bandAndParameter = separateBandNameAndParameterName(alias); BandData band = findBandByPath(bandAndParameter.getBandPath()); if (band != null) { insertValue(textRange.getText(), textRange, band, bandAndParameter.getParameterName()); } else { throw wrapWithReportingException(String.format("No band for alias [%s] found", alias)); } } catch (ReportingException e) { throw e; } catch (Exception e) { throw wrapWithReportingException(String.format( "An error occurred while replacing aliases in document. Regexp [%s]. Replacement number [%d]", ALIAS_WITH_BAND_NAME_REGEXP, i), e); } } } protected void insertValue(XText text, XTextRange textRange, BandData band, String parameterName) { String fullParameterName = band.getName() + "." + parameterName; Object paramValue = band.getParameterValue(parameterName); Map<String, ReportFieldFormat> formats = rootBand.getReportFieldFormats(); try { boolean handled = false; if (paramValue != null) { if ((formats != null) && (formats.containsKey(fullParameterName))) { String format = formats.get(fullParameterName).getFormat(); // Handle doctags for (ContentInliner contentInliner : contentInliners) { Matcher matcher = contentInliner.getTagPattern().matcher(format); if (matcher.find()) { contentInliner.inlineToDoc(officeComponent, textRange, text, paramValue, matcher); handled = true; } } } if (!handled) { String valueString = formatValue(paramValue, parameterName, fullParameterName); text.insertString(textRange, valueString, true); } } else { text.insertString(textRange, "", true); } } catch (Exception ex) { throw wrapWithReportingException( String.format("An error occurred while inserting parameter [%s] into text line [%s]", parameterName, text.getString()), ex); } } //delete nonexistent symbols from cell text protected String formatCellText(String cellText) { if (cellText != null) { return cellText.replace("\r", ""); } else { return cellText; } } protected static final Object clipboardLock = new Object(); protected static void clearClipboard() { try { Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new Transferable() { public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[0]; } public boolean isDataFlavorSupported(DataFlavor flavor) { return false; } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { throw new UnsupportedFlavorException(flavor); } }, null); } catch (IllegalStateException ignored) { //ignore exception } } protected class BandFinder { protected String tableName; protected TableManager tableManager; protected String bandName; protected BandData band; public BandFinder(TableManager tableManager) { this.tableName = tableManager.getTableName(); this.tableManager = tableManager; } public String getBandName() { return bandName; } public BandData getBand() { return band; } public BandFinder find() { bandName = tableName; band = rootBand.findBandRecursively(bandName); if (band == null) { XText xText = tableManager.findFirstEntryInRow(BAND_NAME_DECLARATION_PATTERN, 0); if (xText != null) { Matcher matcher = BAND_NAME_DECLARATION_PATTERN.matcher(xText.getString()); if (matcher.find()) { bandName = matcher.group(1); band = rootBand.findBandRecursively(bandName); XTextCursor xTextCursor = xText.createTextCursor(); xTextCursor.gotoStart(false); xTextCursor.goRight((short) matcher.end(), false); xTextCursor.goLeft((short) matcher.group().length(), true); xText.insertString(xTextCursor, "", true); } } } return this; } } }