PageRenderTime 56ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/pentaho/di/trans/steps/excelwriter/ExcelWriterStep.java

https://bitbucket.org/ghooning/pentaho-kettle-4.3.0-stable
Java | 935 lines | 639 code | 166 blank | 130 comment | 203 complexity | b84f6536268cba938c4b3d41c46cab83 MD5 | raw file
  1. /*******************************************************************************
  2. *
  3. * Pentaho Data Integration
  4. *
  5. * Copyright (C) 2002-2012 by Pentaho : http://www.pentaho.com
  6. *
  7. *******************************************************************************
  8. *
  9. * Licensed under the Apache License, Version 2.0 (the "License");
  10. * you may not use this file except in compliance with
  11. * the License. You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing, software
  16. * distributed under the License is distributed on an "AS IS" BASIS,
  17. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. * See the License for the specific language governing permissions and
  19. * limitations under the License.
  20. *
  21. ******************************************************************************/
  22. package org.pentaho.di.trans.steps.excelwriter;
  23. import java.io.BufferedInputStream;
  24. import java.io.BufferedOutputStream;
  25. import java.io.IOException;
  26. import java.io.OutputStream;
  27. import org.apache.commons.vfs.FileObject;
  28. import org.apache.poi.ss.usermodel.Hyperlink;
  29. import org.apache.poi.hssf.usermodel.HSSFSheet;
  30. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  31. import org.apache.poi.ss.usermodel.Cell;
  32. import org.apache.poi.ss.usermodel.ClientAnchor;
  33. import org.apache.poi.ss.usermodel.Comment;
  34. import org.apache.poi.ss.usermodel.CreationHelper;
  35. import org.apache.poi.ss.usermodel.DataFormat;
  36. import org.apache.poi.ss.usermodel.Drawing;
  37. import org.apache.poi.ss.usermodel.Font;
  38. import org.apache.poi.ss.usermodel.FormulaEvaluator;
  39. import org.apache.poi.ss.usermodel.IndexedColors;
  40. import org.apache.poi.ss.usermodel.RichTextString;
  41. import org.apache.poi.ss.usermodel.Row;
  42. import org.apache.poi.ss.usermodel.Sheet;
  43. import org.apache.poi.ss.usermodel.Workbook;
  44. import org.apache.poi.ss.util.CellUtil;
  45. import org.apache.poi.ss.util.CellReference;
  46. import org.apache.poi.xssf.usermodel.XSSFSheet;
  47. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  48. import org.pentaho.di.core.Const;
  49. import org.pentaho.di.core.ResultFile;
  50. import org.pentaho.di.core.exception.KettleException;
  51. import org.pentaho.di.core.row.ValueMeta;
  52. import org.pentaho.di.core.row.ValueMetaInterface;
  53. import org.pentaho.di.core.vfs.KettleVFS;
  54. import org.pentaho.di.i18n.BaseMessages;
  55. import org.pentaho.di.trans.Trans;
  56. import org.pentaho.di.trans.TransMeta;
  57. import org.pentaho.di.trans.step.*;
  58. public class ExcelWriterStep extends BaseStep implements StepInterface
  59. {
  60. private ExcelWriterStepData data;
  61. private ExcelWriterStepMeta meta;
  62. private static Class<?> PKG = ExcelWriterStepMeta.class; // for i18n
  63. public ExcelWriterStep(StepMeta s, StepDataInterface stepDataInterface, int c, TransMeta t, Trans dis)
  64. {
  65. super(s, stepDataInterface, c, t, dis);
  66. }
  67. public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException
  68. {
  69. meta = (ExcelWriterStepMeta) smi;
  70. data = (ExcelWriterStepData) sdi;
  71. // get next row
  72. Object[] r = getRow();
  73. // first row initialization
  74. if (first && r != null) {
  75. first = false;
  76. data.outputRowMeta = getInputRowMeta().clone();
  77. data.inputRowMeta = getInputRowMeta().clone();
  78. // if we are supposed to init the file dalayed, here we go
  79. if (r != null && meta.isDoNotOpenNewFileInit()) {
  80. data.firstFileOpened = true;
  81. prepareNextOutputFile();
  82. }
  83. // remember where the output fields are in the input row
  84. data.fieldnrs = new int[meta.getOutputFields().length];
  85. for (int i = 0; i < meta.getOutputFields().length; i++) {
  86. data.fieldnrs[i] = data.inputRowMeta.indexOfValue(meta.getOutputFields()[i].getName());
  87. if (data.fieldnrs[i] < 0) {
  88. logError("Field [" + meta.getOutputFields()[i].getName() + "] couldn't be found in the input stream!");
  89. setErrors(1);
  90. stopAll();
  91. return false;
  92. }
  93. }
  94. // remember where the comment fields are in the input row
  95. data.commentfieldnrs = new int[meta.getOutputFields().length];
  96. for (int i = 0; i < meta.getOutputFields().length; i++) {
  97. data.commentfieldnrs[i] = data.inputRowMeta.indexOfValue(meta.getOutputFields()[i].getCommentField());
  98. if (data.commentfieldnrs[i] < 0 && !Const.isEmpty(meta.getOutputFields()[i].getCommentField())) {
  99. logError("Comment Field [" + meta.getOutputFields()[i].getCommentField() + "] couldn't be found in the input stream!");
  100. setErrors(1);
  101. stopAll();
  102. return false;
  103. }
  104. }
  105. // remember where the comment author fields are in the input row
  106. data.commentauthorfieldnrs = new int[meta.getOutputFields().length];
  107. for (int i = 0; i < meta.getOutputFields().length; i++) {
  108. data.commentauthorfieldnrs[i] = data.inputRowMeta.indexOfValue(meta.getOutputFields()[i].getCommentAuthorField());
  109. if (data.commentauthorfieldnrs[i] < 0 && !Const.isEmpty(meta.getOutputFields()[i].getCommentAuthorField())) {
  110. logError("Comment Author Field [" + meta.getOutputFields()[i].getCommentAuthorField() + "] couldn't be found in the input stream!");
  111. setErrors(1);
  112. stopAll();
  113. return false;
  114. }
  115. }
  116. // remember where the link fields are in the input row
  117. data.linkfieldnrs = new int[meta.getOutputFields().length];
  118. for (int i = 0; i < meta.getOutputFields().length; i++) {
  119. data.linkfieldnrs[i] = data.inputRowMeta.indexOfValue(meta.getOutputFields()[i].getHyperlinkField());
  120. if (data.linkfieldnrs[i] < 0 && !Const.isEmpty(meta.getOutputFields()[i].getHyperlinkField())) {
  121. logError("Link Field [" + meta.getOutputFields()[i].getHyperlinkField() + "] couldn't be found in the input stream!");
  122. setErrors(1);
  123. stopAll();
  124. return false;
  125. }
  126. }
  127. }
  128. if (r != null) {
  129. // File Splitting Feature, is it time to create a new file?
  130. if (!meta.isAppendLines() && meta.getSplitEvery() > 0 && data.datalines > 0 && data.datalines % meta.getSplitEvery() == 0) {
  131. closeOutputFile();
  132. prepareNextOutputFile();
  133. }
  134. writeNextLine(r);
  135. incrementLinesOutput();
  136. data.datalines++;
  137. // pass on the row unchanged
  138. putRow(data.outputRowMeta, r);
  139. // Some basic logging
  140. if (checkFeedback(getLinesOutput())) {
  141. if (log.isBasic())
  142. logBasic("Linenr " + getLinesOutput());
  143. }
  144. return true;
  145. }
  146. else {
  147. // after the last row, the (last) file is closed
  148. if (data.wb != null) {
  149. closeOutputFile();
  150. }
  151. setOutputDone();
  152. clearWorkbookMem();
  153. return false;
  154. }
  155. }
  156. // clears all memory that POI may hold
  157. private void clearWorkbookMem()
  158. {
  159. data.file = null;
  160. data.sheet = null;
  161. data.wb = null;
  162. data.clearStyleCache(0);
  163. }
  164. private void closeOutputFile() throws KettleException
  165. {
  166. try {
  167. // may have to write a footer here
  168. if (meta.isFooterEnabled()) {
  169. writeHeader();
  170. }
  171. // handle auto size for columns
  172. if (meta.isAutoSizeColums()) {
  173. if (meta.getOutputFields() == null || meta.getOutputFields().length == 0) {
  174. for (int i = 0; i < data.inputRowMeta.size(); i++) {
  175. data.sheet.autoSizeColumn(i + data.startingCol);
  176. }
  177. }
  178. else {
  179. for (int i = 0; i < meta.getOutputFields().length; i++) {
  180. data.sheet.autoSizeColumn(i + data.startingCol);
  181. }
  182. }
  183. }
  184. // force recalculation of formulas if requested
  185. if (meta.isForceFormulaRecalculation()) {
  186. recalculateAllWorkbookFormulas();
  187. }
  188. BufferedOutputStream out = new BufferedOutputStream(KettleVFS.getOutputStream(data.file, false));
  189. data.wb.write(out);
  190. out.close();
  191. }
  192. catch (IOException e) {
  193. throw new KettleException(e);
  194. }
  195. }
  196. // recalculates all formula fields for the entire workbook
  197. private void recalculateAllWorkbookFormulas()
  198. {
  199. if (data.wb instanceof XSSFWorkbook) {
  200. // XLSX needs full reevaluation
  201. FormulaEvaluator evaluator = data.wb.getCreationHelper().createFormulaEvaluator();
  202. for (int sheetNum = 0; sheetNum < data.wb.getNumberOfSheets(); sheetNum++) {
  203. Sheet sheet = data.wb.getSheetAt(sheetNum);
  204. for (Row r : sheet) {
  205. for (Cell c : r) {
  206. if (c.getCellType() == Cell.CELL_TYPE_FORMULA) {
  207. evaluator.evaluateFormulaCell(c);
  208. }
  209. }
  210. }
  211. }
  212. }
  213. else if (data.wb instanceof HSSFWorkbook) {
  214. // XLS supports a "dirty" flag to have excel recalculate everything when a sheet is opened
  215. for (int sheetNum = 0; sheetNum < data.wb.getNumberOfSheets(); sheetNum++) {
  216. HSSFSheet sheet = ((HSSFWorkbook) data.wb).getSheetAt(sheetNum);
  217. sheet.setForceFormulaRecalculation(true);
  218. }
  219. }
  220. }
  221. public void writeNextLine(Object[] r) throws KettleException
  222. {
  223. try {
  224. openLine();
  225. Row xlsRow = data.sheet.getRow(data.posY);
  226. if (xlsRow == null) {
  227. xlsRow = data.sheet.createRow(data.posY);
  228. }
  229. Object v = null;
  230. if (meta.getOutputFields() == null || meta.getOutputFields().length == 0) {
  231. /*
  232. * Write all values in stream to text file.
  233. */
  234. int nr = data.inputRowMeta.size();
  235. data.clearStyleCache(nr);
  236. data.linkfieldnrs = new int[nr];
  237. data.commentfieldnrs = new int[nr];
  238. for (int i = 0; i < nr; i++) {
  239. v = r[i];
  240. writeField(v, data.inputRowMeta.getValueMeta(i), null, xlsRow, data.posX++, r, i, false);
  241. }
  242. // go to the next line
  243. data.posX = data.startingCol;
  244. data.posY++;
  245. }
  246. else {
  247. /*
  248. * Only write the fields specified!
  249. */
  250. for (int i = 0; i < meta.getOutputFields().length; i++) {
  251. v = r[data.fieldnrs[i]];
  252. writeField(v, data.inputRowMeta.getValueMeta(data.fieldnrs[i]), meta.getOutputFields()[i], xlsRow, data.posX++, r, i, false);
  253. }
  254. // go to the next line
  255. data.posX = data.startingCol;
  256. data.posY++;
  257. }
  258. }
  259. catch (Exception e) {
  260. logError("Error writing line :" + e.toString());
  261. throw new KettleException(e);
  262. }
  263. }
  264. private Comment createCellComment(String author, String comment)
  265. {
  266. // comments only supported for XLSX
  267. if (data.sheet instanceof XSSFSheet) {
  268. CreationHelper factory = data.wb.getCreationHelper();
  269. Drawing drawing = data.sheet.createDrawingPatriarch();
  270. ClientAnchor anchor = factory.createClientAnchor();
  271. Comment cmt = drawing.createCellComment(anchor);
  272. RichTextString str = factory.createRichTextString(comment);
  273. cmt.setString(str);
  274. cmt.setAuthor(author);
  275. return cmt;
  276. }
  277. return null;
  278. }
  279. /**
  280. * @param reference
  281. * @return the cell the refernce points to
  282. */
  283. private Cell getCellFromReference(String reference)
  284. {
  285. CellReference cellRef = new CellReference(reference);
  286. String sheetName = cellRef.getSheetName();
  287. Sheet sheet = data.sheet;
  288. if (!Const.isEmpty(sheetName)) {
  289. sheet = data.wb.getSheet(sheetName);
  290. }
  291. if (sheet == null) {
  292. return null;
  293. }
  294. // reference is assumed to be absolute
  295. Row xlsRow = sheet.getRow(cellRef.getRow());
  296. if (xlsRow == null) {
  297. return null;
  298. }
  299. Cell styleCell = xlsRow.getCell(cellRef.getCol());
  300. return styleCell;
  301. }
  302. private void writeField(Object v, ValueMetaInterface vMeta, ExcelWriterStepField excelField, Row xlsRow, int posX, Object[] row, int fieldNr, boolean isTitle) throws KettleException
  303. {
  304. try {
  305. boolean cellExisted = true;
  306. // get the cell
  307. Cell cell = xlsRow.getCell(posX);
  308. if (cell == null) {
  309. cellExisted = false;
  310. cell = xlsRow.createCell(posX);
  311. }
  312. // if cell existed and existing cell's styles should not be changed, don't
  313. if (!(cellExisted && meta.isLeaveExistingStylesUnchanged())) {
  314. // if the style of this field is cached, reuse it
  315. if (!isTitle && data.getCachedStyle(fieldNr) != null) {
  316. cell.setCellStyle(data.getCachedStyle(fieldNr));
  317. }
  318. else {
  319. // apply style if requested
  320. if (excelField != null) {
  321. // determine correct cell for title or data rows
  322. String styleRef = null;
  323. if (!isTitle && !Const.isEmpty(excelField.getStyleCell())) {
  324. styleRef = excelField.getStyleCell();
  325. }
  326. else if (isTitle && !Const.isEmpty(excelField.getTitleStyleCell())) {
  327. styleRef = excelField.getTitleStyleCell();
  328. }
  329. if (styleRef != null) {
  330. Cell styleCell = getCellFromReference(styleRef);
  331. if (styleCell != null && cell != styleCell) {
  332. cell.setCellStyle(styleCell.getCellStyle());
  333. }
  334. }
  335. }
  336. // set cell format as specified, specific format overrides cell specification
  337. if (!isTitle && excelField != null && !Const.isEmpty(excelField.getFormat()) && !excelField.getFormat().startsWith("Image")) {
  338. DataFormat format = data.wb.createDataFormat();
  339. short formatIndex = format.getFormat(excelField.getFormat());
  340. CellUtil.setCellStyleProperty(cell, data.wb, CellUtil.DATA_FORMAT, formatIndex);
  341. }
  342. // cache it for later runs
  343. if (!isTitle) {
  344. data.cacheStyle(fieldNr, cell.getCellStyle());
  345. }
  346. }
  347. }
  348. // create link on cell if requested
  349. if (!isTitle && excelField != null && data.linkfieldnrs[fieldNr] >= 0) {
  350. String link = data.inputRowMeta.getValueMeta(data.linkfieldnrs[fieldNr]).getString(row[data.linkfieldnrs[fieldNr]]);
  351. if (!Const.isEmpty(link)) {
  352. CreationHelper ch = data.wb.getCreationHelper();
  353. // set the link on the cell depending on link type
  354. Hyperlink hyperLink = null;
  355. if (link.startsWith("http:") || link.startsWith("https:") || link.startsWith("ftp:")) {
  356. hyperLink = ch.createHyperlink(Hyperlink.LINK_URL);
  357. hyperLink.setLabel("URL Link");
  358. }
  359. else if (link.startsWith("mailto:")) {
  360. hyperLink = ch.createHyperlink(Hyperlink.LINK_EMAIL);
  361. hyperLink.setLabel("Email Link");
  362. }
  363. else if (link.startsWith("'")) {
  364. hyperLink = ch.createHyperlink(Hyperlink.LINK_DOCUMENT);
  365. hyperLink.setLabel("Link within this document");
  366. }
  367. else {
  368. hyperLink = ch.createHyperlink(Hyperlink.LINK_FILE);
  369. hyperLink.setLabel("Link to a file");
  370. }
  371. hyperLink.setAddress(link);
  372. cell.setHyperlink(hyperLink);
  373. // if cell existed and existing cell's styles should not be changed, don't
  374. if (!(cellExisted && meta.isLeaveExistingStylesUnchanged())) {
  375. if (data.getCachedLinkStyle(fieldNr) != null) {
  376. cell.setCellStyle(data.getCachedLinkStyle(fieldNr));
  377. }
  378. else {
  379. //CellStyle style = cell.getCellStyle();
  380. Font origFont = data.wb.getFontAt(cell.getCellStyle().getFontIndex());
  381. Font hlink_font = data.wb.createFont();
  382. // reporduce original font characteristics
  383. hlink_font.setBoldweight(origFont.getBoldweight());
  384. hlink_font.setCharSet(origFont.getCharSet());
  385. hlink_font.setFontHeight(origFont.getFontHeight());
  386. hlink_font.setFontName(origFont.getFontName());
  387. hlink_font.setItalic(origFont.getItalic());
  388. hlink_font.setStrikeout(origFont.getStrikeout());
  389. hlink_font.setTypeOffset(origFont.getTypeOffset());
  390. // make it blue and underlined
  391. hlink_font.setUnderline(Font.U_SINGLE);
  392. hlink_font.setColor(IndexedColors.BLUE.getIndex());
  393. CellUtil.setCellStyleProperty(cell, data.wb, CellUtil.FONT, hlink_font.getIndex());
  394. data.cacheLinkStyle(fieldNr, cell.getCellStyle());
  395. }
  396. }
  397. }
  398. }
  399. // create comment on cell if requrested
  400. if (!isTitle && excelField != null && data.commentfieldnrs[fieldNr] >= 0 && data.wb instanceof XSSFWorkbook) {
  401. String comment = data.inputRowMeta.getValueMeta(data.commentfieldnrs[fieldNr]).getString(row[data.commentfieldnrs[fieldNr]]);
  402. if (!Const.isEmpty(comment)) {
  403. String author = data.commentauthorfieldnrs[fieldNr] >= 0 ? data.inputRowMeta.getValueMeta(data.commentauthorfieldnrs[fieldNr]).getString(row[data.commentauthorfieldnrs[fieldNr]]) : "Kettle PDI";
  404. cell.setCellComment(createCellComment(author, comment));
  405. }
  406. }
  407. // cell is getting a formula value or static content
  408. if (!isTitle && excelField != null && excelField.isFormula()) {
  409. // formula case
  410. cell.setCellFormula(vMeta.getString(v));
  411. }
  412. else {
  413. // static content case
  414. switch (vMeta.getType()) {
  415. case ValueMetaInterface.TYPE_DATE: {
  416. if (v != null && vMeta.getDate(v) != null) {
  417. cell.setCellValue(vMeta.getDate(v));
  418. }
  419. }
  420. break;
  421. case ValueMetaInterface.TYPE_BOOLEAN: {
  422. if (v != null) {
  423. cell.setCellValue(vMeta.getBoolean(v));
  424. }
  425. }
  426. break;
  427. case ValueMetaInterface.TYPE_STRING:
  428. case ValueMetaInterface.TYPE_BINARY:
  429. {
  430. if (v != null) {
  431. cell.setCellValue(vMeta.getString(v));
  432. }
  433. }
  434. break;
  435. case ValueMetaInterface.TYPE_BIGNUMBER:
  436. case ValueMetaInterface.TYPE_NUMBER:
  437. case ValueMetaInterface.TYPE_INTEGER: {
  438. if (v != null) {
  439. cell.setCellValue(vMeta.getNumber(v));
  440. }
  441. }
  442. break;
  443. default:
  444. break;
  445. }
  446. }
  447. }
  448. catch (Exception e) {
  449. logError("Error writing field (" + data.posX + "," + data.posY + ") : " + e.toString());
  450. logError(Const.getStackTracker(e));
  451. throw new KettleException(e);
  452. }
  453. }
  454. /**
  455. * Returns the output filename that belongs to this step observing the file split feature
  456. *
  457. * @return current output filename to write to
  458. */
  459. public String buildFilename(int splitNr)
  460. {
  461. return meta.buildFilename(this, getCopy(), splitNr);
  462. }
  463. /**
  464. * Copies a VFS File
  465. *
  466. * @param in
  467. * the source file object
  468. * @param out
  469. * the destination file object
  470. * @throws KettleException
  471. */
  472. public static void copyFile(FileObject in, FileObject out) throws KettleException
  473. {
  474. BufferedInputStream fis = null;
  475. BufferedOutputStream fos = null;
  476. try {
  477. fis = new BufferedInputStream(KettleVFS.getInputStream(in));
  478. fos = new BufferedOutputStream(KettleVFS.getOutputStream(out, false));
  479. byte[] buf = new byte[1024 * 1024]; // copy in chunks of 1 MB
  480. int i = 0;
  481. while ((i = fis.read(buf)) != -1) {
  482. fos.write(buf, 0, i);
  483. }
  484. fos.flush();
  485. fos.close();
  486. fis.close();
  487. }
  488. catch (Exception e) {
  489. throw new KettleException(e);
  490. }
  491. finally {
  492. if (fis != null) {
  493. try {
  494. fis.close();
  495. }
  496. catch (IOException e) {
  497. e.printStackTrace();
  498. }
  499. }
  500. if (fos != null) {
  501. try {
  502. fos.close();
  503. }
  504. catch (IOException e) {
  505. e.printStackTrace();
  506. }
  507. }
  508. }
  509. }
  510. public void prepareNextOutputFile() throws KettleException
  511. {
  512. try {
  513. // clear style cache
  514. int numOfFields = meta.getOutputFields() != null && meta.getOutputFields().length > 0 ? meta.getOutputFields().length : 0;
  515. if (numOfFields == 0) {
  516. numOfFields = data.inputRowMeta != null ? data.inputRowMeta.size() : 0;
  517. }
  518. data.clearStyleCache(numOfFields);
  519. // build new filename
  520. String buildFilename = buildFilename(data.splitnr);
  521. data.file = KettleVFS.getFileObject(buildFilename, getTransMeta());
  522. if (log.isDebug())
  523. logDebug(BaseMessages.getString(PKG, "ExcelWriterStep.Log.OpeningFile", buildFilename));
  524. // determine whether existing file must be deleted
  525. if (data.file.exists() && data.createNewFile) {
  526. if (!data.file.delete()) {
  527. if (log.isBasic()) {
  528. logBasic(BaseMessages.getString(PKG, "ExcelWriterStep.Log.CouldNotDeleteStaleFile", buildFilename));
  529. }
  530. setErrors(1);
  531. throw new KettleException("Could not delete stale file " + buildFilename);
  532. }
  533. }
  534. // adding filename to result
  535. if (meta.isAddToResultFiles()) {
  536. // Add this to the result file names...
  537. ResultFile resultFile = new ResultFile(ResultFile.FILE_TYPE_GENERAL, data.file, getTransMeta().getName(), getStepname());
  538. resultFile.setComment("This file was created with an Excel writer step by Pentaho Data Integration");
  539. addResultFile(resultFile);
  540. }
  541. boolean appendingToSheet = true;
  542. // if now no file exists we must create it as indicated by user
  543. if (!data.file.exists()) {
  544. // if template file is enabled
  545. if (meta.isTemplateEnabled()) {
  546. // handle template case (must have same format)
  547. // ensure extensions match
  548. String templateExt = KettleVFS.getFileObject(data.realTemplateFileName).getName().getExtension();
  549. if (!meta.getExtension().equalsIgnoreCase(templateExt)) {
  550. throw new KettleException("Template Format Mismatch: Template has extension: " + templateExt + ", but output file has extension: " + meta.getExtension() + ". Template and output file must share the same format!");
  551. }
  552. if (KettleVFS.getFileObject(data.realTemplateFileName).exists()) {
  553. // if the template exists just copy the template in place
  554. copyFile(KettleVFS.getFileObject(data.realTemplateFileName, getTransMeta()), data.file);
  555. }
  556. else {
  557. // template is missing, log it and get out
  558. if (log.isBasic())
  559. logBasic(BaseMessages.getString(PKG, "ExcelWriterStep.Log.TemplateMissing", data.realTemplateFileName));
  560. setErrors(1);
  561. throw new KettleException("Template file missing: " + data.realTemplateFileName);
  562. }
  563. }
  564. else {
  565. // handle fresh file case, just create a fresh workbook
  566. Workbook wb = meta.getExtension().equalsIgnoreCase("xlsx") ? new XSSFWorkbook() : new HSSFWorkbook();
  567. OutputStream out = KettleVFS.getOutputStream(data.file, false);
  568. wb.createSheet(data.realSheetname);
  569. wb.write(out);
  570. out.close();
  571. }
  572. appendingToSheet = false;
  573. }
  574. // file is guaranteed to be in place now
  575. if (meta.getExtension().equalsIgnoreCase("xlsx")) {
  576. data.wb = new XSSFWorkbook(KettleVFS.getInputStream(data.file));
  577. }
  578. else {
  579. data.wb = new HSSFWorkbook(KettleVFS.getInputStream(data.file));
  580. }
  581. int existingActiveSheetIndex = data.wb.getActiveSheetIndex();
  582. int replacingSheetAt = -1;
  583. if (data.wb.getSheet(data.realSheetname) != null) {
  584. // sheet exists, replace or reuse as indicated by user
  585. if (data.createNewSheet) {
  586. replacingSheetAt = data.wb.getSheetIndex(data.wb.getSheet(data.realSheetname));
  587. data.wb.removeSheetAt(replacingSheetAt);
  588. }
  589. }
  590. // if sheet is now missing, we need to create a new one
  591. if (data.wb.getSheet(data.realSheetname) == null) {
  592. if (meta.isTemplateSheetEnabled()) {
  593. Sheet ts = data.wb.getSheet(data.realTemplateSheetName);
  594. // if template sheet is missing, break
  595. if (ts == null) {
  596. throw new KettleException("Tempate Sheet: " + data.realTemplateSheetName + " not found, aborting");
  597. }
  598. data.sheet = data.wb.cloneSheet(data.wb.getSheetIndex(ts));
  599. data.wb.setSheetName(data.wb.getSheetIndex(data.sheet), data.realSheetname);
  600. // unhide sheet in case it was hidden
  601. data.wb.setSheetHidden(data.wb.getSheetIndex(data.sheet), false);
  602. }
  603. // no template to use, simply create a new sheet
  604. else {
  605. data.sheet = data.wb.createSheet(data.realSheetname);
  606. }
  607. if (replacingSheetAt > -1) {
  608. data.wb.setSheetOrder(data.sheet.getSheetName(), replacingSheetAt);
  609. }
  610. // preserves active sheet selection in workbook
  611. data.wb.setActiveSheet(existingActiveSheetIndex);
  612. data.wb.setSelectedTab(existingActiveSheetIndex);
  613. appendingToSheet = false;
  614. }
  615. else {
  616. // sheet is there and should be reused
  617. data.sheet = data.wb.getSheet(data.realSheetname);
  618. }
  619. // if use chose to make the current sheet active, do so
  620. if (meta.isMakeSheetActive()) {
  621. int sheetIndex = data.wb.getSheetIndex(data.sheet);
  622. data.wb.setActiveSheet(sheetIndex);
  623. data.wb.setSelectedTab(sheetIndex);
  624. }
  625. // handle write protection
  626. if (meta.isSheetProtected()) {
  627. // Write protect Sheet by setting password
  628. // works only for xls output at the moment
  629. if (data.wb instanceof HSSFWorkbook) {
  630. ((HSSFWorkbook) data.wb).writeProtectWorkbook(data.realPassword, Const.isEmpty(meta.getProtectedBy()) ? "Kettle PDI" : data.realProtectedBy);
  631. }
  632. }
  633. // starting cell support
  634. data.startingRow = 0;
  635. data.startingCol = 0;
  636. if (!Const.isEmpty(data.realStartingCell)) {
  637. CellReference cellRef = new CellReference(data.realStartingCell);
  638. data.startingRow = cellRef.getRow();
  639. data.startingCol = cellRef.getCol();
  640. }
  641. data.posX = data.startingCol;
  642. data.posY = data.startingRow;
  643. // Find last row and append accordingly
  644. if (!data.createNewSheet && meta.isAppendLines() && appendingToSheet) {
  645. data.posY = data.sheet.getLastRowNum();
  646. if (data.posY > 0) {
  647. data.posY++;
  648. }
  649. }
  650. // offset by configured value
  651. // Find last row and append accordingly
  652. if (!data.createNewSheet && meta.getAppendOffset() != 0 && appendingToSheet) {
  653. data.posY += meta.getAppendOffset();
  654. }
  655. // may have to write a few empty lines
  656. if (!data.createNewSheet && meta.getAppendEmpty() > 0 && appendingToSheet) {
  657. for (int i = 0; i < meta.getAppendEmpty(); i++) {
  658. openLine();
  659. if (!data.shiftExistingCells || meta.isAppendLines()) {
  660. data.posY++;
  661. }
  662. }
  663. }
  664. // may have to write a header here
  665. if (meta.isHeaderEnabled() && !(!data.createNewSheet && meta.isAppendOmitHeader() && appendingToSheet)) {
  666. writeHeader();
  667. }
  668. if (log.isDebug())
  669. logDebug(BaseMessages.getString(PKG, "ExcelWriterStep.Log.FileOpened", buildFilename));
  670. // this is the number of the new output file
  671. data.splitnr++;
  672. }
  673. catch (Exception e) {
  674. logError("Error opening new file", e);
  675. setErrors(1);
  676. throw new KettleException(e);
  677. }
  678. }
  679. private void openLine()
  680. {
  681. if (data.shiftExistingCells) {
  682. data.sheet.shiftRows(data.posY, Math.max(data.posY, data.sheet.getLastRowNum()), 1);
  683. }
  684. }
  685. private void writeHeader() throws KettleException
  686. {
  687. try {
  688. openLine();
  689. Row xlsRow = data.sheet.getRow(data.posY);
  690. if (xlsRow == null) {
  691. xlsRow = data.sheet.createRow(data.posY);
  692. }
  693. int posX = data.posX;
  694. // If we have fields specified: list them in this order!
  695. if (meta.getOutputFields() != null && meta.getOutputFields().length > 0) {
  696. for (int i = 0; i < meta.getOutputFields().length; i++) {
  697. String fieldName = !Const.isEmpty(meta.getOutputFields()[i].getTitle()) ? meta.getOutputFields()[i].getTitle() : meta.getOutputFields()[i].getName();
  698. ValueMetaInterface vMeta = new ValueMeta(fieldName, ValueMetaInterface.TYPE_STRING);
  699. writeField(fieldName, vMeta, meta.getOutputFields()[i], xlsRow, posX++, null, -1, true);
  700. }
  701. // Just put all field names in
  702. }
  703. else if (data.inputRowMeta != null) {
  704. for (int i = 0; i < data.inputRowMeta.size(); i++) {
  705. String fieldName = data.inputRowMeta.getFieldNames()[i];
  706. ValueMetaInterface vMeta = new ValueMeta(fieldName, ValueMetaInterface.TYPE_STRING);
  707. writeField(fieldName, vMeta, null, xlsRow, posX++, null, -1, true);
  708. }
  709. }
  710. data.posY++;
  711. incrementLinesOutput();
  712. }
  713. catch (Exception e) {
  714. throw new KettleException(e);
  715. }
  716. }
  717. /**
  718. * transformation run initialize, may create the output file if specified by user options
  719. *
  720. * @see org.pentaho.di.trans.step.BaseStep#init(org.pentaho.di.trans.step.StepMetaInterface, org.pentaho.di.trans.step.StepDataInterface)
  721. *
  722. */
  723. public boolean init(StepMetaInterface smi, StepDataInterface sdi)
  724. {
  725. meta = (ExcelWriterStepMeta) smi;
  726. data = (ExcelWriterStepData) sdi;
  727. if (super.init(smi, sdi)) {
  728. data.splitnr = 0;
  729. data.datalines = 0;
  730. data.realSheetname = environmentSubstitute(meta.getSheetname());
  731. data.realTemplateSheetName = environmentSubstitute(meta.getTemplateSheetName());
  732. data.realTemplateFileName = environmentSubstitute(meta.getTemplateFileName());
  733. data.realStartingCell = environmentSubstitute(meta.getStartingCell());
  734. data.realPassword = environmentSubstitute(meta.getPassword());
  735. data.realProtectedBy = environmentSubstitute(meta.getProtectedBy());
  736. data.shiftExistingCells = ExcelWriterStepMeta.ROW_WRITE_PUSH_DOWN.equals(meta.getRowWritingMethod());
  737. data.createNewSheet = ExcelWriterStepMeta.IF_SHEET_EXISTS_CREATE_NEW.equals(meta.getIfSheetExists());
  738. data.createNewFile = ExcelWriterStepMeta.IF_FILE_EXISTS_CREATE_NEW.equals(meta.getIfFileExists());
  739. // if we are supposed to init the file up front, here we go
  740. if (!meta.isDoNotOpenNewFileInit()) {
  741. data.firstFileOpened = true;
  742. try {
  743. prepareNextOutputFile();
  744. }
  745. catch (KettleException e) {
  746. e.printStackTrace();
  747. logError("Couldn't prepare output file " + meta.getFileName());
  748. setErrors(1L);
  749. stopAll();
  750. }
  751. }
  752. return true;
  753. }
  754. return false;
  755. }
  756. /**
  757. * transformation run end
  758. *
  759. * @see org.pentaho.di.trans.step.BaseStep#dispose(org.pentaho.di.trans.step.StepMetaInterface, org.pentaho.di.trans.step.StepDataInterface)
  760. *
  761. */
  762. public void dispose(StepMetaInterface smi, StepDataInterface sdi)
  763. {
  764. meta = (ExcelWriterStepMeta) smi;
  765. data = (ExcelWriterStepData) sdi;
  766. clearWorkbookMem();
  767. super.dispose(smi, sdi);
  768. }
  769. }