METAMODEL-1205: Add JDK9+ support for Excel module with POI 4.0.0
authorKasper Sørensen <i.am.kasper.sorensen@gmail.com>
Mon, 26 Nov 2018 16:12:55 +0000 (08:12 -0800)
committerKasper Sørensen <i.am.kasper.sorensen@gmail.com>
Mon, 26 Nov 2018 16:12:55 +0000 (08:12 -0800)
Closes #194

core/src/test/java/org/apache/metamodel/insert/AbstractInsertBuilderTest.java
excel/pom.xml
excel/src/main/java/org/apache/metamodel/excel/DefaultSpreadsheetReaderDelegate.java
excel/src/main/java/org/apache/metamodel/excel/ExcelUpdateCallback.java
excel/src/main/java/org/apache/metamodel/excel/ExcelUtils.java
excel/src/main/java/org/apache/metamodel/excel/XlsxSheetToRowsHandler.java

index cf22c04..ec5b2b1 100644 (file)
@@ -31,62 +31,62 @@ import org.apache.metamodel.util.MutableRef;
 
 public class AbstractInsertBuilderTest extends TestCase {
 
-       public void testInsertValues() throws Exception {
-               final MutableRef<Boolean> executed = new MutableRef<Boolean>(false);
-               final MutableTable table = new MutableTable("foo");
-               table.addColumn(new MutableColumn("foo"));
-               table.addColumn(new MutableColumn("bar"));
-               table.addColumn(new MutableColumn("baz"));
-               RowInsertionBuilder insertBuilder = new AbstractRowInsertionBuilder<UpdateCallback>(
-                               null, table) {
-                       @Override
-                       public void execute() throws MetaModelException {
-                               assertEquals("[1, 2, 3]", Arrays.toString(getValues()));
-                               executed.set(true);
-                       }
-               };
+    public void testInsertValues() throws Exception {
+        final MutableRef<Boolean> executed = new MutableRef<Boolean>(false);
+        final MutableTable table = new MutableTable("foo");
+        table.addColumn(new MutableColumn("foo"));
+        table.addColumn(new MutableColumn("bar"));
+        table.addColumn(new MutableColumn("baz"));
+        RowInsertionBuilder insertBuilder = new AbstractRowInsertionBuilder<UpdateCallback>(null, table) {
+            @Override
+            public void execute() throws MetaModelException {
+                assertEquals("[1, 2, 3]", Arrays.toString(getValues()));
+                executed.set(true);
+            }
+        };
 
-               assertFalse(executed.get().booleanValue());
+        assertFalse(executed.get().booleanValue());
 
-               insertBuilder.value(0, 1).value("bar", 2)
-                               .value(table.getColumnByName("baz"), 3).execute();
+        insertBuilder.value(0, 1).value("bar", 2).value(table.getColumnByName("baz"), 3).execute();
 
-               assertTrue(executed.get());
-               
-               assertEquals("Row[values=[1, 2, 3]]", insertBuilder.toRow().toString());
-               
-       }
+        assertTrue(executed.get());
 
-       public void testIllegalArguments() throws Exception {
-               final MutableTable table = new MutableTable("foo");
-               table.addColumn(new MutableColumn("foo"));
-               RowInsertionBuilder insertBuilder = new AbstractRowInsertionBuilder<UpdateCallback>(
-                               null, table) {
-                       @Override
-                       public void execute() throws MetaModelException {
-                       }
-               };
-               
-               try {
-                       insertBuilder.value((Column)null, "foo");
-                       fail("Exception expected");
-               } catch (IllegalArgumentException e) {
-                       assertEquals("Column cannot be null", e.getMessage());
-               }
+        assertEquals("Row[values=[1, 2, 3]]", insertBuilder.toRow().toString());
 
-               try {
-                       insertBuilder.value("hmm", "foo");
-                       fail("Exception expected");
-               } catch (IllegalArgumentException e) {
-                       assertEquals("No such column in table: hmm, available columns are: [Column[name=foo,columnNumber=0,type=null,nullable=null,nativeType=null,columnSize=null]]", e.getMessage());
-               }
+    }
 
-               try {
-                       insertBuilder.value(4, "foo");
-                       fail("Exception expected");
-               } catch (ArrayIndexOutOfBoundsException e) {
-            assertTrue("4".equals(e.getMessage())
-                    || "Array index out of range: 4".equals(e.getMessage()));
-               }
-       }
+    public void testIllegalArguments() throws Exception {
+        final MutableTable table = new MutableTable("foo");
+        table.addColumn(new MutableColumn("foo"));
+        RowInsertionBuilder insertBuilder = new AbstractRowInsertionBuilder<UpdateCallback>(null, table) {
+            @Override
+            public void execute() throws MetaModelException {
+            }
+        };
+
+        try {
+            insertBuilder.value((Column) null, "foo");
+            fail("Exception expected");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Column cannot be null", e.getMessage());
+        }
+
+        try {
+            insertBuilder.value("hmm", "foo");
+            fail("Exception expected");
+        } catch (IllegalArgumentException e) {
+            assertEquals(
+                    "No such column in table: hmm, available columns are: [Column[name=foo,columnNumber=0,type=null,nullable=null,nativeType=null,columnSize=null]]",
+                    e.getMessage());
+        }
+
+        try {
+            insertBuilder.value(4, "foo");
+            fail("Exception expected");
+        } catch (ArrayIndexOutOfBoundsException e) {
+            final String message = e.getMessage().toLowerCase();
+            assertTrue("Got message: " + message, message.contains("index 4") || "4".equals(message)
+                    || "Array index out of range: 4".equals(message));
+        }
+    }
 }
index 709717a..13ee4f0 100644 (file)
@@ -40,7 +40,7 @@ under the License.
                <dependency>
                        <groupId>org.apache.poi</groupId>
                        <artifactId>poi-ooxml</artifactId>
-                       <version>3.17</version>
+                       <version>4.0.0</version>
                        <exclusions>
                                <exclusion>
                                        <groupId>commons-logging</groupId>
index 17f11ec..97ba50f 100644 (file)
@@ -66,7 +66,7 @@ final class DefaultSpreadsheetReaderDelegate implements SpreadsheetReaderDelegat
     @Override
     public Schema createSchema(String schemaName) {
         final MutableSchema schema = new MutableSchema(schemaName);
-        final Workbook wb = ExcelUtils.readWorkbook(_resource);
+        final Workbook wb = ExcelUtils.readWorkbook(_resource, true);
         try {
             for (int i = 0; i < wb.getNumberOfSheets(); i++) {
                 final Sheet currentSheet = wb.getSheetAt(i);
@@ -83,7 +83,7 @@ final class DefaultSpreadsheetReaderDelegate implements SpreadsheetReaderDelegat
 
     @Override
     public DataSet executeQuery(Table table, List<Column> columns, int maxRows) {
-        final Workbook wb = ExcelUtils.readWorkbook(_resource);
+        final Workbook wb = ExcelUtils.readWorkbook(_resource, true);
         final Sheet sheet = wb.getSheet(table.getName());
 
         if (sheet == null || sheet.getPhysicalNumberOfRows() == 0) {
index f99ec89..f2375c4 100644 (file)
@@ -95,7 +95,7 @@ final class ExcelUpdateCallback extends AbstractUpdateCallback implements Update
             if (_workbook != null) {
                 ExcelUtils.writeAndCloseWorkbook(_dataContext, _workbook);
             }
-            _workbook = ExcelUtils.readWorkbook(_dataContext);
+            _workbook = ExcelUtils.readWorkbookForUpdate(_dataContext);
             if (streamingAllowed && _workbook instanceof XSSFWorkbook) {
                 _workbook = new SXSSFWorkbook((XSSFWorkbook) _workbook);
             }
index c7fe930..2da6ef3 100644 (file)
@@ -19,7 +19,6 @@
 package org.apache.metamodel.excel;
 
 import java.io.File;
-import java.io.OutputStream;
 import java.text.NumberFormat;
 import java.util.Date;
 import java.util.Iterator;
@@ -39,7 +38,6 @@ import org.apache.metamodel.data.Style.SizeUnit;
 import org.apache.metamodel.data.StyleBuilder;
 import org.apache.metamodel.query.SelectItem;
 import org.apache.metamodel.schema.Table;
-import org.apache.metamodel.util.Action;
 import org.apache.metamodel.util.DateUtils;
 import org.apache.metamodel.util.FileHelper;
 import org.apache.metamodel.util.FileResource;
@@ -95,7 +93,15 @@ final class ExcelUtils {
         }
     }
 
-    public static Workbook readWorkbook(Resource resource) {
+    /**
+     * Opens a {@link Workbook} based on a {@link Resource}.
+     * 
+     * @param resource
+     * @param allowFileOptimization whether or not to allow POI to use file handles which supposedly speeds things up,
+     *            but creates issues when writing multiple times to the same file in short bursts of time.
+     * @return
+     */
+    public static Workbook readWorkbook(Resource resource, boolean allowFileOptimization) {
         if (!resource.isExists()) {
             // resource does not exist- create a blank workbook
             if (isXlsxFile(resource)) {
@@ -105,10 +111,11 @@ final class ExcelUtils {
             }
         }
 
-        if (resource instanceof FileResource) {
+        if (allowFileOptimization && resource instanceof FileResource) {
             final File file = ((FileResource) resource).getFile();
             try {
-                return WorkbookFactory.create(file);
+                // open read-only mode
+                return WorkbookFactory.create(file, null, true);
             } catch (Exception e) {
                 logger.error("Could not open workbook", e);
                 throw new IllegalStateException("Could not open workbook", e);
@@ -137,9 +144,9 @@ final class ExcelUtils {
      * 
      * @return a workbook instance based on the ExcelDataContext.
      */
-    public static Workbook readWorkbook(ExcelDataContext dataContext) {
+    public static Workbook readWorkbookForUpdate(ExcelDataContext dataContext) {
         Resource resource = dataContext.getResource();
-        return readWorkbook(resource);
+        return readWorkbook(resource, false);
     }
 
     /**
@@ -156,28 +163,12 @@ final class ExcelUtils {
         final Resource realResource = dataContext.getResource();
         final Resource tempResource = new InMemoryResource(realResource.getQualifiedPath());
 
-        tempResource.write(new Action<OutputStream>() {
-            @Override
-            public void run(OutputStream outputStream) throws Exception {
-                wb.write(outputStream);
-            }
-        });
-
-        if (wb instanceof HSSFWorkbook && realResource instanceof FileResource && realResource.isExists()) {
-            // TODO POI has a problem with closing a file-reference/channel
-            // after wb.write() is invoked. See POI issue to be fixed:
-            // https://bz.apache.org/bugzilla/show_bug.cgi?id=58480
-            System.gc();
-            System.runFinalization();
-            try {
-                Thread.sleep(800);
-            } catch (InterruptedException e) {
-            }
-        }
+        tempResource.write(out -> wb.write(out));
 
         FileHelper.safeClose(wb);
 
         FileHelper.copy(tempResource, realResource);
+
     }
 
     public static String getCellValue(Workbook wb, Cell cell) {
@@ -189,7 +180,7 @@ final class ExcelUtils {
 
         final String result;
 
-        switch (cell.getCellTypeEnum()) {
+        switch (cell.getCellType()) {
         case BLANK:
         case _NONE:
             result = null;
@@ -238,7 +229,7 @@ final class ExcelUtils {
             result = cell.getRichStringCellValue().getString();
             break;
         default:
-            throw new IllegalStateException("Unknown cell type: " + cell.getCellTypeEnum());
+            throw new IllegalStateException("Unknown cell type: " + cell.getCellType());
         }
 
         logger.debug("cell {} resolved to value: {}", cellCoordinate, result);
@@ -296,7 +287,7 @@ final class ExcelUtils {
         }
         final CellStyle cellStyle = cell.getCellStyle();
 
-        final short fontIndex = cellStyle.getFontIndex();
+        final int fontIndex = cellStyle.getFontIndexAsInt();
         final Font font = workbook.getFontAt(fontIndex);
         final StyleBuilder styleBuilder = new StyleBuilder();
 
@@ -312,7 +303,7 @@ final class ExcelUtils {
         }
 
         // Font size
-        final Font stdFont = workbook.getFontAt((short) 0);
+        final Font stdFont = workbook.getFontAt(0);
         final short fontSize = font.getFontHeightInPoints();
         if (stdFont.getFontHeightInPoints() != fontSize) {
             styleBuilder.fontSize(fontSize, SizeUnit.PT);
@@ -344,7 +335,7 @@ final class ExcelUtils {
         }
 
         // Background color
-        if (cellStyle.getFillPatternEnum() == FillPatternType.SOLID_FOREGROUND) {
+        if (cellStyle.getFillPattern() == FillPatternType.SOLID_FOREGROUND) {
             Color color = cellStyle.getFillForegroundColorColor();
             if (color instanceof HSSFColor) {
                 short[] triplet = ((HSSFColor) color).getTriplet();
@@ -363,7 +354,7 @@ final class ExcelUtils {
         }
 
         // alignment
-        switch (cellStyle.getAlignmentEnum()) {
+        switch (cellStyle.getAlignment()) {
         case LEFT:
             styleBuilder.leftAligned();
             break;
index 8b9f6c4..7e4a9c0 100644 (file)
@@ -29,6 +29,7 @@ import org.apache.poi.ss.usermodel.DataFormatter;
 import org.apache.poi.ss.usermodel.DateUtil;
 import org.apache.poi.ss.usermodel.FillPatternType;
 import org.apache.poi.ss.usermodel.FontUnderline;
+import org.apache.poi.ss.usermodel.RichTextString;
 import org.apache.poi.xssf.eventusermodel.XSSFReader;
 import org.apache.poi.xssf.model.SharedStringsTable;
 import org.apache.poi.xssf.model.StylesTable;
@@ -47,15 +48,19 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
 /**
- * XML handler for transforming a sheet into rows. Uses an
- * {@link XlsxRowCallback} to publish identified rows.
+ * XML handler for transforming a sheet into rows. Uses an {@link XlsxRowCallback} to publish identified rows.
  */
 final class XlsxSheetToRowsHandler extends DefaultHandler {
 
     private static final Logger logger = LoggerFactory.getLogger(XlsxSheetToRowsHandler.class);
 
     private static enum XssfDataType {
-        BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER,
+        BOOL,
+        ERROR,
+        FORMULA,
+        INLINESTR,
+        SSTINDEX,
+        NUMBER,
     }
 
     // global variables
@@ -150,12 +155,12 @@ final class XlsxSheetToRowsHandler extends DefaultHandler {
                 _dataType = XssfDataType.FORMULA;
             }
 
-            String cellStyleStr = attributes.getValue("s");
+            final String cellStyleStr = attributes.getValue("s");
             if (cellStyleStr != null) {
                 // It's a number, but almost certainly one
                 // with a special style or format
-                int styleIndex = Integer.parseInt(cellStyleStr);
-                XSSFCellStyle style = _stylesTable.getStyleAt(styleIndex);
+                final int styleIndex = Integer.parseInt(cellStyleStr);
+                final XSSFCellStyle style = _stylesTable.getStyleAt(styleIndex);
 
                 configureStyle(style);
 
@@ -186,8 +191,8 @@ final class XlsxSheetToRowsHandler extends DefaultHandler {
         }
 
         if (style.getFillPatternEnum() == FillPatternType.SOLID_FOREGROUND) {
-            XSSFColor fillForegroundXSSFColor = style.getFillForegroundXSSFColor();
-            String argb = fillForegroundXSSFColor.getARGBHex();
+            final XSSFColor fillForegroundXSSFColor = style.getFillForegroundXSSFColor();
+            final String argb = fillForegroundXSSFColor.getARGBHex();
             if (argb != null) {
                 _style.background(argb.substring(2));
             }
@@ -199,7 +204,7 @@ final class XlsxSheetToRowsHandler extends DefaultHandler {
             _style.fontSize(fontHeight, SizeUnit.PT);
         }
 
-        XSSFColor fontColor = style.getFont().getXSSFColor();
+        final XSSFColor fontColor = style.getFont().getXSSFColor();
         if (fontColor != null) {
             String argbHex = fontColor.getARGBHex();
             if (argbHex != null) {
@@ -265,7 +270,7 @@ final class XlsxSheetToRowsHandler extends DefaultHandler {
         switch (_dataType) {
 
         case BOOL:
-            char first = _value.charAt(0);
+            final char first = _value.charAt(0);
             return first == '0' ? "false" : "true";
         case ERROR:
             logger.warn("Error-cell occurred: {}", _value);
@@ -273,19 +278,19 @@ final class XlsxSheetToRowsHandler extends DefaultHandler {
         case FORMULA:
             return _value.toString();
         case INLINESTR:
-            XSSFRichTextString rtsi = new XSSFRichTextString(_value.toString());
+            final XSSFRichTextString rtsi = new XSSFRichTextString(_value.toString());
             return rtsi.toString();
         case SSTINDEX:
-            String sstIndex = _value.toString();
-            int idx = Integer.parseInt(sstIndex);
-            XSSFRichTextString rtss = new XSSFRichTextString(_sharedStringTable.getEntryAt(idx));
-            return rtss.toString();
+            final String sstIndex = _value.toString();
+            final int idx = Integer.parseInt(sstIndex);
+            final RichTextString item = _sharedStringTable.getItemAt(idx);
+            return item.getString();
         case NUMBER:
             final String numberString = _value.toString();
             if (_formatString != null) {
-                DataFormatter formatter = getDataFormatter();
+                final DataFormatter formatter = getDataFormatter();
                 if (HSSFDateUtil.isADateFormat(_formatIndex, _formatString)) {
-                    Date date = DateUtil.getJavaDate(Double.parseDouble(numberString));
+                    final Date date = DateUtil.getJavaDate(Double.parseDouble(numberString));
                     return DateUtils.createDateFormat().format(date);
                 }
                 return formatter.formatRawCellContents(Double.parseDouble(numberString), _formatIndex, _formatString);