mirror of
https://github.com/enso-org/enso.git
synced 2025-01-05 14:11:36 +03:00
Do not report exceptions on long running Excel reads (#11916)
* Do not report exceptions on long running Excel reads This change introduces two modifications: - `ClosedByInterruptException` is wrapped in `InterruptedException` instead of `RuntimeException` - when instrumentation encounters `InterruptedException` it bails early Having `ClosedByInterruptException` wrapped in `RuntimeException` meant that it is being reported as a regular `HostException` in the engine and to the user. Instead it should be treated specially since we know that it is caused by cancelling a long-running job. Since it is a statically checked exception it has to be declared and the information has to be propagated through various lambda constructs (thanks Java!). The above change alone meant that an error is not reported for `Data.read` nodes but any values dependent on it would still report `No_Such_Method` error when the exception is reported as a value. Hence the early bail out mechanism. * Send `PendingInterrupted` on interrupt The information could be used in GUI to indicate pending execution that will take tad longer. * Prettify * Test `PendingInterrupted` payload * Add `wasInterrupted` flag to `Pending` Reduce `PendingInterrupted` to a flag in `Pending` * fmt
This commit is contained in:
parent
e5a1c5a6fa
commit
d87484b9b2
@ -392,7 +392,7 @@ interface ExpressionUpdate {
|
|||||||
An information about the computed value.
|
An information about the computed value.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
type ExpressionUpdatePayload = Value | DatafalowError | Panic | Pending;
|
type ExpressionUpdatePayload = Value | DataflowError | Panic | Pending;
|
||||||
|
|
||||||
/** Indicates that the expression was computed to a value. */
|
/** Indicates that the expression was computed to a value. */
|
||||||
interface Value {
|
interface Value {
|
||||||
@ -424,6 +424,8 @@ interface Pending {
|
|||||||
/** Optional amount of already done work as a number between `0.0` to `1.0`.
|
/** Optional amount of already done work as a number between `0.0` to `1.0`.
|
||||||
*/
|
*/
|
||||||
progress?: number;
|
progress?: number;
|
||||||
|
/** Indicates whether the computation of the expression has been interrupted and will be retried. */
|
||||||
|
wasInterrupted: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Information about warnings associated with the value. */
|
/** Information about warnings associated with the value. */
|
||||||
|
@ -230,8 +230,8 @@ final class ContextEventsListener(
|
|||||||
functionSchema.map(toProtocolFunctionSchema)
|
functionSchema.map(toProtocolFunctionSchema)
|
||||||
)
|
)
|
||||||
|
|
||||||
case Api.ExpressionUpdate.Payload.Pending(m, p) =>
|
case Api.ExpressionUpdate.Payload.Pending(m, p, i) =>
|
||||||
ContextRegistryProtocol.ExpressionUpdate.Payload.Pending(m, p)
|
ContextRegistryProtocol.ExpressionUpdate.Payload.Pending(m, p, i)
|
||||||
|
|
||||||
case Api.ExpressionUpdate.Payload.DataflowError(trace) =>
|
case Api.ExpressionUpdate.Payload.DataflowError(trace) =>
|
||||||
ContextRegistryProtocol.ExpressionUpdate.Payload.DataflowError(trace)
|
ContextRegistryProtocol.ExpressionUpdate.Payload.DataflowError(trace)
|
||||||
|
@ -231,8 +231,17 @@ object ContextRegistryProtocol {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Pending(message: Option[String], progress: Option[Double])
|
/** Indicates that an expression is pending a computation
|
||||||
extends Payload
|
*/
|
||||||
|
case class Pending(
|
||||||
|
message: Option[String],
|
||||||
|
progress: Option[Double],
|
||||||
|
wasInterrupted: Boolean
|
||||||
|
) extends Payload
|
||||||
|
|
||||||
|
/** Indicates that an expression's computation has been interrupted and shall be retried.
|
||||||
|
*/
|
||||||
|
case object PendingInterrupted extends Payload
|
||||||
|
|
||||||
/** Indicates that the expression was computed to an error.
|
/** Indicates that the expression was computed to an error.
|
||||||
*
|
*
|
||||||
@ -258,6 +267,8 @@ object ContextRegistryProtocol {
|
|||||||
|
|
||||||
val Pending = "Pending"
|
val Pending = "Pending"
|
||||||
|
|
||||||
|
val PendingInterrupted = "PendingInterrupted"
|
||||||
|
|
||||||
val DataflowError = "DataflowError"
|
val DataflowError = "DataflowError"
|
||||||
|
|
||||||
val Panic = "Panic"
|
val Panic = "Panic"
|
||||||
@ -291,6 +302,14 @@ object ContextRegistryProtocol {
|
|||||||
.deepMerge(
|
.deepMerge(
|
||||||
Json.obj(CodecField.Type -> PayloadType.Pending.asJson)
|
Json.obj(CodecField.Type -> PayloadType.Pending.asJson)
|
||||||
)
|
)
|
||||||
|
case m: Payload.PendingInterrupted.type =>
|
||||||
|
Encoder[Payload.PendingInterrupted.type]
|
||||||
|
.apply(m)
|
||||||
|
.deepMerge(
|
||||||
|
Json.obj(
|
||||||
|
CodecField.Type -> PayloadType.PendingInterrupted.asJson
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val decoder: Decoder[Payload] =
|
implicit val decoder: Decoder[Payload] =
|
||||||
@ -307,6 +326,9 @@ object ContextRegistryProtocol {
|
|||||||
|
|
||||||
case PayloadType.Pending =>
|
case PayloadType.Pending =>
|
||||||
Decoder[Payload.Pending].tryDecode(cursor)
|
Decoder[Payload.Pending].tryDecode(cursor)
|
||||||
|
|
||||||
|
case PayloadType.PendingInterrupted =>
|
||||||
|
Decoder[Payload.PendingInterrupted.type].tryDecode(cursor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,11 +158,14 @@ object Runtime {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TBD
|
/** Indicates that an expression is pending a computation
|
||||||
*/
|
*/
|
||||||
@named("expressionUpdatePayloadPending")
|
@named("expressionUpdatePayloadPending")
|
||||||
case class Pending(message: Option[String], progress: Option[Double])
|
case class Pending(
|
||||||
extends Payload;
|
message: Option[String],
|
||||||
|
progress: Option[Double],
|
||||||
|
wasInterrupted: Boolean = false
|
||||||
|
) extends Payload
|
||||||
|
|
||||||
/** Indicates that the expression was computed to an error.
|
/** Indicates that the expression was computed to an error.
|
||||||
*
|
*
|
||||||
|
@ -95,6 +95,21 @@ object ProgramExecutionSupport {
|
|||||||
val onComputedValueCallback: Consumer[ExpressionValue] = { value =>
|
val onComputedValueCallback: Consumer[ExpressionValue] = { value =>
|
||||||
if (callStack.isEmpty) {
|
if (callStack.isEmpty) {
|
||||||
logger.log(Level.FINEST, s"ON_COMPUTED ${value.getExpressionId}")
|
logger.log(Level.FINEST, s"ON_COMPUTED ${value.getExpressionId}")
|
||||||
|
|
||||||
|
if (VisualizationResult.isInterruptedException(value.getValue)) {
|
||||||
|
value.getValue match {
|
||||||
|
case e: AbstractTruffleException =>
|
||||||
|
sendInterruptedExpressionUpdate(
|
||||||
|
contextId,
|
||||||
|
executionFrame.syncState,
|
||||||
|
value
|
||||||
|
)
|
||||||
|
// Bail out early. Any references to this value that do not expect
|
||||||
|
// Interrupted error will likely return `No_Such_Method` otherwise.
|
||||||
|
throw new ThreadInterruptedException(e);
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
}
|
||||||
sendExpressionUpdate(contextId, executionFrame.syncState, value)
|
sendExpressionUpdate(contextId, executionFrame.syncState, value)
|
||||||
sendVisualizationUpdates(
|
sendVisualizationUpdates(
|
||||||
contextId,
|
contextId,
|
||||||
@ -377,6 +392,50 @@ object ProgramExecutionSupport {
|
|||||||
Api.ExecutionResult.Failure(ex.getMessage, None)
|
Api.ExecutionResult.Failure(ex.getMessage, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def sendInterruptedExpressionUpdate(
|
||||||
|
contextId: ContextId,
|
||||||
|
syncState: UpdatesSynchronizationState,
|
||||||
|
value: ExpressionValue
|
||||||
|
)(implicit ctx: RuntimeContext): Unit = {
|
||||||
|
val expressionId = value.getExpressionId
|
||||||
|
val methodCall = toMethodCall(value)
|
||||||
|
if (
|
||||||
|
!syncState.isExpressionSync(expressionId) ||
|
||||||
|
(methodCall.isDefined && !syncState.isMethodPointerSync(
|
||||||
|
expressionId
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
val payload =
|
||||||
|
Api.ExpressionUpdate.Payload.Pending(None, None, wasInterrupted = true)
|
||||||
|
ctx.endpoint.sendToClient(
|
||||||
|
Api.Response(
|
||||||
|
Api.ExpressionUpdates(
|
||||||
|
contextId,
|
||||||
|
Set(
|
||||||
|
Api.ExpressionUpdate(
|
||||||
|
value.getExpressionId,
|
||||||
|
Option(value.getTypes).map(_.toVector),
|
||||||
|
methodCall,
|
||||||
|
value.getProfilingInfo.map { case e: ExecutionTime =>
|
||||||
|
Api.ProfilingInfo.ExecutionTime(e.getNanoTimeElapsed)
|
||||||
|
}.toVector,
|
||||||
|
value.wasCached(),
|
||||||
|
value.isTypeChanged || value.isFunctionCallChanged,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
syncState.setExpressionSync(expressionId)
|
||||||
|
ctx.state.expressionExecutionState.setExpressionExecuted(expressionId)
|
||||||
|
if (methodCall.isDefined) {
|
||||||
|
syncState.setMethodPointerSync(expressionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def sendExpressionUpdate(
|
private def sendExpressionUpdate(
|
||||||
contextId: ContextId,
|
contextId: ContextId,
|
||||||
syncState: UpdatesSynchronizationState,
|
syncState: UpdatesSynchronizationState,
|
||||||
|
@ -500,16 +500,15 @@ class RuntimeAsyncCommandsTest
|
|||||||
|
|
||||||
responses should contain theSameElementsAs Seq(
|
responses should contain theSameElementsAs Seq(
|
||||||
Api.Response(requestId, Api.RecomputeContextResponse(contextId)),
|
Api.Response(requestId, Api.RecomputeContextResponse(contextId)),
|
||||||
TestMessages.update(
|
TestMessages.pendingInterrupted(
|
||||||
contextId,
|
contextId,
|
||||||
vId,
|
|
||||||
ConstantsGen.INTEGER,
|
|
||||||
methodCall = Some(
|
methodCall = Some(
|
||||||
MethodCall(
|
MethodCall(
|
||||||
MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "loop"),
|
MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "loop"),
|
||||||
Vector(1)
|
Vector(1)
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
vId
|
||||||
),
|
),
|
||||||
context.executionComplete(contextId)
|
context.executionComplete(contextId)
|
||||||
)
|
)
|
||||||
|
@ -479,4 +479,33 @@ object TestMessages {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** Create an pending interrupted response.
|
||||||
|
*
|
||||||
|
* @param contextId an identifier of the context
|
||||||
|
* @param expressionIds a list of pending expressions
|
||||||
|
* @return the expression update response
|
||||||
|
*/
|
||||||
|
def pendingInterrupted(
|
||||||
|
contextId: UUID,
|
||||||
|
methodCall: Option[Api.MethodCall],
|
||||||
|
expressionIds: UUID*
|
||||||
|
): Api.Response =
|
||||||
|
Api.Response(
|
||||||
|
Api.ExpressionUpdates(
|
||||||
|
contextId,
|
||||||
|
expressionIds.toSet.map { expressionId =>
|
||||||
|
Api.ExpressionUpdate(
|
||||||
|
expressionId,
|
||||||
|
None,
|
||||||
|
methodCall,
|
||||||
|
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
Api.ExpressionUpdate.Payload
|
||||||
|
.Pending(None, None, wasInterrupted = true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
package org.enso.interpreter.runtime.control;
|
package org.enso.interpreter.runtime.control;
|
||||||
|
|
||||||
/** Thrown when guest code discovers a thread interrupt. */
|
/** Thrown when guest code discovers a thread interrupt. */
|
||||||
public class ThreadInterruptedException extends RuntimeException {}
|
public class ThreadInterruptedException extends RuntimeException {
|
||||||
|
public ThreadInterruptedException() {}
|
||||||
|
|
||||||
|
public ThreadInterruptedException(Throwable e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,6 +22,7 @@ import org.apache.poi.ss.usermodel.Workbook;
|
|||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
import org.enso.base.cache.ReloadDetector;
|
import org.enso.base.cache.ReloadDetector;
|
||||||
import org.enso.table.excel.xssfreader.XSSFReaderWorkbook;
|
import org.enso.table.excel.xssfreader.XSSFReaderWorkbook;
|
||||||
|
import org.enso.table.util.FunctionWithException;
|
||||||
|
|
||||||
public class ExcelConnectionPool {
|
public class ExcelConnectionPool {
|
||||||
public static final ExcelConnectionPool INSTANCE = new ExcelConnectionPool();
|
public static final ExcelConnectionPool INSTANCE = new ExcelConnectionPool();
|
||||||
@ -29,7 +30,7 @@ public class ExcelConnectionPool {
|
|||||||
private ExcelConnectionPool() {}
|
private ExcelConnectionPool() {}
|
||||||
|
|
||||||
public ReadOnlyExcelConnection openReadOnlyConnection(File file, ExcelFileFormat format)
|
public ReadOnlyExcelConnection openReadOnlyConnection(File file, ExcelFileFormat format)
|
||||||
throws IOException {
|
throws IOException, InterruptedException {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (isCurrentlyWriting) {
|
if (isCurrentlyWriting) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
@ -134,7 +135,7 @@ public class ExcelConnectionPool {
|
|||||||
*/
|
*/
|
||||||
public <R> R lockForWriting(
|
public <R> R lockForWriting(
|
||||||
File file, ExcelFileFormat format, File[] accompanyingFiles, Function<WriteHelper, R> action)
|
File file, ExcelFileFormat format, File[] accompanyingFiles, Function<WriteHelper, R> action)
|
||||||
throws IOException {
|
throws IOException, InterruptedException {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (isCurrentlyWriting) {
|
if (isCurrentlyWriting) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
@ -242,7 +243,8 @@ public class ExcelConnectionPool {
|
|||||||
private ExcelWorkbook workbook;
|
private ExcelWorkbook workbook;
|
||||||
private IOException initializationException = null;
|
private IOException initializationException = null;
|
||||||
|
|
||||||
<T> T withWorkbook(Function<ExcelWorkbook, T> action) throws IOException {
|
<T> T withWorkbook(FunctionWithException<ExcelWorkbook, T, InterruptedException> action)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return action.apply(accessCurrentWorkbook());
|
return action.apply(accessCurrentWorkbook());
|
||||||
}
|
}
|
||||||
@ -258,7 +260,7 @@ public class ExcelConnectionPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reopen(boolean throwOnFailure) throws IOException {
|
void reopen(boolean throwOnFailure) throws IOException, InterruptedException {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (workbook != null) {
|
if (workbook != null) {
|
||||||
throw new IllegalStateException("The workbook is already open.");
|
throw new IllegalStateException("The workbook is already open.");
|
||||||
|
@ -181,7 +181,8 @@ public class ExcelRange {
|
|||||||
* @param sheet ExcelSheet containing the range refers to.
|
* @param sheet ExcelSheet containing the range refers to.
|
||||||
* @return Expanded range covering the connected table of cells.
|
* @return Expanded range covering the connected table of cells.
|
||||||
*/
|
*/
|
||||||
public static ExcelRange expandSingleCell(ExcelRange excelRange, ExcelSheet sheet) {
|
public static ExcelRange expandSingleCell(ExcelRange excelRange, ExcelSheet sheet)
|
||||||
|
throws InterruptedException {
|
||||||
ExcelRow currentRow = sheet.get(excelRange.getTopRow());
|
ExcelRow currentRow = sheet.get(excelRange.getTopRow());
|
||||||
if (currentRow == null || currentRow.isEmpty(excelRange.getLeftColumn())) {
|
if (currentRow == null || currentRow.isEmpty(excelRange.getLeftColumn())) {
|
||||||
return new ExcelRange(
|
return new ExcelRange(
|
||||||
@ -337,7 +338,7 @@ public class ExcelRange {
|
|||||||
return isWholeColumn() ? Integer.MAX_VALUE : bottomRow - topRow + 1;
|
return isWholeColumn() ? Integer.MAX_VALUE : bottomRow - topRow + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLastNonEmptyRow(ExcelSheet sheet) {
|
public int getLastNonEmptyRow(ExcelSheet sheet) throws InterruptedException {
|
||||||
int lastRow =
|
int lastRow =
|
||||||
Math.min(sheet.getLastRow(), isWholeColumn() ? sheet.getLastRow() : bottomRow) + 1;
|
Math.min(sheet.getLastRow(), isWholeColumn() ? sheet.getLastRow() : bottomRow) + 1;
|
||||||
|
|
||||||
|
@ -12,10 +12,10 @@ public interface ExcelSheet {
|
|||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
/** Gets the initial row index within the sheet (1-based). */
|
/** Gets the initial row index within the sheet (1-based). */
|
||||||
int getFirstRow();
|
int getFirstRow() throws InterruptedException;
|
||||||
|
|
||||||
/** Gets the final row index within the sheet (1-based). */
|
/** Gets the final row index within the sheet (1-based). */
|
||||||
int getLastRow();
|
int getLastRow() throws InterruptedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the row at the given index within the sheet (1-based)
|
* Gets the row at the given index within the sheet (1-based)
|
||||||
@ -23,7 +23,7 @@ public interface ExcelSheet {
|
|||||||
* @param row the row index (1-based)/
|
* @param row the row index (1-based)/
|
||||||
* @return the row object or null if the row index is out of range or doesn't exist.
|
* @return the row object or null if the row index is out of range or doesn't exist.
|
||||||
*/
|
*/
|
||||||
ExcelRow get(int row);
|
ExcelRow get(int row) throws InterruptedException;
|
||||||
|
|
||||||
/** Gets the underlying Apache POI Sheet object - may be null. Provided for Writer use only. */
|
/** Gets the underlying Apache POI Sheet object - may be null. Provided for Writer use only. */
|
||||||
Sheet getSheet();
|
Sheet getSheet();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.enso.table.excel;
|
package org.enso.table.excel;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.function.Function;
|
import org.enso.table.util.FunctionWithException;
|
||||||
|
|
||||||
public class ReadOnlyExcelConnection implements AutoCloseable {
|
public class ReadOnlyExcelConnection implements AutoCloseable {
|
||||||
|
|
||||||
@ -27,7 +27,9 @@ public class ReadOnlyExcelConnection implements AutoCloseable {
|
|||||||
record = null;
|
record = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized <T> T withWorkbook(Function<ExcelWorkbook, T> f) throws IOException {
|
public synchronized <T> T withWorkbook(
|
||||||
|
FunctionWithException<ExcelWorkbook, T, InterruptedException> f)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
throw new IllegalStateException("ReadOnlyExcelConnection is being used after it was closed.");
|
throw new IllegalStateException("ReadOnlyExcelConnection is being used after it was closed.");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.enso.table.excel.xssfreader;
|
package org.enso.table.excel.xssfreader;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.ClosedByInterruptException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
@ -33,7 +34,7 @@ public class XSSFReaderSheet implements ExcelSheet {
|
|||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void ensureReadSheetData() {
|
private synchronized void ensureReadSheetData() throws InterruptedException {
|
||||||
if (hasReadSheetData) {
|
if (hasReadSheetData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -70,6 +71,8 @@ public class XSSFReaderSheet implements ExcelSheet {
|
|||||||
try {
|
try {
|
||||||
var sheet = reader.getSheet(relId);
|
var sheet = reader.getSheet(relId);
|
||||||
xmlReader.parse(new InputSource(sheet));
|
xmlReader.parse(new InputSource(sheet));
|
||||||
|
} catch (ClosedByInterruptException e) {
|
||||||
|
throw new InterruptedException(e.getMessage());
|
||||||
} catch (SAXException | InvalidFormatException | IOException e) {
|
} catch (SAXException | InvalidFormatException | IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@ -94,25 +97,25 @@ public class XSSFReaderSheet implements ExcelSheet {
|
|||||||
return sheetName;
|
return sheetName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDimensions() {
|
public String getDimensions() throws InterruptedException {
|
||||||
ensureReadSheetData();
|
ensureReadSheetData();
|
||||||
return dimensions;
|
return dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFirstRow() {
|
public int getFirstRow() throws InterruptedException {
|
||||||
ensureReadSheetData();
|
ensureReadSheetData();
|
||||||
return firstRow;
|
return firstRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getLastRow() {
|
public int getLastRow() throws InterruptedException {
|
||||||
ensureReadSheetData();
|
ensureReadSheetData();
|
||||||
return lastRow;
|
return lastRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExcelRow get(int row) {
|
public ExcelRow get(int row) throws InterruptedException {
|
||||||
ensureReadSheetData();
|
ensureReadSheetData();
|
||||||
|
|
||||||
if (!rowData.containsKey(row)) {
|
if (!rowData.containsKey(row)) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.enso.table.excel.xssfreader;
|
package org.enso.table.excel.xssfreader;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.ClosedByInterruptException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -8,7 +9,6 @@ import java.util.HashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import javax.xml.XMLConstants;
|
import javax.xml.XMLConstants;
|
||||||
import javax.xml.namespace.NamespaceContext;
|
import javax.xml.namespace.NamespaceContext;
|
||||||
import javax.xml.xpath.XPathConstants;
|
import javax.xml.xpath.XPathConstants;
|
||||||
@ -26,6 +26,7 @@ import org.apache.poi.xssf.model.SharedStrings;
|
|||||||
import org.apache.poi.xssf.usermodel.XSSFRelation;
|
import org.apache.poi.xssf.usermodel.XSSFRelation;
|
||||||
import org.enso.table.excel.ExcelSheet;
|
import org.enso.table.excel.ExcelSheet;
|
||||||
import org.enso.table.excel.ExcelWorkbook;
|
import org.enso.table.excel.ExcelWorkbook;
|
||||||
|
import org.enso.table.util.ConsumerWithException;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
@ -90,7 +91,7 @@ public class XSSFReaderWorkbook implements ExcelWorkbook {
|
|||||||
private SharedStrings sharedStrings;
|
private SharedStrings sharedStrings;
|
||||||
private XSSFReaderFormats styles;
|
private XSSFReaderFormats styles;
|
||||||
|
|
||||||
public XSSFReaderWorkbook(String path) throws IOException {
|
public XSSFReaderWorkbook(String path) throws IOException, InterruptedException {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
|
|
||||||
// Read the workbook data
|
// Read the workbook data
|
||||||
@ -101,7 +102,8 @@ public class XSSFReaderWorkbook implements ExcelWorkbook {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void withReader(Consumer<XSSFReader> action) throws IOException {
|
void withReader(ConsumerWithException<XSSFReader, InterruptedException> action)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
try (var pkg = OPCPackage.open(path, PackageAccess.READ)) {
|
try (var pkg = OPCPackage.open(path, PackageAccess.READ)) {
|
||||||
var reader = new XSSFReader(pkg);
|
var reader = new XSSFReader(pkg);
|
||||||
action.accept(reader);
|
action.accept(reader);
|
||||||
@ -115,7 +117,7 @@ public class XSSFReaderWorkbook implements ExcelWorkbook {
|
|||||||
|
|
||||||
private record NamedRange(String name, String formula) {}
|
private record NamedRange(String name, String formula) {}
|
||||||
|
|
||||||
private void readWorkbookData() throws IOException {
|
private void readWorkbookData() throws IOException, InterruptedException {
|
||||||
withReader(
|
withReader(
|
||||||
reader -> {
|
reader -> {
|
||||||
try {
|
try {
|
||||||
@ -124,6 +126,8 @@ public class XSSFReaderWorkbook implements ExcelWorkbook {
|
|||||||
read1904DateSetting(workbookDoc);
|
read1904DateSetting(workbookDoc);
|
||||||
readSheetInfo(workbookDoc);
|
readSheetInfo(workbookDoc);
|
||||||
readNamedRanges(workbookDoc);
|
readNamedRanges(workbookDoc);
|
||||||
|
} catch (ClosedByInterruptException e) {
|
||||||
|
throw new InterruptedException(e.getMessage());
|
||||||
} catch (SAXException
|
} catch (SAXException
|
||||||
| IOException
|
| IOException
|
||||||
| InvalidFormatException
|
| InvalidFormatException
|
||||||
@ -171,7 +175,7 @@ public class XSSFReaderWorkbook implements ExcelWorkbook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void ensureReadShared() {
|
private synchronized void ensureReadShared() throws InterruptedException {
|
||||||
if (hasReadShared) {
|
if (hasReadShared) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -207,6 +211,8 @@ public class XSSFReaderWorkbook implements ExcelWorkbook {
|
|||||||
styles = new XSSFReaderFormats(stylesTable);
|
styles = new XSSFReaderFormats(stylesTable);
|
||||||
|
|
||||||
hasReadShared = true;
|
hasReadShared = true;
|
||||||
|
} catch (ClosedByInterruptException e) {
|
||||||
|
throw new InterruptedException(e.getMessage());
|
||||||
} catch (InvalidFormatException | IOException e) {
|
} catch (InvalidFormatException | IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@ -258,12 +264,12 @@ public class XSSFReaderWorkbook implements ExcelWorkbook {
|
|||||||
return namedRange == null ? null : namedRange.formula;
|
return namedRange == null ? null : namedRange.formula;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SharedStrings getSharedStrings() {
|
public SharedStrings getSharedStrings() throws InterruptedException {
|
||||||
ensureReadShared();
|
ensureReadShared();
|
||||||
return sharedStrings;
|
return sharedStrings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public XSSFReaderFormats getStyles() {
|
public XSSFReaderFormats getStyles() throws InterruptedException {
|
||||||
ensureReadShared();
|
ensureReadShared();
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import org.apache.poi.ss.util.CellReference;
|
import org.apache.poi.ss.util.CellReference;
|
||||||
@ -24,6 +23,7 @@ import org.enso.table.excel.ExcelSheet;
|
|||||||
import org.enso.table.excel.ExcelWorkbook;
|
import org.enso.table.excel.ExcelWorkbook;
|
||||||
import org.enso.table.excel.ReadOnlyExcelConnection;
|
import org.enso.table.excel.ReadOnlyExcelConnection;
|
||||||
import org.enso.table.problems.ProblemAggregator;
|
import org.enso.table.problems.ProblemAggregator;
|
||||||
|
import org.enso.table.util.FunctionWithException;
|
||||||
import org.graalvm.polyglot.Context;
|
import org.graalvm.polyglot.Context;
|
||||||
|
|
||||||
/** A table reader for MS Excel files. */
|
/** A table reader for MS Excel files. */
|
||||||
@ -36,7 +36,8 @@ public class ExcelReader {
|
|||||||
* @return a String[] containing the sheet names.
|
* @return a String[] containing the sheet names.
|
||||||
* @throws IOException when the action fails
|
* @throws IOException when the action fails
|
||||||
*/
|
*/
|
||||||
public static String[] readSheetNames(File file, ExcelFileFormat format) throws IOException {
|
public static String[] readSheetNames(File file, ExcelFileFormat format)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
return withWorkbook(file, format, ExcelReader::readSheetNames);
|
return withWorkbook(file, format, ExcelReader::readSheetNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +66,8 @@ public class ExcelReader {
|
|||||||
* @return a String[] containing the range names.
|
* @return a String[] containing the range names.
|
||||||
* @throws IOException when the action fails
|
* @throws IOException when the action fails
|
||||||
*/
|
*/
|
||||||
public static String[] readRangeNames(File file, ExcelFileFormat format) throws IOException {
|
public static String[] readRangeNames(File file, ExcelFileFormat format)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
return withWorkbook(file, format, ExcelWorkbook::getRangeNames);
|
return withWorkbook(file, format, ExcelWorkbook::getRangeNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +91,7 @@ public class ExcelReader {
|
|||||||
Integer row_limit,
|
Integer row_limit,
|
||||||
ExcelFileFormat format,
|
ExcelFileFormat format,
|
||||||
ProblemAggregator problemAggregator)
|
ProblemAggregator problemAggregator)
|
||||||
throws IOException, InvalidLocationException {
|
throws IOException, InvalidLocationException, InterruptedException {
|
||||||
return withWorkbook(
|
return withWorkbook(
|
||||||
file,
|
file,
|
||||||
format,
|
format,
|
||||||
@ -130,7 +132,7 @@ public class ExcelReader {
|
|||||||
Integer row_limit,
|
Integer row_limit,
|
||||||
ExcelFileFormat format,
|
ExcelFileFormat format,
|
||||||
ProblemAggregator problemAggregator)
|
ProblemAggregator problemAggregator)
|
||||||
throws IOException, InvalidLocationException {
|
throws IOException, InvalidLocationException, InterruptedException {
|
||||||
return withWorkbook(
|
return withWorkbook(
|
||||||
file,
|
file,
|
||||||
format,
|
format,
|
||||||
@ -175,7 +177,7 @@ public class ExcelReader {
|
|||||||
Integer row_limit,
|
Integer row_limit,
|
||||||
ExcelFileFormat format,
|
ExcelFileFormat format,
|
||||||
ProblemAggregator problemAggregator)
|
ProblemAggregator problemAggregator)
|
||||||
throws IOException, InvalidLocationException {
|
throws IOException, InvalidLocationException, InterruptedException {
|
||||||
return withWorkbook(
|
return withWorkbook(
|
||||||
file,
|
file,
|
||||||
format,
|
format,
|
||||||
@ -202,7 +204,7 @@ public class ExcelReader {
|
|||||||
int skip_rows,
|
int skip_rows,
|
||||||
Integer row_limit,
|
Integer row_limit,
|
||||||
ProblemAggregator problemAggregator)
|
ProblemAggregator problemAggregator)
|
||||||
throws InvalidLocationException {
|
throws InvalidLocationException, InterruptedException {
|
||||||
int sheetIndex = workbook.getSheetIndex(rangeNameOrAddress);
|
int sheetIndex = workbook.getSheetIndex(rangeNameOrAddress);
|
||||||
if (sheetIndex != -1) {
|
if (sheetIndex != -1) {
|
||||||
return readTable(
|
return readTable(
|
||||||
@ -247,7 +249,7 @@ public class ExcelReader {
|
|||||||
Integer row_limit,
|
Integer row_limit,
|
||||||
ExcelFileFormat format,
|
ExcelFileFormat format,
|
||||||
ProblemAggregator problemAggregator)
|
ProblemAggregator problemAggregator)
|
||||||
throws IOException, InvalidLocationException {
|
throws IOException, InvalidLocationException, InterruptedException {
|
||||||
return withWorkbook(
|
return withWorkbook(
|
||||||
file,
|
file,
|
||||||
format,
|
format,
|
||||||
@ -256,7 +258,10 @@ public class ExcelReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T withWorkbook(
|
private static <T> T withWorkbook(
|
||||||
File file, ExcelFileFormat format, Function<ExcelWorkbook, T> action) throws IOException {
|
File file,
|
||||||
|
ExcelFileFormat format,
|
||||||
|
FunctionWithException<ExcelWorkbook, T, InterruptedException> action)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
try (ReadOnlyExcelConnection connection =
|
try (ReadOnlyExcelConnection connection =
|
||||||
ExcelConnectionPool.INSTANCE.openReadOnlyConnection(file, format)) {
|
ExcelConnectionPool.INSTANCE.openReadOnlyConnection(file, format)) {
|
||||||
return connection.withWorkbook(action);
|
return connection.withWorkbook(action);
|
||||||
@ -270,7 +275,7 @@ public class ExcelReader {
|
|||||||
int skip_rows,
|
int skip_rows,
|
||||||
Integer row_limit,
|
Integer row_limit,
|
||||||
ProblemAggregator problemAggregator)
|
ProblemAggregator problemAggregator)
|
||||||
throws InvalidLocationException {
|
throws InvalidLocationException, InterruptedException {
|
||||||
int sheetIndex = workbook.getSheetIndex(excelRange.getSheetName());
|
int sheetIndex = workbook.getSheetIndex(excelRange.getSheetName());
|
||||||
if (sheetIndex == -1) {
|
if (sheetIndex == -1) {
|
||||||
throw new InvalidLocationException(
|
throw new InvalidLocationException(
|
||||||
@ -294,7 +299,8 @@ public class ExcelReader {
|
|||||||
ExcelHeaders.HeaderBehavior headers,
|
ExcelHeaders.HeaderBehavior headers,
|
||||||
int skipRows,
|
int skipRows,
|
||||||
int rowCount,
|
int rowCount,
|
||||||
ProblemAggregator problemAggregator) {
|
ProblemAggregator problemAggregator)
|
||||||
|
throws InterruptedException {
|
||||||
|
|
||||||
ExcelSheet sheet = workbook.getSheetAt(sheetIndex);
|
ExcelSheet sheet = workbook.getSheetAt(sheetIndex);
|
||||||
|
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.enso.table.util;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link java.util.function.Consumer} except that a one can declare a checked exception, E.
|
||||||
|
* Represents an operation that accepts a single input argument and returns no result. Unlike most
|
||||||
|
* other functional interfaces, {@code Consumer} is expected to operate via side-effects.
|
||||||
|
*
|
||||||
|
* <p>This is a <a href="package-summary.html">functional interface</a> whose functional method is
|
||||||
|
* {@link #accept(Object)}.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the input to the operation
|
||||||
|
* @param <E> the type of the checked exception
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ConsumerWithException<T, E extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs this operation on the given argument.
|
||||||
|
*
|
||||||
|
* @param t the input argument
|
||||||
|
*/
|
||||||
|
void accept(T t) throws E;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a composed {@code Consumer} that performs, in sequence, this operation followed by the
|
||||||
|
* {@code after} operation. If performing either operation throws an exception, it is relayed to
|
||||||
|
* the caller of the composed operation. If performing this operation throws an exception, the
|
||||||
|
* {@code after} operation will not be performed.
|
||||||
|
*
|
||||||
|
* @param after the operation to perform after this operation
|
||||||
|
* @return a composed {@code Consumer} that performs in sequence this operation followed by the
|
||||||
|
* {@code after} operation
|
||||||
|
* @throws NullPointerException if {@code after} is null
|
||||||
|
*/
|
||||||
|
default ConsumerWithException<T, E> andThen(java.util.function.Consumer<? super T> after)
|
||||||
|
throws E {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
return (T t) -> {
|
||||||
|
accept(t);
|
||||||
|
after.accept(t);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package org.enso.table.util;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link Function} except that a one can declare a checked exception, E. Represents a
|
||||||
|
* function that accepts one argument and produces a result.
|
||||||
|
*
|
||||||
|
* <p>This is a <a href="package-summary.html">functional interface</a> whose functional method is
|
||||||
|
* {@link #apply(Object)}.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the input to the function
|
||||||
|
* @param <R> the type of the result of the function
|
||||||
|
* @param <E> the type of the checked exception
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FunctionWithException<T, R, E extends Throwable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies this function to the given argument.
|
||||||
|
*
|
||||||
|
* @param t the function argument
|
||||||
|
* @return the function result
|
||||||
|
*/
|
||||||
|
R apply(T t) throws E;
|
||||||
|
|
||||||
|
default <V> FunctionWithException<V, R, E> compose(
|
||||||
|
FunctionWithException<? super V, ? extends T, E> before) {
|
||||||
|
Objects.requireNonNull(before);
|
||||||
|
return (V v) -> apply(before.apply(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a composed function that first applies this function to its input, and then applies the
|
||||||
|
* {@code after} function to the result. If evaluation of either function throws an exception, it
|
||||||
|
* is relayed to the caller of the composed function.
|
||||||
|
*
|
||||||
|
* @param <V> the type of output of the {@code after} function, and of the composed function
|
||||||
|
* @param after the function to apply after this function is applied
|
||||||
|
* @return a composed function that first applies this function and then applies the {@code after}
|
||||||
|
* function
|
||||||
|
* @throws NullPointerException if after is null
|
||||||
|
* @see #compose(Function)
|
||||||
|
*/
|
||||||
|
default <V> FunctionWithException<T, V, E> andThen(
|
||||||
|
FunctionWithException<? super R, ? extends V, E> after) {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
return (T t) -> after.apply(apply(t));
|
||||||
|
}
|
||||||
|
}
|
@ -53,7 +53,8 @@ public class ExcelWriter {
|
|||||||
ExistingDataException,
|
ExistingDataException,
|
||||||
IllegalStateException,
|
IllegalStateException,
|
||||||
ColumnNameMismatchException,
|
ColumnNameMismatchException,
|
||||||
ColumnCountMismatchException {
|
ColumnCountMismatchException,
|
||||||
|
InterruptedException {
|
||||||
if (sheetIndex == 0 || sheetIndex > workbook.getNumberOfSheets()) {
|
if (sheetIndex == 0 || sheetIndex > workbook.getNumberOfSheets()) {
|
||||||
int i = 1;
|
int i = 1;
|
||||||
while (workbook.getSheet("Sheet" + i) != null) {
|
while (workbook.getSheet("Sheet" + i) != null) {
|
||||||
@ -116,7 +117,8 @@ public class ExcelWriter {
|
|||||||
ExistingDataException,
|
ExistingDataException,
|
||||||
IllegalStateException,
|
IllegalStateException,
|
||||||
ColumnNameMismatchException,
|
ColumnNameMismatchException,
|
||||||
ColumnCountMismatchException {
|
ColumnCountMismatchException,
|
||||||
|
InterruptedException {
|
||||||
int sheetIndex = workbook.getNumberOfSheets() == 0 ? -1 : workbook.getSheetIndex(sheetName);
|
int sheetIndex = workbook.getNumberOfSheets() == 0 ? -1 : workbook.getSheetIndex(sheetName);
|
||||||
if (sheetIndex == -1) {
|
if (sheetIndex == -1) {
|
||||||
writeTableToSheet(
|
writeTableToSheet(
|
||||||
@ -169,7 +171,8 @@ public class ExcelWriter {
|
|||||||
RangeExceededException,
|
RangeExceededException,
|
||||||
ExistingDataException,
|
ExistingDataException,
|
||||||
ColumnNameMismatchException,
|
ColumnNameMismatchException,
|
||||||
ColumnCountMismatchException {
|
ColumnCountMismatchException,
|
||||||
|
InterruptedException {
|
||||||
Name name = workbook.getName(rangeNameOrAddress);
|
Name name = workbook.getName(rangeNameOrAddress);
|
||||||
ExcelRange excelRange;
|
ExcelRange excelRange;
|
||||||
try {
|
try {
|
||||||
@ -194,7 +197,8 @@ public class ExcelWriter {
|
|||||||
RangeExceededException,
|
RangeExceededException,
|
||||||
ExistingDataException,
|
ExistingDataException,
|
||||||
ColumnNameMismatchException,
|
ColumnNameMismatchException,
|
||||||
ColumnCountMismatchException {
|
ColumnCountMismatchException,
|
||||||
|
InterruptedException {
|
||||||
int sheetIndex = workbook.getSheetIndex(range.getSheetName());
|
int sheetIndex = workbook.getSheetIndex(range.getSheetName());
|
||||||
if (sheetIndex == -1) {
|
if (sheetIndex == -1) {
|
||||||
throw new InvalidLocationException(
|
throw new InvalidLocationException(
|
||||||
@ -263,7 +267,8 @@ public class ExcelWriter {
|
|||||||
throws RangeExceededException,
|
throws RangeExceededException,
|
||||||
ExistingDataException,
|
ExistingDataException,
|
||||||
ColumnNameMismatchException,
|
ColumnNameMismatchException,
|
||||||
ColumnCountMismatchException {
|
ColumnCountMismatchException,
|
||||||
|
InterruptedException {
|
||||||
Table mappedTable =
|
Table mappedTable =
|
||||||
switch (existingDataMode) {
|
switch (existingDataMode) {
|
||||||
case APPEND_BY_INDEX -> ColumnMapper.mapColumnsByPosition(
|
case APPEND_BY_INDEX -> ColumnMapper.mapColumnsByPosition(
|
||||||
@ -333,7 +338,7 @@ public class ExcelWriter {
|
|||||||
Long rowLimit,
|
Long rowLimit,
|
||||||
ExcelHeaders.HeaderBehavior headers,
|
ExcelHeaders.HeaderBehavior headers,
|
||||||
ExcelSheet sheet)
|
ExcelSheet sheet)
|
||||||
throws RangeExceededException, ExistingDataException {
|
throws RangeExceededException, ExistingDataException, InterruptedException {
|
||||||
boolean writeHeaders = headers == ExcelHeaders.HeaderBehavior.USE_FIRST_ROW_AS_HEADERS;
|
boolean writeHeaders = headers == ExcelHeaders.HeaderBehavior.USE_FIRST_ROW_AS_HEADERS;
|
||||||
int requiredRows =
|
int requiredRows =
|
||||||
Math.min(table.rowCount(), rowLimit == null ? Integer.MAX_VALUE : rowLimit.intValue())
|
Math.min(table.rowCount(), rowLimit == null ? Integer.MAX_VALUE : rowLimit.intValue())
|
||||||
@ -383,7 +388,8 @@ public class ExcelWriter {
|
|||||||
* @param sheet Sheet containing the range.
|
* @param sheet Sheet containing the range.
|
||||||
* @return True if range is empty and clear is False, otherwise returns False.
|
* @return True if range is empty and clear is False, otherwise returns False.
|
||||||
*/
|
*/
|
||||||
private static boolean rangeIsNotEmpty(Workbook workbook, ExcelRange range, ExcelSheet sheet) {
|
private static boolean rangeIsNotEmpty(Workbook workbook, ExcelRange range, ExcelSheet sheet)
|
||||||
|
throws InterruptedException {
|
||||||
ExcelRange fullRange = range.getAbsoluteRange(workbook);
|
ExcelRange fullRange = range.getAbsoluteRange(workbook);
|
||||||
for (int row = fullRange.getTopRow(); row <= fullRange.getBottomRow(); row++) {
|
for (int row = fullRange.getTopRow(); row <= fullRange.getBottomRow(); row++) {
|
||||||
ExcelRow excelRow = sheet.get(row);
|
ExcelRow excelRow = sheet.get(row);
|
||||||
@ -401,7 +407,8 @@ public class ExcelWriter {
|
|||||||
* @param range The range to clear.
|
* @param range The range to clear.
|
||||||
* @param sheet Sheet containing the range.
|
* @param sheet Sheet containing the range.
|
||||||
*/
|
*/
|
||||||
private static void clearRange(Workbook workbook, ExcelRange range, ExcelSheet sheet) {
|
private static void clearRange(Workbook workbook, ExcelRange range, ExcelSheet sheet)
|
||||||
|
throws InterruptedException {
|
||||||
ExcelRange fullRange = range.getAbsoluteRange(workbook);
|
ExcelRange fullRange = range.getAbsoluteRange(workbook);
|
||||||
for (int row = fullRange.getTopRow(); row <= fullRange.getBottomRow(); row++) {
|
for (int row = fullRange.getTopRow(); row <= fullRange.getBottomRow(); row++) {
|
||||||
ExcelRow excelRow = sheet.get(row);
|
ExcelRow excelRow = sheet.get(row);
|
||||||
@ -547,7 +554,7 @@ public class ExcelWriter {
|
|||||||
* @return EXCEL_COLUMN_NAMES if the range has headers, otherwise USE_FIRST_ROW_AS_HEADERS.
|
* @return EXCEL_COLUMN_NAMES if the range has headers, otherwise USE_FIRST_ROW_AS_HEADERS.
|
||||||
*/
|
*/
|
||||||
private static ExcelHeaders.HeaderBehavior shouldWriteHeaders(
|
private static ExcelHeaders.HeaderBehavior shouldWriteHeaders(
|
||||||
ExcelSheet excelSheet, int topRow, int startCol, int endCol) {
|
ExcelSheet excelSheet, int topRow, int startCol, int endCol) throws InterruptedException {
|
||||||
ExcelRow row = excelSheet.get(topRow);
|
ExcelRow row = excelSheet.get(topRow);
|
||||||
|
|
||||||
// If the first row is missing or empty, should write headers.
|
// If the first row is missing or empty, should write headers.
|
||||||
|
Loading…
Reference in New Issue
Block a user