diff --git a/ganttproject-tester/test/biz/ganttproject/impex/csv/CsvImportTest.java b/ganttproject-tester/test/biz/ganttproject/impex/csv/CsvImportTest.java index 0901c59480..dd9a209af8 100644 --- a/ganttproject-tester/test/biz/ganttproject/impex/csv/CsvImportTest.java +++ b/ganttproject-tester/test/biz/ganttproject/impex/csv/CsvImportTest.java @@ -229,7 +229,7 @@ private List>> createPairs(String. private byte[] createXls(String... rows) throws Exception { ByteArrayOutputStream stream = new ByteArrayOutputStream(); - try (SpreadsheetWriter writer = new XlsWriterImpl(stream)) { + try (SpreadsheetWriter writer = new XlsWriterImpl(stream, "test")) { for (String row : rows) { for (String cell : row.split(",", -1)) { writer.print(cell.trim()); diff --git a/ganttproject-tester/test/biz/ganttproject/impex/csv/GPCsvImportTest.java b/ganttproject-tester/test/biz/ganttproject/impex/csv/GPCsvImportTest.java index 9e40f13bce..4028c7e83a 100644 --- a/ganttproject-tester/test/biz/ganttproject/impex/csv/GPCsvImportTest.java +++ b/ganttproject-tester/test/biz/ganttproject/impex/csv/GPCsvImportTest.java @@ -334,7 +334,7 @@ private Map doTestImportAssignments(Supplier supplier private byte[] createXls(String... rows) throws Exception { ByteArrayOutputStream stream = new ByteArrayOutputStream(); - try (SpreadsheetWriter writer = new XlsWriterImpl(stream)) { + try (SpreadsheetWriter writer = new XlsWriterImpl(stream, "test")) { for (String row : rows) { for (String line : row.split("\n", -1)) { for (String cell : line.split(",", -1)) { diff --git a/ganttproject/src/biz/ganttproject/impex/csv/CsvRecordImpl.java b/ganttproject/src/biz/ganttproject/impex/csv/CsvRecordImpl.java index ddeaaccdc2..82002754c5 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/CsvRecordImpl.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/CsvRecordImpl.java @@ -35,7 +35,7 @@ class CsvRecordImpl implements SpreadsheetRecord { @Override public String get(String name) { - return myRecord.get(name); + return (isSet(name)) ? myRecord.get(name) : ""; } @Override @@ -62,4 +62,9 @@ public Iterator iterator() { public int size() { return myRecord.size(); } + + @Override + public String toString() { + return myRecord.toString(); + } } \ No newline at end of file diff --git a/ganttproject/src/biz/ganttproject/impex/csv/CsvWriterImpl.java b/ganttproject/src/biz/ganttproject/impex/csv/CsvWriterImpl.java index 11db3fb220..a8a7b92916 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/CsvWriterImpl.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/CsvWriterImpl.java @@ -26,6 +26,8 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.math.BigDecimal; +import java.util.Calendar; /** * @author akurutin on 04.04.2017. @@ -53,4 +55,35 @@ public void close() throws IOException { myCsvPrinter.flush(); myCsvPrinter.close(); } + + @Override + public void newSheet() throws IOException { + println(); + println(); + } + + @Override + public void newSheet(String name) throws IOException { + newSheet(); + } + + @Override + public void print(Double value) throws IOException { + myCsvPrinter.print(String.valueOf(value)); + } + + @Override + public void print(Integer value) throws IOException { + myCsvPrinter.print(String.valueOf(value)); + } + + @Override + public void print(BigDecimal value) throws IOException { + myCsvPrinter.print(value.toPlainString()); + } + + @Override + public void print(Calendar value) throws IOException { + myCsvPrinter.print(value.toString()); + } } diff --git a/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVExport.java b/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVExport.java index 44164dcb16..9f79281faf 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVExport.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVExport.java @@ -114,15 +114,14 @@ private SpreadsheetWriter getCsvWriter(OutputStream stream) throws IOException { } private SpreadsheetWriter getXlsWriter(OutputStream stream) { - return new XlsWriterImpl(stream); + return new XlsWriterImpl(stream, i18n("tasksList")); } public void save(SpreadsheetWriter writer) throws IOException { writeTasks(writer); if (myHumanResourceManager.getResources().size() > 0) { - writer.println(); - writer.println(); + writer.newSheet(i18n("resourcesList")); writeResources(writer); } } @@ -171,22 +170,22 @@ private void writeTasks(SpreadsheetWriter writer) throws IOException { } else { switch (defaultColumn) { case ID: - writer.print(String.valueOf(task.getTaskID())); + writer.print(task.getTaskID()); break; case NAME: writer.print(getName(task)); break; case BEGIN_DATE: - writer.print(task.getStart().toString()); + writer.print(task.getStart()); break; case END_DATE: - writer.print(task.getDisplayEnd().toString()); + writer.print(task.getDisplayEnd()); break; case DURATION: - writer.print(String.valueOf(task.getDuration().getLength())); + writer.print(task.getDuration().getLength()); break; case COMPLETION: - writer.print(String.valueOf(task.getCompletionPercentage())); + writer.print(task.getCompletionPercentage()); break; case OUTLINE_NUMBER: List outlinePath = task.getManager().getTaskHierarchy().getOutlinePath(task); @@ -203,7 +202,7 @@ private void writeTasks(SpreadsheetWriter writer) throws IOException { writer.print(getAssignments(task)); break; case COST: - writer.print(task.getCost().getValue().toPlainString()); + writer.print(task.getCost().getValue()); break; case INFO: case PRIORITY: @@ -282,13 +281,13 @@ private void writeResources(SpreadsheetWriter writer) throws IOException { writer.print(""); break; case STANDARD_RATE: - writer.print(p.getStandardPayRate().toPlainString()); + writer.print(p.getStandardPayRate()); break; case TOTAL_COST: - writer.print(p.getTotalCost().toPlainString()); + writer.print(p.getTotalCost()); break; case TOTAL_LOAD: - writer.print(String.valueOf(p.getTotalLoad())); + writer.print(p.getTotalLoad()); break; } } diff --git a/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVOpen.java b/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVOpen.java index 01c1ca0aa0..2001c07cf6 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVOpen.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVOpen.java @@ -130,7 +130,7 @@ private int doLoad(SpreadsheetReader reader, int numGroup, int linesToSkip) { numGroup++; } - for (Iterator it = reader.iterator(); it.hasNext(); ) { + for (Iterator it = reader.iterator(); it.hasNext();) { SpreadsheetRecord record = it.next(); lineCounter++; if (linesToSkip-- > 0) { @@ -155,7 +155,7 @@ private int doLoad(SpreadsheetReader reader, int numGroup, int linesToSkip) { } } } - if (currentGroup.doProcess(record)) { + if (currentGroup.process(record)) { searchHeader = false; } else { mySkippedLine++; diff --git a/ganttproject/src/biz/ganttproject/impex/csv/SpreadsheetWriter.java b/ganttproject/src/biz/ganttproject/impex/csv/SpreadsheetWriter.java index b9f4c97c46..c80bf124d4 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/SpreadsheetWriter.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/SpreadsheetWriter.java @@ -20,6 +20,7 @@ package biz.ganttproject.impex.csv; import java.io.IOException; +import java.math.BigDecimal; /** * @author akurutin on 04.04.2017. @@ -27,6 +28,18 @@ public interface SpreadsheetWriter extends AutoCloseable { void print(String value) throws IOException; + void print(Double value) throws IOException; + + void print(Integer value) throws IOException; + + void print(BigDecimal value) throws IOException; + + void print(java.util.Calendar value) throws IOException; + void println() throws IOException; + void newSheet() throws IOException; + + void newSheet(String name) throws IOException; + } diff --git a/ganttproject/src/biz/ganttproject/impex/csv/XlsReaderImpl.java b/ganttproject/src/biz/ganttproject/impex/csv/XlsReaderImpl.java index 62c1b9cdc4..ee6bbb3d56 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/XlsReaderImpl.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/XlsReaderImpl.java @@ -21,9 +21,13 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; + +import net.sourceforge.ganttproject.language.GanttLanguage; + import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import java.io.IOException; @@ -32,6 +36,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.text.SimpleDateFormat; /** * @author torkhov @@ -40,10 +45,12 @@ class XlsReaderImpl implements SpreadsheetReader { private final Workbook myBook; private final Map myHeaders; + private final SimpleDateFormat myDateFormat; XlsReaderImpl(InputStream is, List columnHeaders) throws IOException { myBook = new HSSFWorkbook(is); myHeaders = initializeHeader(columnHeaders); + myDateFormat = GanttLanguage.getInstance().getShortDateFormat(); } @Override @@ -53,11 +60,44 @@ public void close() throws IOException { @Override public Iterator iterator() { - return Iterators.transform(myBook.getSheetAt(0).iterator(), (input) -> new XlsRecordImpl(getCellValues(input), myHeaders)); + Iterator iterator = null; + Iterator it = null; + + // import all sheets into CSV style records with two empty records to separate "tables" + for (int i = 0; i < myBook.getNumberOfSheets(); ++i) { + Sheet sheet = myBook.getSheetAt(i); + if (i < myBook.getNumberOfSheets() - 1) { + int lastRow = sheet.getPhysicalNumberOfRows(); + sheet.createRow(lastRow + 1); + sheet.createRow(lastRow + 2); + } + it = Iterators.transform(sheet.iterator(), (input) -> new XlsRecordImpl(getCellValues(input), myHeaders)); + iterator = (iterator == null) ? it : Iterators.concat(iterator, it); + } + + return iterator; } private List getCellValues(Row row) { - return Lists.newArrayList(Iterables.transform(row, Cell::getStringCellValue)); + return Lists.newArrayList(Iterables.transform(row, (input) -> { + switch (input.getCellTypeEnum()) { + // handle the cases for date, integer and double as used when writing + // this is neccessary to get correct date values and make the parser happy + case NUMERIC: { + if (HSSFDateUtil.isCellDateFormatted(input)) { + return myDateFormat.format(input.getDateCellValue()); + } else { + if (input.getCellStyle().getDataFormat() == (short) 1) { + return String.valueOf((int) input.getNumericCellValue()); + } else { + return String.valueOf(input.getNumericCellValue()); + } + } + } + default: + return input.getStringCellValue(); + } + })); } /** diff --git a/ganttproject/src/biz/ganttproject/impex/csv/XlsRecordImpl.java b/ganttproject/src/biz/ganttproject/impex/csv/XlsRecordImpl.java index b2626300eb..bb8d14d7e7 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/XlsRecordImpl.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/XlsRecordImpl.java @@ -44,7 +44,7 @@ public String get(String name) { if (index == null) { throw new IllegalArgumentException(String.format("Mapping for %s not found, expected one of %s", name, myMapping.keySet())); } - return myValues.get(index); + return (myValues.size() <= index) ? "" : myValues.get(index); } @Override @@ -71,4 +71,17 @@ public Iterator iterator() { public int size() { return myValues.size(); } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (String name : myMapping.keySet()) { + try { + sb.append(name + ": " + get(myMapping.get(name)) + ";"); + } catch (Throwable t) { + sb.append(name + ": ;"); + } + } + return sb.toString(); + } } \ No newline at end of file diff --git a/ganttproject/src/biz/ganttproject/impex/csv/XlsWriterImpl.java b/ganttproject/src/biz/ganttproject/impex/csv/XlsWriterImpl.java index ba636b0557..d85bb6e408 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/XlsWriterImpl.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/XlsWriterImpl.java @@ -20,45 +20,64 @@ package biz.ganttproject.impex.csv; import com.google.common.base.Preconditions; + import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import java.io.IOException; import java.io.OutputStream; +import java.math.BigDecimal; +import java.util.Calendar; /** * @author akurutin on 04.04.2017. */ public class XlsWriterImpl implements SpreadsheetWriter { private final Workbook myWorkbook; - private final Sheet mySheet; private final OutputStream myStream; + private final CellStyle myDateStyle; + private final CellStyle myIntegerStyle; + private final CellStyle myDoubleStyle; - + private Sheet myCurrentSheet = null; private Row myCurrentRow = null; private int myNextRowInd = 0; private int myNextCellInd = 0; - XlsWriterImpl(OutputStream stream) { + XlsWriterImpl(OutputStream stream, String initialSheetName) { myStream = Preconditions.checkNotNull(stream); myWorkbook = new HSSFWorkbook(); - mySheet = myWorkbook.createSheet(); + + myDateStyle = myWorkbook.createCellStyle(); + short fmt = myWorkbook.createDataFormat().getFormat("yyyy-mm-dd"); + myDateStyle.setDataFormat(fmt); + + // https://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/BuiltinFormats.html + myIntegerStyle = myWorkbook.createCellStyle(); + myIntegerStyle.setDataFormat((short) 1); + myDoubleStyle = myWorkbook.createCellStyle(); + myDoubleStyle.setDataFormat((short) 2); + + myCurrentSheet = myWorkbook.createSheet(initialSheetName); } @Override public void print(String value) throws IOException { - if (myCurrentRow == null) { - createNewRow(); + if (value != null) { + addCell().setCellValue(value); } + } - Cell cell = myCurrentRow.createCell(myNextCellInd++); - if (value != null) { - cell.setCellValue(value); + private Cell addCell() throws IOException { + if (myCurrentRow == null) { + createNewRow(); } + return myCurrentRow.createCell(myNextCellInd++); } @Override @@ -75,6 +94,56 @@ public void close() throws IOException { } private void createNewRow() { - myCurrentRow = mySheet.createRow(myNextRowInd++); + myCurrentRow = myCurrentSheet.createRow(myNextRowInd++); + } + + @Override + public void newSheet() throws IOException { + resetForNewSheet(); + myCurrentSheet = myWorkbook.createSheet(); + } + + @Override + public void newSheet(String name) throws IOException { + resetForNewSheet(); + myCurrentSheet = myWorkbook.createSheet(name); + } + + private void resetForNewSheet() { + myNextRowInd = 0; + myCurrentRow = null; + myNextCellInd = 0; + } + + @Override + public void print(Double value) throws IOException { + Cell cell = addCell(); + cell.setCellStyle(myDoubleStyle); + if (value != null) { + cell.setCellValue(value); + } + } + + @Override + public void print(Integer value) throws IOException { + Cell cell = addCell(); + cell.setCellStyle(myIntegerStyle); + if (value != null) { + cell.setCellValue(value); + } + } + + @Override + public void print(BigDecimal value) throws IOException { + print(value.doubleValue()); + } + + @Override + public void print(Calendar value) throws IOException { + Cell cell = addCell(); + cell.setCellStyle(myDateStyle); + if (value != null) { + cell.setCellValue(value); + } } }