use of io.deephaven.web.shared.fu.RemoverFn in project deephaven-core by deephaven.
the class JsFigureFactory method create.
private static Promise<JsFigure> create(JsFigureDescriptor descriptor) {
JsArray<JsTable> tables = descriptor.getTables();
if (tables == null || tables.length == 0) {
return (Promise<JsFigure>) (Promise) Promise.reject("No tables provided for Figure creation");
}
FigureDescriptor figureDescriptor = convertJsFigureDescriptor(descriptor);
FetchObjectResponse response = new FetchObjectResponse();
response.setData(figureDescriptor.serializeBinary());
Promise<?>[] tableCopyPromises = tables.map((table, index, all) -> table.copy(false)).asArray(new Promise[0]);
return Promise.all(tableCopyPromises).then(unknownTableCopies -> {
JsArray<JsTable> jsTableCopies = Js.cast(unknownTableCopies);
JsTable[] tableCopies = jsTableCopies.asArray(new JsTable[0]);
return new JsFigure(c -> c.apply(null, response), (figure, descriptor1) -> {
// We need to listen for disconnects and reconnects
boolean[] isTableDisconnected = new boolean[tableCopies.length];
ArrayList<RemoverFn> removerFns = new ArrayList<>(tableCopies.length * 3);
for (int i = 0; i < tableCopies.length; i++) {
final int tableIndex = i;
// Tables are closed when the figure is closed, no need to remove listeners later
removerFns.add(tableCopies[i].addEventListener(JsTable.EVENT_DISCONNECT, ignore -> {
isTableDisconnected[tableIndex] = true;
for (int j = 0; j < isTableDisconnected.length; j++) {
if (isTableDisconnected[j] && j != tableIndex) {
return;
}
}
figure.fireEvent(JsFigure.EVENT_DISCONNECT);
figure.unsubscribe();
}));
removerFns.add(tableCopies[i].addEventListener(JsTable.EVENT_RECONNECT, ignore -> {
isTableDisconnected[tableIndex] = false;
for (int j = 0; j < isTableDisconnected.length; j++) {
if (isTableDisconnected[j]) {
return;
}
}
try {
figure.verifyTables();
figure.fireEvent(JsFigure.EVENT_RECONNECT);
figure.enqueueSubscriptionCheck();
} catch (JsFigure.FigureSourceException e) {
final CustomEventInit init = CustomEventInit.create();
init.setDetail(e);
figure.fireEvent(JsFigure.EVENT_RECONNECTFAILED, init);
}
}));
removerFns.add(tableCopies[i].addEventListener(JsTable.EVENT_RECONNECTFAILED, err -> {
for (RemoverFn removerFn : removerFns) {
removerFn.remove();
}
figure.unsubscribe();
final CustomEventInit init = CustomEventInit.create();
init.setDetail(err);
figure.fireEvent(JsFigure.EVENT_RECONNECTFAILED, init);
}));
}
return Promise.resolve(new JsFigure.FigureTableFetchData(tableCopies, new TableMap[0], Collections.emptyMap()));
}).refetch();
});
}
use of io.deephaven.web.shared.fu.RemoverFn in project deephaven-core by deephaven.
the class JsTable method fetchTotals.
private Promise<JsTotalsTable> fetchTotals(Object config, JsProvider<ClientTableState> state) {
JsTotalsTableConfig directive = getTotalsDirectiveFromOptionalConfig(config);
ClientTableState[] lastGood = { null };
final JsTableFetch totalsFactory = (callback, newState, metadata) -> {
final ClientTableState target;
// we know this will get called at least once, immediately, so lastGood will never be null
if (isClosed()) {
// source table was closed, we have to rely on lastGood...
target = lastGood[0];
} else {
target = state.valueOf();
// make sure we are only retained by one state at a time
// TODO: refactor subscription system to handle non-JsTable subscriptions w/ same one:one semantics,
target.retain(directive);
if (lastGood[0] != null && lastGood[0] != target) {
lastGood[0].unretain(directive);
}
lastGood[0] = target;
}
JsLog.debug("Sending totals table fetch ", directive, " for ", target, "(", LazyString.of(target::getHandle), "), into ", LazyString.of(newState::getHandle), "(", newState, ")");
// );
throw new UnsupportedOperationException("totalsTables");
};
String summary = "totals table " + directive + ", " + directive.groupBy.join(",");
final ClientTableState totals = workerConnection.newState(totalsFactory, summary);
final LazyPromise<JsTotalsTable> result = new LazyPromise<>();
boolean[] downsample = { true };
return // lastGood will always be non-null after this
totals.refetch(this, workerConnection.metadata()).then(ready -> {
JsTable wrapped = new JsTable(workerConnection, ready);
// technically this is overkill, but it is more future-proofed than only listening for column
// changes
final RemoverFn remover = addEventListener(INTERNAL_EVENT_STATECHANGED, e -> {
// which simply accrues state until the user decides to commit the modification).
if (downsample[0]) {
downsample[0] = false;
LazyPromise.runLater(() -> {
if (wrapped.isClosed()) {
return;
}
downsample[0] = true;
// IDS-2684 - comment out the four lines above to reproduce
// when ever the main table changes its state, reload the totals table from the
// new state
final ClientTableState existing = wrapped.state();
final ClientTableState nextState = workerConnection.newState(totalsFactory, summary);
JsLog.debug("Rebasing totals table", existing, " -> ", nextState, " for ", wrapped);
wrapped.setState(nextState);
// If the wrapped table's state has changed (any filter / sort / columns
// applied),
// then we'll want to re-apply these conditions on top of the newly set state.
final boolean needsMutation = !existing.isEqual(ready);
final ThenOnFulfilledCallbackFn restoreVp = running -> {
// now that we've (possibly) updated selection conditions, put back in any
// viewport.
result.onSuccess(JsTotalsTable::refreshViewport);
return null;
};
final Promise<ClientTableState> promise = nextState.refetch(this, workerConnection.metadata());
if (needsMutation) {
// nextState will be empty, so we might want to test for
// isEmpty() instead
wrapped.batch(b -> b.setConfig(existing)).then(restoreVp);
} else {
promise.then(restoreVp);
}
// IDS-2684 - Comment out the two lines below to reproduce
});
}
});
wrapped.onClosed.add(remover::remove);
wrapped.onClosed.add(() -> lastGood[0].unretain(directive));
onClosed.add(remover::remove);
final JsTotalsTable totalsTable = new JsTotalsTable(wrapped, directive.serialize(), directive.groupBy);
result.succeed(totalsTable);
return result.asPromise();
});
}
Aggregations