PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/saiku-core/saiku-service/src/main/java/org/saiku/service/util/export/excel/ExcelWorksheetBuilder.java

https://github.com/belerweb/saiku
Java | 498 lines | 368 code | 93 blank | 37 comment | 75 complexity | 62e8acf4429cfff3470c06c3a6501284 MD5 | raw file
Possible License(s): Apache-2.0
  1. package org.saiku.service.util.export.excel;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.text.SimpleDateFormat;
  6. import java.util.ArrayList;
  7. import java.util.Date;
  8. import java.util.HashMap;
  9. import java.util.List;
  10. import java.util.Map;
  11. import java.util.Properties;
  12. import org.apache.poi.hssf.usermodel.HSSFCellStyle;
  13. import org.apache.poi.hssf.usermodel.HSSFPalette;
  14. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  15. import org.apache.poi.hssf.util.HSSFColor;
  16. import org.apache.poi.ss.usermodel.Cell;
  17. import org.apache.poi.ss.usermodel.CellStyle;
  18. import org.apache.poi.ss.usermodel.DataFormat;
  19. import org.apache.poi.ss.usermodel.Font;
  20. import org.apache.poi.ss.usermodel.Row;
  21. import org.apache.poi.ss.usermodel.Sheet;
  22. import org.apache.poi.ss.util.CellRangeAddress;
  23. import org.saiku.olap.dto.SaikuDimensionSelection;
  24. import org.saiku.olap.dto.SaikuSelection;
  25. import org.saiku.olap.dto.resultset.AbstractBaseCell;
  26. import org.saiku.olap.dto.resultset.CellDataSet;
  27. import org.saiku.olap.dto.resultset.DataCell;
  28. import org.saiku.olap.util.SaikuProperties;
  29. import org.saiku.service.util.exception.SaikuServiceException;
  30. import org.slf4j.Logger;
  31. import org.slf4j.LoggerFactory;
  32. /**
  33. * Created with IntelliJ IDEA.
  34. * User: sramazzina
  35. * Date: 21/06/12
  36. * Time: 7.35
  37. * To change this template use File | Settings | File Templates.
  38. */
  39. public class ExcelWorksheetBuilder {
  40. private static final String BASIC_SHEET_FONT_FAMILY = "Arial";
  41. private static final short BASIC_SHEET_FONT_SIZE = 11;
  42. private static final String EMPTY_STRING = "";
  43. private static final String CSS_COLORS_CODE_PROPERTIES = "css-colors-codes.properties";
  44. private AbstractBaseCell[][] rowsetHeader;
  45. private AbstractBaseCell[][] rowsetBody;
  46. private HSSFWorkbook excelWorkbook;
  47. private Sheet workbookSheet;
  48. private String sheetName;
  49. private int topLeftCornerWidth;
  50. private int topLeftCornerHeight;
  51. private HSSFCellStyle basicCS;
  52. private HSSFCellStyle numberCS;
  53. private HSSFCellStyle lighterHeaderCellCS;
  54. private HSSFCellStyle darkerHeaderCellCS;
  55. private List<SaikuDimensionSelection> queryFilters;
  56. private Map<String, Integer> colorCodesMap;
  57. HSSFPalette customColorsPalette;
  58. int nextAvailableColorCode = 41;
  59. Properties cssColorCodesProperties;
  60. private static final Logger log = LoggerFactory.getLogger(ExcelWorksheetBuilder.class);
  61. public ExcelWorksheetBuilder(CellDataSet table, List<SaikuDimensionSelection> filters, String sheetName) {
  62. init(table, filters, sheetName);
  63. }
  64. protected void init(CellDataSet table, List<SaikuDimensionSelection> filters, String sheetName) {
  65. queryFilters = filters;
  66. excelWorkbook = new HSSFWorkbook();
  67. customColorsPalette = excelWorkbook.getCustomPalette();
  68. colorCodesMap = new HashMap<String, Integer>();
  69. this.sheetName = sheetName;
  70. rowsetHeader = table.getCellSetHeaders();
  71. rowsetBody = table.getCellSetBody();
  72. topLeftCornerWidth = findTopLeftCornerWidth();
  73. topLeftCornerHeight = findTopLeftCornerHeight();
  74. initCellStyles();
  75. }
  76. protected void initCellStyles() {
  77. Font font = excelWorkbook.createFont();
  78. font.setFontHeightInPoints((short) BASIC_SHEET_FONT_SIZE);
  79. font.setFontName(BASIC_SHEET_FONT_FAMILY);
  80. basicCS = excelWorkbook.createCellStyle();
  81. basicCS.setFont(font);
  82. basicCS.setAlignment(CellStyle.ALIGN_LEFT);
  83. setCellBordersColor(basicCS);
  84. numberCS = excelWorkbook.createCellStyle();
  85. numberCS.setFont(font);
  86. numberCS.setAlignment(CellStyle.ALIGN_RIGHT);
  87. setCellBordersColor(numberCS);
  88. /*
  89. justasg:
  90. Let's set default format, used if measure has no format at all.
  91. More info: http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/BuiltinFormats.html#getBuiltinFormat(int)
  92. If we don't have default format, it will output values up to maximum detail, i.e. 121212.3456789
  93. and we like them as 121,212.346
  94. */
  95. DataFormat fmt = excelWorkbook.createDataFormat();
  96. short dataFormat = fmt.getFormat(SaikuProperties.webExportExcelDefaultNumberFormat);
  97. numberCS.setDataFormat(dataFormat);
  98. Font headerFont = excelWorkbook.createFont();
  99. headerFont.setFontHeightInPoints((short) BASIC_SHEET_FONT_SIZE);
  100. headerFont.setFontName(BASIC_SHEET_FONT_FAMILY);
  101. headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
  102. lighterHeaderCellCS = excelWorkbook.createCellStyle();
  103. lighterHeaderCellCS.setFont(headerFont);
  104. lighterHeaderCellCS.setAlignment(CellStyle.ALIGN_CENTER);
  105. lighterHeaderCellCS.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index);
  106. lighterHeaderCellCS.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
  107. setCellBordersColor(lighterHeaderCellCS);
  108. darkerHeaderCellCS = excelWorkbook.createCellStyle();
  109. darkerHeaderCellCS.setFont(headerFont);
  110. darkerHeaderCellCS.setAlignment(CellStyle.ALIGN_CENTER);
  111. darkerHeaderCellCS.setFillForegroundColor(HSSFColor.GREY_40_PERCENT.index);
  112. darkerHeaderCellCS.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
  113. setCellBordersColor(darkerHeaderCellCS);
  114. }
  115. protected void setCellBordersColor(HSSFCellStyle style) {
  116. style.setBorderBottom(CellStyle.BORDER_THIN);
  117. style.setBottomBorderColor(HSSFColor.GREY_80_PERCENT.index);
  118. style.setBorderTop(CellStyle.BORDER_THIN);
  119. style.setTopBorderColor(HSSFColor.GREY_80_PERCENT.index);
  120. style.setBorderLeft(CellStyle.BORDER_THIN);
  121. style.setLeftBorderColor(HSSFColor.GREY_80_PERCENT.index);
  122. style.setBorderRight(CellStyle.BORDER_THIN);
  123. style.setRightBorderColor(HSSFColor.GREY_80_PERCENT.index);
  124. }
  125. public byte[] build() throws SaikuServiceException {
  126. int startRow = initExcelSheet();
  127. int lastHeaderRow = buildExcelTableHeader(startRow);
  128. addExcelTableRows(lastHeaderRow);
  129. finalizeExcelSheet(startRow);
  130. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  131. try {
  132. excelWorkbook.write(bout);
  133. } catch (IOException e) {
  134. throw new SaikuServiceException("Error creating excel export for query", e);
  135. }
  136. return bout.toByteArray();
  137. }
  138. private void finalizeExcelSheet(int startRow) {
  139. int headerWidth = rowsetHeader.length;
  140. // Autosize columns
  141. for (int i=0; i<rowsetBody[0].length; i++) {
  142. workbookSheet.autoSizeColumn(i);
  143. }
  144. // Freeze the header columns
  145. workbookSheet.createFreezePane( 0, startRow + headerWidth, 0, startRow + headerWidth );
  146. }
  147. private int initExcelSheet() {
  148. // Main Workbook Sheet
  149. workbookSheet = excelWorkbook.createSheet(this.sheetName);
  150. initSummarySheet();
  151. return 0;
  152. }
  153. private void initSummarySheet() {
  154. // Main Workbook Sheet
  155. Sheet summarySheet = excelWorkbook.createSheet("Summary page");
  156. int row = 1;
  157. Row sheetRow = summarySheet.createRow((short) row);
  158. Cell cell = sheetRow.createCell(0);
  159. String todayDate = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date());
  160. cell.setCellValue("Export date and time: " + todayDate);
  161. summarySheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 2));
  162. row = row+2;
  163. sheetRow = summarySheet.createRow((short) row);
  164. cell = sheetRow.createCell(0);
  165. cell.setCellValue("Dimension");
  166. cell = sheetRow.createCell(1);
  167. cell.setCellValue("Level");
  168. cell = sheetRow.createCell(2);
  169. cell.setCellValue("Filter Applied");
  170. row++;
  171. for (SaikuDimensionSelection item : queryFilters) {
  172. for (SaikuSelection s : item.getSelections()) {
  173. sheetRow = summarySheet.createRow((short) row);
  174. cell = sheetRow.createCell(0);
  175. cell.setCellValue(s.getDimensionUniqueName());
  176. cell = sheetRow.createCell(1);
  177. cell.setCellValue(s.getLevelUniqueName());
  178. cell = sheetRow.createCell(2);
  179. cell.setCellValue(s.getCaption());
  180. row++;
  181. }
  182. }
  183. row += 2;
  184. sheetRow = summarySheet.createRow((short) row);
  185. cell = sheetRow.createCell(0);
  186. cell.setCellValue("Export made using Saiku OLAP client.");
  187. summarySheet.addMergedRegion(new CellRangeAddress(row, row, 0, 10));
  188. // Autosize columns for summary sheet
  189. for (int i=0; i<5; i++) {
  190. summarySheet.autoSizeColumn(i);
  191. }
  192. }
  193. private void addExcelTableRows(int startingRow) {
  194. Row sheetRow = null;
  195. Cell cell = null;
  196. String formatString = null;
  197. for (int x = 0; x < rowsetBody.length; x++) {
  198. sheetRow = workbookSheet.createRow((short) x + startingRow);
  199. for (int y = 0; y < rowsetBody[x].length; y++) {
  200. cell = sheetRow.createCell(y);
  201. String value = rowsetBody[x][y].getFormattedValue();
  202. if (value == null) {
  203. // If the row cells has a null values it means the value is repeated in the data internally
  204. // but not in the interface. To properly format the Excel export file we need that value so we
  205. // get it from the same position in the prev row
  206. value = workbookSheet.getRow(sheetRow.getRowNum()-1).getCell(y).getStringCellValue();
  207. }
  208. if (rowsetBody[x][y] instanceof DataCell && ((DataCell) rowsetBody[x][y]).getRawNumber() != null) {
  209. Number numberValue = ((DataCell) rowsetBody[x][y]).getRawNumber();
  210. cell.setCellValue(numberValue.doubleValue());
  211. applyCellFormatting(cell, x, y);
  212. } else {
  213. cell.setCellStyle(basicCS);
  214. cell.setCellValue(value);
  215. }
  216. }
  217. }
  218. }
  219. protected void applyCellFormatting(Cell cell, int x, int y) {
  220. String formatString;
  221. formatString = ((DataCell) rowsetBody[x][y]).getFormatString();
  222. if ((formatString != null) && (formatString.trim().length() > 0)) {
  223. // Inherit formatting from cube schema FORMAT_STRING
  224. CellStyle numberCSClone = excelWorkbook.createCellStyle();
  225. numberCSClone.cloneStyleFrom(numberCS);
  226. DataFormat fmt = excelWorkbook.createDataFormat();
  227. // the format string can contain macro values such as "Standard" from mondrian.util.Format
  228. // try and look it up, otherwise use the given one
  229. formatString = FormatUtil.getFormatString(formatString);
  230. short dataFormat = fmt.getFormat(formatString);
  231. numberCSClone.setDataFormat(dataFormat);
  232. // Check for cell background
  233. Map<String, String> properties = ((DataCell) rowsetBody[x][y]).getProperties();
  234. if (properties.containsKey("style")) {
  235. short colorCodeIndex = getColorFromCustomPalette(properties.get("style"));
  236. if (colorCodeIndex != -1) {
  237. numberCSClone.setFillForegroundColor(colorCodeIndex);
  238. numberCSClone.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
  239. }
  240. }
  241. cell.setCellStyle(numberCSClone);
  242. } else {
  243. cell.setCellStyle(numberCS);
  244. }
  245. }
  246. private short getColorFromCustomPalette(String style) {
  247. short returnedColorIndex = -1;
  248. InputStream is = null;
  249. if (colorCodesMap.containsKey(style)) {
  250. returnedColorIndex = colorCodesMap.get(style).shortValue();
  251. } else {
  252. try {
  253. if (cssColorCodesProperties == null) {
  254. is = getClass().getResourceAsStream(CSS_COLORS_CODE_PROPERTIES);
  255. if (is != null) {
  256. cssColorCodesProperties = new Properties();
  257. cssColorCodesProperties.load(is);
  258. }
  259. }
  260. String colorCode = cssColorCodesProperties.getProperty(style);
  261. if (colorCode != null) {
  262. int redCode = Integer.parseInt(colorCode.substring(1, 3), 16);
  263. int greenCode = Integer.parseInt(colorCode.substring(3, 5), 16);
  264. int blueCode = Integer.parseInt(colorCode.substring(5, 7), 16);
  265. customColorsPalette.setColorAtIndex(new Byte((byte) nextAvailableColorCode), new Byte((byte) redCode), new Byte((byte) greenCode), new Byte((byte) blueCode));
  266. returnedColorIndex = customColorsPalette.getColor(nextAvailableColorCode).getIndex();
  267. colorCodesMap.put(style, new Integer(returnedColorIndex));
  268. nextAvailableColorCode++;
  269. }
  270. } catch (IOException e) {
  271. e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
  272. } finally {
  273. try {
  274. if (is != null)
  275. is.close();
  276. } catch (IOException e) {
  277. e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
  278. }
  279. }
  280. }
  281. return returnedColorIndex; //To change body of created methods use File | Settings | File Templates.
  282. }
  283. protected int buildExcelTableHeader(int startRow) {
  284. Row sheetRow = null;
  285. int x = 0;
  286. int y = 0;
  287. int startSameFromPos = 0;
  288. int mergedCellsWidth = 0;
  289. boolean isLastHeaderRow = false;
  290. boolean isLastColumn = false;
  291. String nextHeader = EMPTY_STRING;
  292. String currentHeader = EMPTY_STRING;
  293. ArrayList<ExcelMergedRegionItemConfig> mergedItemsConfig = new ArrayList<ExcelMergedRegionItemConfig>();
  294. for (x = 0; x < rowsetHeader.length; x++) {
  295. sheetRow = workbookSheet.createRow((short) x + startRow);
  296. nextHeader = EMPTY_STRING;
  297. isLastColumn = false;
  298. startSameFromPos = 0;
  299. mergedCellsWidth = 0;
  300. if (x + 1 == rowsetHeader.length) isLastHeaderRow = true;
  301. for (y = 0; y < rowsetHeader[x].length; y++) {
  302. currentHeader = rowsetHeader[x][y].getFormattedValue();
  303. if (currentHeader != null) {
  304. if (rowsetHeader[x].length == y+1)
  305. isLastColumn = true;
  306. else
  307. nextHeader = rowsetHeader[x][y+1].getFormattedValue();
  308. manageColumnHeaderDisplay(sheetRow, x, y, currentHeader);
  309. if (!isLastHeaderRow) {
  310. if (!nextHeader.equals(currentHeader) || isLastColumn) {
  311. manageCellsMerge(y,
  312. x + startRow,
  313. mergedCellsWidth + 1,
  314. startSameFromPos,
  315. mergedItemsConfig);
  316. startSameFromPos = y+1;
  317. mergedCellsWidth = 0;
  318. } else if (nextHeader.equals(currentHeader)) {
  319. mergedCellsWidth++;
  320. }
  321. }
  322. } else
  323. startSameFromPos++;
  324. }
  325. // Manage the merge condition on exit from columns scan
  326. if (!isLastHeaderRow)
  327. manageCellsMerge(y - 1, x, mergedCellsWidth+1, startSameFromPos, mergedItemsConfig);
  328. }
  329. if (topLeftCornerHeight > 0 && topLeftCornerWidth > 0) {
  330. workbookSheet.addMergedRegion(new CellRangeAddress(startRow, startRow + topLeftCornerHeight - 1, 0, topLeftCornerWidth - 1));
  331. }
  332. if (mergedItemsConfig.size()>0) {
  333. for (ExcelMergedRegionItemConfig item : mergedItemsConfig) {
  334. workbookSheet.addMergedRegion(new CellRangeAddress(item.getStartY(), item.getStartY() + item.getHeight(),
  335. item.getStartX(), item.getStartX() + item.getWidth() - 1));
  336. }
  337. }
  338. return x + startRow;
  339. }
  340. private void manageColumnHeaderDisplay(Row sheetRow, int x, int y, String currentHeader) {
  341. if (topLeftCornerHeight > 0 && x >= topLeftCornerHeight) {
  342. fillHeaderCell(sheetRow, currentHeader, y);
  343. } else if ((topLeftCornerHeight > 0 && x < topLeftCornerHeight) &&
  344. (topLeftCornerWidth > 0 && y >= topLeftCornerWidth)) {
  345. fillHeaderCell(sheetRow, currentHeader, y);
  346. } else if (topLeftCornerHeight == 0 && topLeftCornerWidth == 0)
  347. fillHeaderCell(sheetRow, currentHeader, y);
  348. }
  349. private void manageCellsMerge(int rowPos, int colPos,
  350. int width,
  351. int startSameFromPos,
  352. ArrayList<ExcelMergedRegionItemConfig> mergedItemsConfig) {
  353. ExcelMergedRegionItemConfig foundItem = null;
  354. boolean itemGetFromList = false;
  355. if (width == 1) return;
  356. for (ExcelMergedRegionItemConfig item : mergedItemsConfig) {
  357. if (item.getStartY() == colPos && item.getStartX() == rowPos) {
  358. foundItem = item;
  359. itemGetFromList = true;
  360. }
  361. }
  362. if (foundItem == null)
  363. foundItem = new ExcelMergedRegionItemConfig();
  364. foundItem.setHeight(0);
  365. foundItem.setWidth(width);
  366. foundItem.setStartX(startSameFromPos);
  367. foundItem.setStartY(colPos);
  368. if (mergedItemsConfig.isEmpty() || !itemGetFromList)
  369. mergedItemsConfig.add(foundItem);
  370. }
  371. private void fillHeaderCell(Row sheetRow, String formattedValue, int y) {
  372. Cell cell = sheetRow.createCell(y);
  373. cell.setCellValue(formattedValue);
  374. cell.setCellStyle(lighterHeaderCellCS);
  375. }
  376. /**
  377. * Find the width in cells of the top left corner of the table
  378. *
  379. * @return
  380. */
  381. private int findTopLeftCornerWidth() {
  382. int width = 0;
  383. int x = 0;
  384. boolean exit = (rowsetHeader[0][0].getRawValue() != null);
  385. String cellValue = null;
  386. for (x = 0; (!exit && rowsetHeader[0].length > x); x++) {
  387. cellValue = rowsetHeader[0][x].getRawValue();
  388. if (cellValue == null) {
  389. width = x + 1;
  390. } else {
  391. exit = true;
  392. }
  393. }
  394. return width;
  395. }
  396. /**
  397. * Find the height in cells of the top left corner of the table
  398. *
  399. * @return
  400. */
  401. private int findTopLeftCornerHeight() {
  402. int height = rowsetHeader.length - 1;
  403. return height;
  404. }
  405. }