From 93cacb312651efa44c12e207fbb8935afc7a958d Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 23 Jan 2024 00:41:33 +0100 Subject: [PATCH 1/4] add new open methods that throw checked exceptions --- .../pjfanning/xlsx/StreamingReader.java | 28 ++- .../xlsx/exceptions/CheckedReadException.java | 26 +++ .../exceptions/ExcelCheckedException.java | 25 +++ .../xlsx/exceptions/ReadException.java | 6 + .../xlsx/impl/StreamingWorkbookReader.java | 166 ++++++++++++------ 5 files changed, 198 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/github/pjfanning/xlsx/exceptions/CheckedReadException.java create mode 100644 src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java diff --git a/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java b/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java index 526c22f6..c95ef886 100644 --- a/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java +++ b/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java @@ -1,5 +1,6 @@ package com.github.pjfanning.xlsx; +import com.github.pjfanning.xlsx.exceptions.CheckedReadException; import com.github.pjfanning.xlsx.exceptions.OpenException; import com.github.pjfanning.xlsx.exceptions.ParseException; import com.github.pjfanning.xlsx.exceptions.ReadException; @@ -545,7 +546,9 @@ public Builder setFullFormatRichText(boolean fullFormatRichText) { * * @param is input stream to read in * @return A {@link Workbook} that can be read from - * @throws com.github.pjfanning.xlsx.exceptions.ReadException if there is an issue reading the stream + * @throws OpenException if there is an issue opening the file + * @throws ReadException if there is an issue reading the file + * @throws ParseException if there is an issue parsing the XML in the file */ public Workbook open(InputStream is) throws OpenException, ReadException, ParseException { StreamingWorkbookReader workbookReader = new StreamingWorkbookReader(this); @@ -559,13 +562,32 @@ public Workbook open(InputStream is) throws OpenException, ReadException, ParseE * * @param file file to read in * @return built streaming reader instance - * @throws com.github.pjfanning.xlsx.exceptions.OpenException if there is an issue opening the file - * @throws com.github.pjfanning.xlsx.exceptions.ReadException if there is an issue reading the file + * @throws OpenException if there is an issue opening the file + * @throws ReadException if there is an issue reading the file + * @throws ParseException if there is an issue parsing the XML in the file */ public Workbook open(File file) throws OpenException, ReadException, ParseException { StreamingWorkbookReader workbookReader = new StreamingWorkbookReader(this); workbookReader.init(file); return new StreamingWorkbook(workbookReader); } + + /** + * Reads a given {@code InputStream} and returns a new + * instance of {@code Workbook}. Due to Apache POI + * limitations, a temporary file must be written in order + * to create a streaming iterator. This process will use + * the same buffer size as specified in {@link #bufferSize(int)}. + * + * @param is input stream to read in + * @return A {@link Workbook} that can be read from + * @throws IOException if an error occurs while opening the file + * @throws CheckedReadException if an error occurs while reading the file + */ + public Workbook openWithCheckedExceptions(InputStream is) throws IOException, CheckedReadException { + StreamingWorkbookReader workbookReader = new StreamingWorkbookReader(this); + workbookReader.initWithCheckedExceptions(is); + return new StreamingWorkbook(workbookReader); + } } } diff --git a/src/main/java/com/github/pjfanning/xlsx/exceptions/CheckedReadException.java b/src/main/java/com/github/pjfanning/xlsx/exceptions/CheckedReadException.java new file mode 100644 index 00000000..2803456d --- /dev/null +++ b/src/main/java/com/github/pjfanning/xlsx/exceptions/CheckedReadException.java @@ -0,0 +1,26 @@ +package com.github.pjfanning.xlsx.exceptions; + +/** + * An exception that is thrown if there is a problem reading the Excel file. + * + * @see ReadException + * @since 4.3.0 + */ +public class CheckedReadException extends ExcelCheckedException { + + public CheckedReadException() { + super(); + } + + public CheckedReadException(String msg) { + super(msg); + } + + public CheckedReadException(Exception e) { + super(e); + } + + public CheckedReadException(String msg, Exception e) { + super(msg, e); + } +} diff --git a/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java b/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java new file mode 100644 index 00000000..3b17d613 --- /dev/null +++ b/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java @@ -0,0 +1,25 @@ +package com.github.pjfanning.xlsx.exceptions; + +/** + * A parent class for all the excel-streaming-reader specific Checked Exceptions (i.e. not Runtime Exceptions). + * + * @since 4.3.0 + */ +public class ExcelCheckedException extends Exception { + + protected ExcelCheckedException() { + super(); + } + + protected ExcelCheckedException(String msg) { + super(msg); + } + + protected ExcelCheckedException(Exception e) { + super(e); + } + + protected ExcelCheckedException(String msg, Exception e) { + super(msg, e); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/pjfanning/xlsx/exceptions/ReadException.java b/src/main/java/com/github/pjfanning/xlsx/exceptions/ReadException.java index 5ff14a72..0c9233cf 100644 --- a/src/main/java/com/github/pjfanning/xlsx/exceptions/ReadException.java +++ b/src/main/java/com/github/pjfanning/xlsx/exceptions/ReadException.java @@ -1,5 +1,11 @@ package com.github.pjfanning.xlsx.exceptions; +/** + * A Runtime Exception that is thrown if there is a problem reading the Excel file. + * This is used in APIs where the method is unable to throw a checked exception. + * + * @see CheckedReadException + */ public class ReadException extends ExcelRuntimeException { public ReadException() { diff --git a/src/main/java/com/github/pjfanning/xlsx/impl/StreamingWorkbookReader.java b/src/main/java/com/github/pjfanning/xlsx/impl/StreamingWorkbookReader.java index 58a363a3..0faaf7dc 100644 --- a/src/main/java/com/github/pjfanning/xlsx/impl/StreamingWorkbookReader.java +++ b/src/main/java/com/github/pjfanning/xlsx/impl/StreamingWorkbookReader.java @@ -4,6 +4,7 @@ import com.github.pjfanning.poi.xssf.streaming.TempFileSharedStringsTable; import com.github.pjfanning.xlsx.SharedStringsImplementationType; import com.github.pjfanning.xlsx.StreamingReader.Builder; +import com.github.pjfanning.xlsx.exceptions.CheckedReadException; import com.github.pjfanning.xlsx.exceptions.ExcelRuntimeException; import com.github.pjfanning.xlsx.exceptions.MissingSheetException; import com.github.pjfanning.xlsx.exceptions.NotSupportedException; @@ -75,6 +76,114 @@ public StreamingWorkbookReader(Builder builder) { * @throws ParseException if an error occurs while parsing the file */ public void init(InputStream is) throws OpenException, ReadException, ParseException { + try { + _init(is); + } catch(SAXException| XMLStreamException e) { + throw new ParseException("Failed to parse stream", e); + } catch(IOException e) { + throw new OpenException("Failed to open stream", e); + } catch(UnsupportedFileFormatException e) { + throw new ReadException("Unsupported File Format (only xlsx files are supported)", e); + } catch(GeneralSecurityException e) { + throw new ReadException("Unable to read workbook - Decryption failed", e); + } catch (ExcelRuntimeException e) { + throw e; + } catch(OpenXML4JException | RuntimeException e) { + throw new ReadException("Unable to read workbook", e); + } + } + + /** + * Initializes the reader with the given input stream. + * @param is the input stream to read from + * @throws IOException if an error occurs while opening the file + * @throws CheckedReadException if an error occurs while reading the file + */ + public void initWithCheckedExceptions(InputStream is) throws IOException, CheckedReadException { + try { + _init(is); + } catch(SAXException | XMLStreamException e) { + throw new CheckedReadException("Failed to parse stream", e); + } catch(IOException e) { + throw e; + } catch(UnsupportedFileFormatException e) { + throw new CheckedReadException("Unsupported File Format (only xlsx files are supported)", e); + } catch(GeneralSecurityException e) { + throw new CheckedReadException("Unable to read workbook - Decryption failed", e); + } catch(OpenXML4JException | RuntimeException e) { + throw new CheckedReadException("Unable to read workbook", e); + } + } + + /** + * Initializes the reader with the given input stream. + * @param f the file to read from + * @throws OpenException if an error occurs while opening the file + * @throws ReadException if an error occurs while reading the file + * @throws ParseException if an error occurs while parsing the file + */ + public void init(File f) throws OpenException, ReadException, ParseException { + try { + _init(f); + } catch(SAXException | XMLStreamException e) { + IOUtils.closeQuietly(pkg); + throw new ParseException("Failed to parse file", e); + } catch(IOException e) { + IOUtils.closeQuietly(pkg); + throw new OpenException("Failed to open file", e); + } catch(UnsupportedFileFormatException e) { + IOUtils.closeQuietly(pkg); + throw new ReadException("Unsupported File Format (only xlsx files are supported)", e); + } catch(OpenXML4JException e) { + IOUtils.closeQuietly(pkg); + throw new ReadException("Unable to read workbook", e); + } catch(GeneralSecurityException e) { + IOUtils.closeQuietly(pkg); + throw new ReadException("Unable to read workbook - Decryption failed", e); + } catch(ExcelRuntimeException e) { + IOUtils.closeQuietly(pkg); + throw e; + } catch(RuntimeException e) { + IOUtils.closeQuietly(pkg); + throw new ReadException("Unable to read workbook", e); + } + } + + /** + * Initializes the reader with the given input stream. + * @param f the file to read from + * @throws IOException if an error occurs while opening the file + * @throws CheckedReadException if an error occurs while reading the file + */ + public void initWithCheckedExceptions(File f) throws IOException, CheckedReadException { + try { + _init(f); + } catch(SAXException | XMLStreamException e) { + IOUtils.closeQuietly(pkg); + throw new CheckedReadException("Failed to parse file", e); + } catch(IOException e) { + IOUtils.closeQuietly(pkg); + throw e; + } catch(UnsupportedFileFormatException e) { + IOUtils.closeQuietly(pkg); + throw new CheckedReadException("Unsupported File Format (only xlsx files are supported)", e); + } catch(OpenXML4JException e) { + IOUtils.closeQuietly(pkg); + throw new CheckedReadException("Unable to read workbook", e); + } catch(GeneralSecurityException e) { + IOUtils.closeQuietly(pkg); + throw new CheckedReadException("Unable to read workbook - Decryption failed", e); + } catch(ExcelRuntimeException e) { + IOUtils.closeQuietly(pkg); + throw new CheckedReadException(e.getMessage(), e); + } catch(RuntimeException e) { + IOUtils.closeQuietly(pkg); + throw new CheckedReadException("Unable to read workbook", e); + } + } + + private void _init(InputStream is) throws OpenXML4JException, XMLStreamException, + GeneralSecurityException, IOException, SAXException { if (builder.avoidTempFiles()) { try { if(builder.getPassword() != null) { @@ -84,18 +193,9 @@ public void init(InputStream is) throws OpenException, ReadException, ParseExcep pkg = OPCPackage.open(is); } loadPackage(pkg); - } catch(SAXException e) { - IOUtils.closeQuietly(pkg); - throw new ParseException("Failed to parse stream", e); - } catch(IOException e) { + } catch(Exception e) { IOUtils.closeQuietly(pkg); - throw new OpenException("Failed to open stream", e); - } catch(GeneralSecurityException e) { - IOUtils.closeQuietly(pkg); - throw new ReadException("Unable to read workbook - Decryption failed", e); - } catch(OpenXML4JException | XMLStreamException | RuntimeException e) { - IOUtils.closeQuietly(pkg); - throw new ReadException("Unable to read workbook", e); + throw e; } } else { File f = null; @@ -104,35 +204,19 @@ public void init(InputStream is) throws OpenException, ReadException, ParseExcep if (log.isDebugEnabled()) { log.debug("Created temp file [{}]", f.getAbsolutePath()); } - init(f); + _init(f); tmp = f; - } catch(OpenException | ReadException e) { + } catch(Exception e) { if (f != null && !f.delete()) { log.debug("failed to delete temp file"); } throw e; - } catch(UnsupportedFileFormatException e) { - if (f != null && !f.delete()) { - log.debug("failed to delete temp file"); - } - throw new ReadException("Unsupported File Format (only xlsx files are supported)", e); - } catch(IOException | RuntimeException e) { - if (f != null && !f.delete()) { - log.debug("failed to delete temp file"); - } - throw new ReadException("Unable to read input stream", e); } } } - /** - * Initializes the reader with the given input stream. - * @param f the file to read from - * @throws OpenException if an error occurs while opening the file - * @throws ReadException if an error occurs while reading the file - * @throws ParseException if an error occurs while parsing the file - */ - public void init(File f) throws OpenException, ReadException, ParseException { + private void _init(File f) throws OpenXML4JException, XMLStreamException, + GeneralSecurityException, IOException, SAXException { try { if(builder.getPassword() != null) { POIFSFileSystem poifs = new POIFSFileSystem(f); @@ -141,27 +225,9 @@ public void init(File f) throws OpenException, ReadException, ParseException { pkg = OPCPackage.open(f); } loadPackage(pkg); - } catch(SAXException e) { - IOUtils.closeQuietly(pkg); - throw new ParseException("Failed to parse file", e); - } catch(IOException e) { - IOUtils.closeQuietly(pkg); - throw new OpenException("Failed to open file", e); - } catch(UnsupportedFileFormatException e) { - IOUtils.closeQuietly(pkg); - throw new ReadException("Unsupported File Format (only xlsx files are supported)", e); - } catch(OpenXML4JException | XMLStreamException e) { - IOUtils.closeQuietly(pkg); - throw new ReadException("Unable to read workbook", e); - } catch(GeneralSecurityException e) { - IOUtils.closeQuietly(pkg); - throw new ReadException("Unable to read workbook - Decryption failed", e); - } catch(ExcelRuntimeException e) { + } catch(Exception e) { IOUtils.closeQuietly(pkg); throw e; - } catch(RuntimeException e) { - IOUtils.closeQuietly(pkg); - throw new ReadException("Unable to read workbook", e); } } From 513eb42af7175a75166a03b13a40b2e0ca7da886 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 23 Jan 2024 00:44:10 +0100 Subject: [PATCH 2/4] tidy --- .../pjfanning/xlsx/exceptions/ExcelCheckedException.java | 2 +- .../github/pjfanning/xlsx/impl/StreamingWorkbookReader.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java b/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java index 3b17d613..5c1ba6e9 100644 --- a/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java +++ b/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java @@ -22,4 +22,4 @@ protected ExcelCheckedException(Exception e) { protected ExcelCheckedException(String msg, Exception e) { super(msg, e); } -} \ No newline at end of file +} diff --git a/src/main/java/com/github/pjfanning/xlsx/impl/StreamingWorkbookReader.java b/src/main/java/com/github/pjfanning/xlsx/impl/StreamingWorkbookReader.java index 0faaf7dc..a9c0bf6d 100644 --- a/src/main/java/com/github/pjfanning/xlsx/impl/StreamingWorkbookReader.java +++ b/src/main/java/com/github/pjfanning/xlsx/impl/StreamingWorkbookReader.java @@ -74,6 +74,7 @@ public StreamingWorkbookReader(Builder builder) { * @throws OpenException if an error occurs while opening the file * @throws ReadException if an error occurs while reading the file * @throws ParseException if an error occurs while parsing the file + * @see #initWithCheckedExceptions(InputStream) */ public void init(InputStream is) throws OpenException, ReadException, ParseException { try { @@ -98,6 +99,7 @@ public void init(InputStream is) throws OpenException, ReadException, ParseExcep * @param is the input stream to read from * @throws IOException if an error occurs while opening the file * @throws CheckedReadException if an error occurs while reading the file + * @since 4.3.0 */ public void initWithCheckedExceptions(InputStream is) throws IOException, CheckedReadException { try { @@ -121,6 +123,8 @@ public void initWithCheckedExceptions(InputStream is) throws IOException, Checke * @throws OpenException if an error occurs while opening the file * @throws ReadException if an error occurs while reading the file * @throws ParseException if an error occurs while parsing the file + * @see #initWithCheckedExceptions(File) + * @since 4.3.0 */ public void init(File f) throws OpenException, ReadException, ParseException { try { @@ -154,6 +158,7 @@ public void init(File f) throws OpenException, ReadException, ParseException { * @param f the file to read from * @throws IOException if an error occurs while opening the file * @throws CheckedReadException if an error occurs while reading the file + * @since 4.3.0 */ public void initWithCheckedExceptions(File f) throws IOException, CheckedReadException { try { From 4f74b126ed678985a12b765554783440a816a972 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 14 May 2024 15:27:07 +0100 Subject: [PATCH 3/4] update javadocs --- .../github/pjfanning/xlsx/StreamingReader.java | 18 ++++++++++++++++++ .../xlsx/exceptions/CheckedReadException.java | 11 +++++++++-- .../xlsx/exceptions/ExcelCheckedException.java | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java b/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java index c95ef886..44eec9a7 100644 --- a/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java +++ b/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java @@ -589,5 +589,23 @@ public Workbook openWithCheckedExceptions(InputStream is) throws IOException, Ch workbookReader.initWithCheckedExceptions(is); return new StreamingWorkbook(workbookReader); } + + /** + * Reads a given {@code InputStream} and returns a new + * instance of {@code Workbook}. Due to Apache POI + * limitations, a temporary file must be written in order + * to create a streaming iterator. This process will use + * the same buffer size as specified in {@link #bufferSize(int)}. + * + * @param @param file file to read in + * @return A {@link Workbook} that can be read from + * @throws IOException if an error occurs while opening the file + * @throws CheckedReadException if an error occurs while reading the file + */ + public Workbook openWithCheckedExceptions(File file) throws IOException, CheckedReadException { + StreamingWorkbookReader workbookReader = new StreamingWorkbookReader(this); + workbookReader.initWithCheckedExceptions(file); + return new StreamingWorkbook(workbookReader); + } } } diff --git a/src/main/java/com/github/pjfanning/xlsx/exceptions/CheckedReadException.java b/src/main/java/com/github/pjfanning/xlsx/exceptions/CheckedReadException.java index 2803456d..80af8abe 100644 --- a/src/main/java/com/github/pjfanning/xlsx/exceptions/CheckedReadException.java +++ b/src/main/java/com/github/pjfanning/xlsx/exceptions/CheckedReadException.java @@ -1,10 +1,17 @@ package com.github.pjfanning.xlsx.exceptions; /** - * An exception that is thrown if there is a problem reading the Excel file. + * A checked exception that is thrown if there is a problem reading the Excel file. + * + *

+ * To avoid adding a large number of new checked exceptions to the method signatures, + * this exception is generic. Any read issue will throw this exception. You can look call + * {@link #getCause()} to get the underlying exception will is likely to be one of the more specific + * legacy exceptions that implement {@link com.github.pjfanning.xlsx.exceptions.ExcelRuntimeException}. + *

* * @see ReadException - * @since 4.3.0 + * @since 4.4.0 */ public class CheckedReadException extends ExcelCheckedException { diff --git a/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java b/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java index 5c1ba6e9..bd0c33a5 100644 --- a/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java +++ b/src/main/java/com/github/pjfanning/xlsx/exceptions/ExcelCheckedException.java @@ -3,7 +3,7 @@ /** * A parent class for all the excel-streaming-reader specific Checked Exceptions (i.e. not Runtime Exceptions). * - * @since 4.3.0 + * @since 4.4.0 */ public class ExcelCheckedException extends Exception { From af3fa3eef7d2e8261ac51ccf612a02381044032d Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 14 May 2024 15:32:22 +0100 Subject: [PATCH 4/4] Update StreamingReader.java --- src/main/java/com/github/pjfanning/xlsx/StreamingReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java b/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java index 44eec9a7..63443395 100644 --- a/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java +++ b/src/main/java/com/github/pjfanning/xlsx/StreamingReader.java @@ -597,7 +597,7 @@ public Workbook openWithCheckedExceptions(InputStream is) throws IOException, Ch * to create a streaming iterator. This process will use * the same buffer size as specified in {@link #bufferSize(int)}. * - * @param @param file file to read in + * @param file file to read in * @return A {@link Workbook} that can be read from * @throws IOException if an error occurs while opening the file * @throws CheckedReadException if an error occurs while reading the file