From 31b668e7b3b4a201a9ae788a43d5fde1853abe2e Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Mon, 6 Apr 2026 14:31:38 -0600 Subject: [PATCH 01/35] feat: DH-21235: Column Restrction JS API --- .../io/deephaven/web/client/api/Column.java | 36 +- .../client/api/barrage/WebBarrageUtils.java | 364 +++++++++++++++++- .../api/barrage/def/ColumnDefinition.java | 20 +- .../barrage/def/InitialTableDefinition.java | 10 + .../api/barrage/def/InputTableMetadata.java | 58 +++ .../web/client/state/ClientTableState.java | 10 + 6 files changed, 490 insertions(+), 8 deletions(-) create mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java b/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java index a44100f1f60..2353272e9db 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java @@ -5,6 +5,7 @@ import com.vertispan.tsdefs.annotations.TsName; import com.vertispan.tsdefs.annotations.TsTypeRef; +import elemental2.core.JsArray; import io.deephaven.web.client.api.filter.FilterValue; import jsinterop.annotations.JsMethod; import jsinterop.annotations.JsNullable; @@ -48,6 +49,7 @@ public class Column { private String description; private final boolean isInputTableKeyColumn; private final boolean isInputTableValueColumn; + private final JsArray columnRestrictions; /** * Format entire rows colors using the expression specified. Returns a {@code CustomColumn} object to apply to a @@ -81,7 +83,8 @@ public static CustomColumn createCustomColumn( public Column(int jsIndex, int index, Integer formatColumnIndex, Integer styleColumnIndex, String type, String name, boolean isPartitionColumn, Integer formatStringColumnIndex, String description, - boolean inputTableKeyColumn, boolean inputTableValueColumn, boolean isSortable) { + boolean inputTableKeyColumn, boolean inputTableValueColumn, boolean isSortable, + JsArray columnRestrictions) { this.jsIndex = jsIndex; this.index = index; assert Objects.equals(formatColumnIndex, styleColumnIndex); @@ -94,6 +97,18 @@ public Column(int jsIndex, int index, Integer formatColumnIndex, Integer styleCo this.isInputTableKeyColumn = inputTableKeyColumn; this.isInputTableValueColumn = inputTableValueColumn; this.isSortable = isSortable; + this.columnRestrictions = columnRestrictions; + } + + /** + * @deprecated Use the constructor with columnRestrictions parameter instead + */ + @Deprecated + public Column(int jsIndex, int index, Integer formatColumnIndex, Integer styleColumnIndex, String type, String name, + boolean isPartitionColumn, Integer formatStringColumnIndex, String description, + boolean inputTableKeyColumn, boolean inputTableValueColumn, boolean isSortable) { + this(jsIndex, index, formatColumnIndex, styleColumnIndex, type, name, isPartitionColumn, + formatStringColumnIndex, description, inputTableKeyColumn, inputTableValueColumn, isSortable, null); } /** @@ -222,6 +237,19 @@ public boolean getIsSortable() { return isSortable; } + /** + * Returns the column restrictions for input table columns, or null if this is not an input table column + * or if no restrictions are defined. The restrictions are implementation-specific constraints that the + * server enforces on column values. + * + * @return Array of column restrictions, or null if none are defined + */ + @JsProperty + @JsNullable + public JsArray getColumnRestrictions() { + return columnRestrictions; + } + /** * Creates a new value for use in filters based on this column. Used either as a parameter to another filter * operation, or as a builder to create a filter operation. @@ -320,11 +348,13 @@ public int hashCode() { public Column withFormatStringColumnIndex(int formatStringColumnIndex) { return new Column(jsIndex, index, styleColumnIndex, styleColumnIndex, type, name, isPartitionColumn, - formatStringColumnIndex, description, isInputTableKeyColumn, isInputTableValueColumn, isSortable); + formatStringColumnIndex, description, isInputTableKeyColumn, isInputTableValueColumn, isSortable, + columnRestrictions); } public Column withStyleColumnIndex(int styleColumnIndex) { return new Column(jsIndex, index, styleColumnIndex, styleColumnIndex, type, name, isPartitionColumn, - formatStringColumnIndex, description, isInputTableKeyColumn, isInputTableValueColumn, isSortable); + formatStringColumnIndex, description, isInputTableKeyColumn, isInputTableValueColumn, isSortable, + columnRestrictions); } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index ea493615c2b..2cd586dc026 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -9,6 +9,7 @@ import io.deephaven.barrage.flatbuf.BarrageMessageWrapper; import io.deephaven.web.client.api.barrage.def.ColumnDefinition; import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; +import io.deephaven.web.client.api.barrage.def.InputTableMetadata; import io.deephaven.web.client.api.barrage.def.TableAttributesDefinition; import io.deephaven.web.shared.data.*; import org.apache.arrow.flatbuf.KeyValue; @@ -65,11 +66,372 @@ public static InitialTableDefinition readTableDefinition(Schema schema) { keyValuePairs("deephaven:attribute_type.", schema.customMetadataLength(), schema::customMetadata), keyValuePairs("deephaven:unsent.attribute.", schema.customMetadataLength(), schema::customMetadata) .keySet()); + + // Parse input table metadata if present + InputTableMetadata inputTableMetadata = parseInputTableMetadata(schema, cols); + return new InitialTableDefinition() .setAttributes(attributes) - .setColumns(cols); + .setColumns(cols) + .setInputTableMetadata(inputTableMetadata); + } + + private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnDefinition[] cols) { + // Extract the tableMetadata from schema custom metadata + Map schemaMetadata = + keyValuePairs("deephaven:", schema.customMetadataLength(), schema::customMetadata); + + consoleLog("parseInputTableMetadata: Checking for tableMetadata in schema"); + + String tableMetadataBase64 = schemaMetadata.get("tableMetadata"); + if (tableMetadataBase64 == null || tableMetadataBase64.isEmpty()) { + consoleLog("parseInputTableMetadata: No tableMetadata found in schema (null or empty)"); + return null; + } + + consoleLog("parseInputTableMetadata: Found tableMetadata, length=" + tableMetadataBase64.length()); + InputTableMetadata metadata = new InputTableMetadata(); + + try { + // Decode base64 to Uint8Array (like Java's Base64.getDecoder().decode()) + consoleLog("parseInputTableMetadata: Decoding base64..."); + Uint8Array bytes = decodeBase64(tableMetadataBase64); + consoleLog("parseInputTableMetadata: Decoded to " + bytes.length + " bytes"); + + // The issue: JavaScript protobuf deserialization fails on google.protobuf.Any types + // Solution: Parse the protobuf manually at the binary level to extract what we need + consoleLog("parseInputTableMetadata: Attempting manual protobuf parsing..."); + jsinterop.base.Any result = parseProtoManually(bytes); + + if (result == null) { + consoleLog("parseInputTableMetadata: Manual parsing returned null"); + return metadata; + } + + consoleLog("parseInputTableMetadata: Successfully parsed, extracting column info"); + + // Extract column restrictions from the manually parsed data + for (ColumnDefinition col : cols) { + String columnName = col.getName(); + jsinterop.base.Any columnInfo = getPropertyFromMap(result, columnName); + + if (columnInfo != null) { + consoleLog("parseInputTableMetadata: Found info for column '" + columnName + "'"); + + // Get restrictions array if available + jsinterop.base.Any restrictionsList = getProperty(columnInfo, "restrictions"); + if (restrictionsList != null && isArray(restrictionsList)) { + elemental2.core.JsArray restrictions = + jsinterop.base.Js.uncheckedCast(restrictionsList); + + if (restrictions.length > 0) { + consoleLog("parseInputTableMetadata: Column '" + columnName + "' has " + + restrictions.length + " restrictions"); + + InputTableMetadata.ColumnRestrictions colRestrictions = + new InputTableMetadata.ColumnRestrictions(); + for (int i = 0; i < restrictions.length; i++) { + colRestrictions.addRestriction(restrictions.getAt(i)); + } + metadata.addColumnRestrictions(columnName, colRestrictions); + } + } + } + } + + consoleLog("parseInputTableMetadata: Successfully parsed metadata"); + } catch (Exception e) { + consoleError("parseInputTableMetadata: Exception during parsing", e); + } + + return metadata; } + // Native JavaScript methods to decode and parse protobuf without generated classes + + private static native Uint8Array decodeBase64(String base64) /*-{ + // Convert base64 string to Uint8Array + var binaryString = atob(base64); + var bytes = new Uint8Array(binaryString.length); + for (var i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; + }-*/; + + private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /*-{ + // Manual protobuf parsing to work around missing google.protobuf.Any + // Based on the pattern from AddToInputTable.java + // Use the BinaryReader from dhinternal.jspb which is the JsInterop wrapper + try { + console.log("parseProtoManually: Starting manual protobuf parsing"); + console.log("parseProtoManually: Bytes length =", bytes.length); + + // Use the JsInterop BinaryReader class + var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); + var reader = new BinaryReader(bytes); + var result = {}; + + // Parse the DeephavenTableMetadata message + // Field 1 is InputTableMetadata + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + console.log("parseProtoManually: Reading field", field); + + if (field === 1) { + // This is the InputTableMetadata field + var inputTableMetadata = {}; + reader.readMessage(inputTableMetadata, function(metadata, rdr) { + // Parse InputTableMetadata + // Field 1 is columnInfoMap (map) + while (rdr.nextField()) { + if (rdr.isEndGroup()) { + break; + } + var subfield = rdr.getFieldNumber(); + console.log("parseProtoManually: InputTableMetadata field", subfield); + + if (subfield === 1) { + // This is the columnInfoMap + var mapEntry = {}; + rdr.readMessage(mapEntry, function(entry, mapReader) { + var key = null; + var value = null; + + while (mapReader.nextField()) { + if (mapReader.isEndGroup()) { + break; + } + var mapField = mapReader.getFieldNumber(); + + if (mapField === 1) { + // Map key (column name) + key = mapReader.readString(); + console.log("parseProtoManually: Column name =", key); + } else if (mapField === 2) { + // Map value (InputTableColumnInfo) + value = {}; + var restrictions = []; + + mapReader.readMessage(value, function(columnInfo, colReader) { + while (colReader.nextField()) { + if (colReader.isEndGroup()) { + break; + } + var colField = colReader.getFieldNumber(); + console.log("parseProtoManually: InputTableColumnInfo field", colField); + + if (colField === 1) { + // kind field + columnInfo.kind = colReader.readEnum(); + } else if (colField === 2) { + // restrictions field (repeated google.protobuf.Any) + // Parse the Any message to extract type_url and value + try { + var anyBytes = colReader.readBytes(); + console.log("parseProtoManually: Got Any bytes, length =", anyBytes.length); + + // Parse the google.protobuf.Any message + // Field 1 = type_url (string) + // Field 2 = value (bytes) + var anyReader = new BinaryReader(anyBytes); + var typeUrl = null; + var valueBytes = null; + + while (anyReader.nextField()) { + if (anyReader.isEndGroup()) { + break; + } + var anyField = anyReader.getFieldNumber(); + if (anyField === 1) { + typeUrl = anyReader.readString(); + console.log("parseProtoManually: Restriction type_url =", typeUrl); + } else if (anyField === 2) { + valueBytes = anyReader.readBytes(); + console.log("parseProtoManually: Restriction value bytes length =", valueBytes.length); + } + } + + // Now parse the actual restriction based on type_url + var restriction = @io.deephaven.web.client.api.barrage.WebBarrageUtils::parseRestriction(*)(typeUrl, valueBytes); + if (restriction) { + restrictions.push(restriction); + } + } catch (e) { + console.warn("parseProtoManually: Failed to parse restriction:", e); + } + } + } + columnInfo.restrictions = restrictions; + }); + } + } + + if (key !== null && value !== null) { + if (!metadata.columnInfoMap) { + metadata.columnInfoMap = {}; + } + metadata.columnInfoMap[key] = value; + console.log("parseProtoManually: Added column", key, "with", value.restrictions ? value.restrictions.length : 0, "restrictions"); + } + }); + } + } + }); + + result = inputTableMetadata.columnInfoMap || {}; + console.log("parseProtoManually: Parsed", Object.keys(result).length, "columns"); + } + } + + return result; + + } catch (e) { + console.error("parseProtoManually: Failed to manually parse protobuf:", e); + console.error("Stack:", e.stack); + return null; + } + }-*/; + + private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8Array valueBytes) /*-{ + // Parse specific restriction types based on typeUrl + // Types from inputtable.proto: + // - IntegerRangeRestriction + // - DoubleRangeRestriction + // - NotNullRestriction + // - NonEmptyRestriction + // - StringListRestriction + + try { + console.log("parseRestriction: Parsing restriction type:", typeUrl); + + var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); + var reader = new BinaryReader(valueBytes); + + // Extract the message type from the type URL + // Format: "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" + // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" + var typeName = typeUrl.substring(typeUrl.lastIndexOf('/') + 1); + var shortName = typeName.substring(typeName.lastIndexOf('.') + 1); + console.log("parseRestriction: Message type:", shortName); + + var restriction = { + type: shortName, + typeUrl: typeUrl + }; + + if (shortName === 'IntegerRangeRestriction') { + // Field 1 = min_inclusive (int64) + // Field 2 = max_inclusive (int64) + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + restriction.minInclusive = reader.readInt64(); + console.log("parseRestriction: minInclusive =", restriction.minInclusive); + } else if (field === 2) { + restriction.maxInclusive = reader.readInt64(); + console.log("parseRestriction: maxInclusive =", restriction.maxInclusive); + } + } + } else if (shortName === 'DoubleRangeRestriction') { + // Field 1 = min_inclusive (double) + // Field 2 = max_inclusive (double) + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + restriction.minInclusive = reader.readDouble(); + console.log("parseRestriction: minInclusive =", restriction.minInclusive); + } else if (field === 2) { + restriction.maxInclusive = reader.readDouble(); + console.log("parseRestriction: maxInclusive =", restriction.maxInclusive); + } + } + } else if (shortName === 'NotNullRestriction') { + // No fields - just the type + restriction.notNull = true; + console.log("parseRestriction: NotNull restriction"); + } else if (shortName === 'NonEmptyRestriction') { + // No fields - just the type + restriction.nonEmpty = true; + console.log("parseRestriction: NonEmpty restriction"); + } else if (shortName === 'StringListRestriction') { + // Field 1 = allowed_values (repeated string) + restriction.allowedValues = []; + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + var value = reader.readString(); + restriction.allowedValues.push(value); + console.log("parseRestriction: allowed value =", value); + } + } + } else { + console.warn("parseRestriction: Unknown restriction type:", shortName); + restriction.raw = valueBytes; + } + + return restriction; + + } catch (e) { + console.error("parseRestriction: Failed to parse restriction:", e); + return null; + } + }-*/; + + private static native jsinterop.base.Any getPropertyFromMap(jsinterop.base.Any map, String key) /*-{ + if (map == null) return null; + return map[key] || null; + }-*/; + + private static native jsinterop.base.Any getProperty(jsinterop.base.Any obj, String propertyName) /*-{ + if (obj == null) return null; + var getter = 'get' + propertyName.charAt(0).toUpperCase() + propertyName.slice(1); + if (typeof obj[getter] === 'function') { + return obj[getter](); + } + return obj[propertyName]; + }-*/; + + private static native jsinterop.base.Any getMapEntry(jsinterop.base.Any map, String key) /*-{ + if (map == null) return null; + if (typeof map.get === 'function') { + return map.get(key); + } + if (typeof map.getMap === 'function') { + var m = map.getMap(); + return m ? m[key] : null; + } + return map[key]; + }-*/; + + private static native boolean isArray(jsinterop.base.Any obj) /*-{ + return Array.isArray(obj); + }-*/; + + private static native void consoleLog(String message) /*-{ + console.log(message); + }-*/; + + private static native void consoleError(String message, Exception e) /*-{ + console.error(message, e); + }-*/; + + private static native void logProtoObject(String label, jsinterop.base.Any obj) /*-{ + console.log(label + ":", obj); + if (obj != null) { + console.log(label + " keys:", Object.keys(obj)); + console.log(label + " methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(obj)).filter(function(name) { + return typeof obj[name] === 'function'; + })); + } + }-*/; + private static ColumnDefinition[] readColumnDefinitions(Schema schema) { ColumnDefinition[] cols = new ColumnDefinition[(int) schema.fieldsLength()]; for (int i = 0; i < schema.fieldsLength(); i++) { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/ColumnDefinition.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/ColumnDefinition.java index 90c0d549613..c1d08e3dd62 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/ColumnDefinition.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/ColumnDefinition.java @@ -3,7 +3,9 @@ // package io.deephaven.web.client.api.barrage.def; +import elemental2.core.JsArray; import io.deephaven.web.client.api.Column; +import jsinterop.base.Any; import org.apache.arrow.flatbuf.Field; import java.util.Map; @@ -36,6 +38,7 @@ public class ColumnDefinition { private final boolean isInputTableKeyColumn; private final boolean isInputTableValueColumn; private final String description; + private JsArray columnRestrictions; public ColumnDefinition(int index, Field field) { Map fieldMetadata = @@ -130,9 +133,17 @@ public String getDescription() { return description; } + public JsArray getColumnRestrictions() { + return columnRestrictions; + } + + public void setColumnRestrictions(JsArray columnRestrictions) { + this.columnRestrictions = columnRestrictions; + } + public Column makeJsColumn(int index, Map> map) { if (isForRow()) { - return makeColumn(-1, this, null, null, false, null, null, false, false); + return makeColumn(-1, this, null, null, false, null, null, false, false, null); } Map byNameMap = map.get(isRollupConstituentNodeColumn()); ColumnDefinition format = byNameMap.get(getFormatColumnName()); @@ -146,16 +157,17 @@ public Column makeJsColumn(int index, Map format == null ? null : format.getColumnIndex(), getDescription(), isInputTableKeyColumn(), - isInputTableValueColumn()); + isInputTableValueColumn(), + getColumnRestrictions()); } private static Column makeColumn(int jsIndex, ColumnDefinition definition, Integer numberFormatIndex, Integer styleIndex, boolean isPartitionColumn, Integer formatStringIndex, String description, - boolean inputTableKeyColumn, boolean inputTableValueColumn) { + boolean inputTableKeyColumn, boolean inputTableValueColumn, JsArray columnRestrictions) { return new Column(jsIndex, definition.getColumnIndex(), numberFormatIndex, styleIndex, definition.getType(), definition.getName(), isPartitionColumn, formatStringIndex, description, inputTableKeyColumn, inputTableValueColumn, - definition.isSortable()); + definition.isSortable(), columnRestrictions); } public boolean isHierarchicalExpandByColumn() { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InitialTableDefinition.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InitialTableDefinition.java index fdd09139abb..4300758f31f 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InitialTableDefinition.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InitialTableDefinition.java @@ -20,6 +20,7 @@ public class InitialTableDefinition { private ColumnDefinition[] columns; private TableAttributesDefinition attributes; + private InputTableMetadata inputTableMetadata; public ColumnDefinition[] getColumns() { return columns; @@ -39,6 +40,15 @@ public InitialTableDefinition setAttributes(final TableAttributesDefinition attr return this; } + public InputTableMetadata getInputTableMetadata() { + return inputTableMetadata; + } + + public InitialTableDefinition setInputTableMetadata(final InputTableMetadata inputTableMetadata) { + this.inputTableMetadata = inputTableMetadata; + return this; + } + public Map> getColumnsByName() { return Arrays.stream(columns) .collect(Collectors.partitioningBy(ColumnDefinition::isRollupConstituentNodeColumn, diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java new file mode 100644 index 00000000000..ab375473050 --- /dev/null +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java @@ -0,0 +1,58 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.web.client.api.barrage.def; + +import elemental2.core.JsArray; +import jsinterop.annotations.JsIgnore; +import jsinterop.annotations.JsNullable; +import jsinterop.base.Any; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents input table metadata parsed from the schema. + */ +public class InputTableMetadata { + private final Map columnRestrictions; + + @JsIgnore + public InputTableMetadata() { + this.columnRestrictions = new HashMap<>(); + } + + @JsIgnore + public void addColumnRestrictions(String columnName, ColumnRestrictions restrictions) { + columnRestrictions.put(columnName, restrictions); + } + + @JsIgnore + @JsNullable + public ColumnRestrictions getColumnRestrictions(String columnName) { + return columnRestrictions.get(columnName); + } + + /** + * Represents restrictions on a column's values. + */ + public static class ColumnRestrictions { + private final JsArray restrictions; + + @JsIgnore + public ColumnRestrictions() { + this.restrictions = new JsArray<>(); + } + + @JsIgnore + public void addRestriction(Any restriction) { + restrictions.push(restriction); + } + + @JsIgnore + public JsArray getRestrictions() { + return restrictions; + } + } +} + diff --git a/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java b/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java index 949264feb46..5aa7aadde64 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java @@ -447,6 +447,16 @@ private void setTableDef(InitialTableDefinition tableDef) { if (create) { ColumnDefinition[] columnDefinitions = tableDef.getColumns(); + // Populate column restrictions from input table metadata if available + if (tableDef.getInputTableMetadata() != null) { + for (ColumnDefinition definition : columnDefinitions) { + var restrictions = tableDef.getInputTableMetadata().getColumnRestrictions(definition.getName()); + if (restrictions != null) { + definition.setColumnRestrictions(restrictions.getRestrictions()); + } + } + } + // iterate through the columns, combine format columns into the normal model Map> byNameMap = tableDef.getColumnsByName(); Column[] columns = new Column[0]; From aa2f17d17cf60249a721b23e31a409598212674b Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 7 Apr 2026 07:32:41 -0600 Subject: [PATCH 02/35] non-native decode --- .../client/api/barrage/WebBarrageUtils.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 2cd586dc026..572d472a9fe 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -5,6 +5,7 @@ import com.google.flatbuffers.FlatBufferBuilder; import elemental2.core.*; +import elemental2.dom.DomGlobal; import io.deephaven.barrage.flatbuf.BarrageMessageType; import io.deephaven.barrage.flatbuf.BarrageMessageWrapper; import io.deephaven.web.client.api.barrage.def.ColumnDefinition; @@ -147,17 +148,18 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD return metadata; } - // Native JavaScript methods to decode and parse protobuf without generated classes + // Decode base64 string to Uint8Array using elemental2 + private static Uint8Array decodeBase64(String base64) { + // Use DomGlobal.atob() to decode base64 to binary string + String binaryString = DomGlobal.atob(base64); - private static native Uint8Array decodeBase64(String base64) /*-{ - // Convert base64 string to Uint8Array - var binaryString = atob(base64); - var bytes = new Uint8Array(binaryString.length); - for (var i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i); + // Convert binary string to Uint8Array + Uint8Array bytes = new Uint8Array(binaryString.length()); + for (int i = 0; i < binaryString.length(); i++) { + bytes.setAt(i, (double) (binaryString.charAt(i) & 0xff)); } return bytes; - }-*/; + } private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /*-{ // Manual protobuf parsing to work around missing google.protobuf.Any From 55145d31cf759a123b5433b3e51dca25e6a16b7f Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 7 Apr 2026 09:20:13 -0600 Subject: [PATCH 03/35] update comment --- .../engine/util/input/InputTableUpdater.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java b/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java index e09f774997c..450229e3d78 100644 --- a/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java +++ b/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java @@ -57,12 +57,17 @@ default List getValueNames() { } /** - * If there are client-side defined restrictions on this column; return them as a JSON string to be interpreted by - * the client for properly displaying the edit field. + * If there are client-side defined restrictions on this column, return them as a list of protobuf Any messages. + * These restrictions are used by the client for properly displaying and validating the edit field. + * + *

+ * The restrictions are packed as {@code google.protobuf.Any} messages, which allows for different restriction + * types (e.g., {@code IntegerRangeRestriction}, {@code DoubleRangeRestriction}, {@code StringListRestriction}, + * etc.) to be sent to the client. The client is responsible for unpacking and interpreting these restrictions. * * @param columnName the column name to query - * @return a string representing the restrictions for this column, or null if no client-side restrictions are - * supplied for this column + * @return a list of protobuf Any messages representing the restrictions for this column, or null if no + * client-side restrictions are supplied for this column */ @Nullable default List getColumnRestrictions(final String columnName) { From 180bc15306c02c25ad9df09a09e5057bce46ee9b Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 7 Apr 2026 10:24:25 -0600 Subject: [PATCH 04/35] send restrictions as json --- .../engine/util/input/InputTableUpdater.java | 13 +- extensions/barrage/build.gradle | 1 + .../extensions/barrage/util/BarrageUtil.java | 30 +- .../client/api/barrage/WebBarrageUtils.java | 301 ++---------------- 4 files changed, 64 insertions(+), 281 deletions(-) diff --git a/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java b/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java index 450229e3d78..e09f774997c 100644 --- a/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java +++ b/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java @@ -57,17 +57,12 @@ default List getValueNames() { } /** - * If there are client-side defined restrictions on this column, return them as a list of protobuf Any messages. - * These restrictions are used by the client for properly displaying and validating the edit field. - * - *

- * The restrictions are packed as {@code google.protobuf.Any} messages, which allows for different restriction - * types (e.g., {@code IntegerRangeRestriction}, {@code DoubleRangeRestriction}, {@code StringListRestriction}, - * etc.) to be sent to the client. The client is responsible for unpacking and interpreting these restrictions. + * If there are client-side defined restrictions on this column; return them as a JSON string to be interpreted by + * the client for properly displaying the edit field. * * @param columnName the column name to query - * @return a list of protobuf Any messages representing the restrictions for this column, or null if no - * client-side restrictions are supplied for this column + * @return a string representing the restrictions for this column, or null if no client-side restrictions are + * supplied for this column */ @Nullable default List getColumnRestrictions(final String columnName) { diff --git a/extensions/barrage/build.gradle b/extensions/barrage/build.gradle index dff44595c32..350e81803c4 100644 --- a/extensions/barrage/build.gradle +++ b/extensions/barrage/build.gradle @@ -15,6 +15,7 @@ dependencies { api project(':engine-table') implementation project(':proto:proto-backplane-grpc-flight') + implementation libs.protobuf.java.util implementation project(':log-factory') api libs.deephaven.barrage.format implementation libs.hdrhistogram diff --git a/extensions/barrage/src/main/java/io/deephaven/extensions/barrage/util/BarrageUtil.java b/extensions/barrage/src/main/java/io/deephaven/extensions/barrage/util/BarrageUtil.java index da8c32a5df3..dcb37270134 100755 --- a/extensions/barrage/src/main/java/io/deephaven/extensions/barrage/util/BarrageUtil.java +++ b/extensions/barrage/src/main/java/io/deephaven/extensions/barrage/util/BarrageUtil.java @@ -8,6 +8,8 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.ByteStringAccess; +import com.google.protobuf.util.JsonFormat; +import com.google.protobuf.util.JsonFormat.TypeRegistry; import com.google.rpc.Code; import io.deephaven.UncheckedDeephavenException; import io.deephaven.api.util.NameValidator; @@ -53,9 +55,14 @@ import io.deephaven.internal.log.LoggerFactory; import io.deephaven.io.logger.Logger; import io.deephaven.proto.backplane.grpc.DeephavenTableMetadata; +import io.deephaven.proto.backplane.grpc.DoubleRangeRestriction; import io.deephaven.proto.backplane.grpc.ExportedTableCreationResponse; import io.deephaven.proto.backplane.grpc.InputTableMetadata; import io.deephaven.proto.backplane.grpc.InputTableColumnInfo; +import io.deephaven.proto.backplane.grpc.IntegerRangeRestriction; +import io.deephaven.proto.backplane.grpc.NonEmptyRestriction; +import io.deephaven.proto.backplane.grpc.NotNullRestriction; +import io.deephaven.proto.backplane.grpc.StringListRestriction; import io.deephaven.proto.flight.util.MessageHelper; import io.deephaven.proto.flight.util.SchemaHelper; import io.deephaven.proto.util.Exceptions; @@ -128,6 +135,17 @@ public class BarrageUtil { private static final Logger log = LoggerFactory.getLogger(BarrageUtil.class); + /** + * TypeRegistry for JSON serialization of InputTableMetadata with restriction types. + */ + private static final TypeRegistry INPUT_TABLE_TYPE_REGISTRY = TypeRegistry.newBuilder() + .add(IntegerRangeRestriction.getDescriptor()) + .add(DoubleRangeRestriction.getDescriptor()) + .add(NotNullRestriction.getDescriptor()) + .add(NonEmptyRestriction.getDescriptor()) + .add(StringListRestriction.getDescriptor()) + .build(); + public static final double TARGET_SNAPSHOT_PERCENTAGE = Configuration.getInstance().getDoubleForClassWithDefault(BarrageUtil.class, "targetSnapshotPercentage", 0.25); @@ -555,9 +573,15 @@ private static void maybeAddInputTableMetadata(@NotNull TableDefinition tableDef final DeephavenTableMetadata metadata = DeephavenTableMetadata.newBuilder().setInputTableMetadata(builder).build(); - final byte[] bytes = metadata.toByteArray(); - final String base64 = Base64.getEncoder().encodeToString(bytes); - putMetadata(schemaMetadata, ATTR_PROTO_METADATA_TAG, base64); + try { + final String json = JsonFormat.printer() + .usingTypeRegistry(INPUT_TABLE_TYPE_REGISTRY) + .print(metadata); + final String base64 = Base64.getEncoder().encodeToString(json.getBytes(java.nio.charset.StandardCharsets.UTF_8)); + putMetadata(schemaMetadata, ATTR_PROTO_METADATA_TAG, base64); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw new UncheckedDeephavenException("Failed to convert InputTableMetadata to JSON", e); + } } @NotNull diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 572d472a9fe..67180c7c387 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -94,27 +94,40 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD InputTableMetadata metadata = new InputTableMetadata(); try { - // Decode base64 to Uint8Array (like Java's Base64.getDecoder().decode()) - consoleLog("parseInputTableMetadata: Decoding base64..."); - Uint8Array bytes = decodeBase64(tableMetadataBase64); - consoleLog("parseInputTableMetadata: Decoded to " + bytes.length + " bytes"); - - // The issue: JavaScript protobuf deserialization fails on google.protobuf.Any types - // Solution: Parse the protobuf manually at the binary level to extract what we need - consoleLog("parseInputTableMetadata: Attempting manual protobuf parsing..."); - jsinterop.base.Any result = parseProtoManually(bytes); - - if (result == null) { - consoleLog("parseInputTableMetadata: Manual parsing returned null"); + // Decode base64 to JSON string using atob + consoleLog("parseInputTableMetadata: Decoding base64 to JSON..."); + String jsonString = DomGlobal.atob(tableMetadataBase64); + consoleLog("parseInputTableMetadata: Decoded JSON string: " + jsonString); + + // Parse JSON string to JavaScript object + consoleLog("parseInputTableMetadata: Parsing JSON..."); + jsinterop.base.Any jsonObject = parseJson(jsonString); + + if (jsonObject == null) { + consoleLog("parseInputTableMetadata: JSON parsing returned null"); + return metadata; + } + + consoleLog("parseInputTableMetadata: Successfully parsed JSON, extracting column info"); + + // Extract inputTableMetadata from the parsed JSON + jsinterop.base.Any inputTableMetadata = getProperty(jsonObject, "inputTableMetadata"); + if (inputTableMetadata == null) { + consoleLog("parseInputTableMetadata: No inputTableMetadata found in JSON"); return metadata; } - consoleLog("parseInputTableMetadata: Successfully parsed, extracting column info"); + // Extract columnInfo from inputTableMetadata + jsinterop.base.Any columnInfoMap = getProperty(inputTableMetadata, "columnInfo"); + if (columnInfoMap == null) { + consoleLog("parseInputTableMetadata: No columnInfo found in inputTableMetadata"); + return metadata; + } - // Extract column restrictions from the manually parsed data + // Extract column restrictions from the JSON data for (ColumnDefinition col : cols) { String columnName = col.getName(); - jsinterop.base.Any columnInfo = getPropertyFromMap(result, columnName); + jsinterop.base.Any columnInfo = getPropertyFromMap(columnInfoMap, columnName); if (columnInfo != null) { consoleLog("parseInputTableMetadata: Found info for column '" + columnName + "'"); @@ -148,240 +161,12 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD return metadata; } - // Decode base64 string to Uint8Array using elemental2 - private static Uint8Array decodeBase64(String base64) { - // Use DomGlobal.atob() to decode base64 to binary string - String binaryString = DomGlobal.atob(base64); - - // Convert binary string to Uint8Array - Uint8Array bytes = new Uint8Array(binaryString.length()); - for (int i = 0; i < binaryString.length(); i++) { - bytes.setAt(i, (double) (binaryString.charAt(i) & 0xff)); - } - return bytes; - } - - private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /*-{ - // Manual protobuf parsing to work around missing google.protobuf.Any - // Based on the pattern from AddToInputTable.java - // Use the BinaryReader from dhinternal.jspb which is the JsInterop wrapper + // Parse JSON string to JavaScript object + private static native jsinterop.base.Any parseJson(String jsonString) /*-{ try { - console.log("parseProtoManually: Starting manual protobuf parsing"); - console.log("parseProtoManually: Bytes length =", bytes.length); - - // Use the JsInterop BinaryReader class - var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); - var reader = new BinaryReader(bytes); - var result = {}; - - // Parse the DeephavenTableMetadata message - // Field 1 is InputTableMetadata - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - console.log("parseProtoManually: Reading field", field); - - if (field === 1) { - // This is the InputTableMetadata field - var inputTableMetadata = {}; - reader.readMessage(inputTableMetadata, function(metadata, rdr) { - // Parse InputTableMetadata - // Field 1 is columnInfoMap (map) - while (rdr.nextField()) { - if (rdr.isEndGroup()) { - break; - } - var subfield = rdr.getFieldNumber(); - console.log("parseProtoManually: InputTableMetadata field", subfield); - - if (subfield === 1) { - // This is the columnInfoMap - var mapEntry = {}; - rdr.readMessage(mapEntry, function(entry, mapReader) { - var key = null; - var value = null; - - while (mapReader.nextField()) { - if (mapReader.isEndGroup()) { - break; - } - var mapField = mapReader.getFieldNumber(); - - if (mapField === 1) { - // Map key (column name) - key = mapReader.readString(); - console.log("parseProtoManually: Column name =", key); - } else if (mapField === 2) { - // Map value (InputTableColumnInfo) - value = {}; - var restrictions = []; - - mapReader.readMessage(value, function(columnInfo, colReader) { - while (colReader.nextField()) { - if (colReader.isEndGroup()) { - break; - } - var colField = colReader.getFieldNumber(); - console.log("parseProtoManually: InputTableColumnInfo field", colField); - - if (colField === 1) { - // kind field - columnInfo.kind = colReader.readEnum(); - } else if (colField === 2) { - // restrictions field (repeated google.protobuf.Any) - // Parse the Any message to extract type_url and value - try { - var anyBytes = colReader.readBytes(); - console.log("parseProtoManually: Got Any bytes, length =", anyBytes.length); - - // Parse the google.protobuf.Any message - // Field 1 = type_url (string) - // Field 2 = value (bytes) - var anyReader = new BinaryReader(anyBytes); - var typeUrl = null; - var valueBytes = null; - - while (anyReader.nextField()) { - if (anyReader.isEndGroup()) { - break; - } - var anyField = anyReader.getFieldNumber(); - if (anyField === 1) { - typeUrl = anyReader.readString(); - console.log("parseProtoManually: Restriction type_url =", typeUrl); - } else if (anyField === 2) { - valueBytes = anyReader.readBytes(); - console.log("parseProtoManually: Restriction value bytes length =", valueBytes.length); - } - } - - // Now parse the actual restriction based on type_url - var restriction = @io.deephaven.web.client.api.barrage.WebBarrageUtils::parseRestriction(*)(typeUrl, valueBytes); - if (restriction) { - restrictions.push(restriction); - } - } catch (e) { - console.warn("parseProtoManually: Failed to parse restriction:", e); - } - } - } - columnInfo.restrictions = restrictions; - }); - } - } - - if (key !== null && value !== null) { - if (!metadata.columnInfoMap) { - metadata.columnInfoMap = {}; - } - metadata.columnInfoMap[key] = value; - console.log("parseProtoManually: Added column", key, "with", value.restrictions ? value.restrictions.length : 0, "restrictions"); - } - }); - } - } - }); - - result = inputTableMetadata.columnInfoMap || {}; - console.log("parseProtoManually: Parsed", Object.keys(result).length, "columns"); - } - } - - return result; - + return JSON.parse(jsonString); } catch (e) { - console.error("parseProtoManually: Failed to manually parse protobuf:", e); - console.error("Stack:", e.stack); - return null; - } - }-*/; - - private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8Array valueBytes) /*-{ - // Parse specific restriction types based on typeUrl - // Types from inputtable.proto: - // - IntegerRangeRestriction - // - DoubleRangeRestriction - // - NotNullRestriction - // - NonEmptyRestriction - // - StringListRestriction - - try { - console.log("parseRestriction: Parsing restriction type:", typeUrl); - - var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); - var reader = new BinaryReader(valueBytes); - - // Extract the message type from the type URL - // Format: "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" - // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" - var typeName = typeUrl.substring(typeUrl.lastIndexOf('/') + 1); - var shortName = typeName.substring(typeName.lastIndexOf('.') + 1); - console.log("parseRestriction: Message type:", shortName); - - var restriction = { - type: shortName, - typeUrl: typeUrl - }; - - if (shortName === 'IntegerRangeRestriction') { - // Field 1 = min_inclusive (int64) - // Field 2 = max_inclusive (int64) - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - restriction.minInclusive = reader.readInt64(); - console.log("parseRestriction: minInclusive =", restriction.minInclusive); - } else if (field === 2) { - restriction.maxInclusive = reader.readInt64(); - console.log("parseRestriction: maxInclusive =", restriction.maxInclusive); - } - } - } else if (shortName === 'DoubleRangeRestriction') { - // Field 1 = min_inclusive (double) - // Field 2 = max_inclusive (double) - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - restriction.minInclusive = reader.readDouble(); - console.log("parseRestriction: minInclusive =", restriction.minInclusive); - } else if (field === 2) { - restriction.maxInclusive = reader.readDouble(); - console.log("parseRestriction: maxInclusive =", restriction.maxInclusive); - } - } - } else if (shortName === 'NotNullRestriction') { - // No fields - just the type - restriction.notNull = true; - console.log("parseRestriction: NotNull restriction"); - } else if (shortName === 'NonEmptyRestriction') { - // No fields - just the type - restriction.nonEmpty = true; - console.log("parseRestriction: NonEmpty restriction"); - } else if (shortName === 'StringListRestriction') { - // Field 1 = allowed_values (repeated string) - restriction.allowedValues = []; - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - var value = reader.readString(); - restriction.allowedValues.push(value); - console.log("parseRestriction: allowed value =", value); - } - } - } else { - console.warn("parseRestriction: Unknown restriction type:", shortName); - restriction.raw = valueBytes; - } - - return restriction; - - } catch (e) { - console.error("parseRestriction: Failed to parse restriction:", e); + console.error("Failed to parse JSON:", e); return null; } }-*/; @@ -400,18 +185,6 @@ private static native jsinterop.base.Any getProperty(jsinterop.base.Any obj, Str return obj[propertyName]; }-*/; - private static native jsinterop.base.Any getMapEntry(jsinterop.base.Any map, String key) /*-{ - if (map == null) return null; - if (typeof map.get === 'function') { - return map.get(key); - } - if (typeof map.getMap === 'function') { - var m = map.getMap(); - return m ? m[key] : null; - } - return map[key]; - }-*/; - private static native boolean isArray(jsinterop.base.Any obj) /*-{ return Array.isArray(obj); }-*/; @@ -424,16 +197,6 @@ private static native void consoleError(String message, Exception e) /*-{ console.error(message, e); }-*/; - private static native void logProtoObject(String label, jsinterop.base.Any obj) /*-{ - console.log(label + ":", obj); - if (obj != null) { - console.log(label + " keys:", Object.keys(obj)); - console.log(label + " methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(obj)).filter(function(name) { - return typeof obj[name] === 'function'; - })); - } - }-*/; - private static ColumnDefinition[] readColumnDefinitions(Schema schema) { ColumnDefinition[] cols = new ColumnDefinition[(int) schema.fieldsLength()]; for (int i = 0; i < schema.fieldsLength(); i++) { From 69391277aa7f4097a56ccefc96b6c0893212745e Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 7 Apr 2026 12:59:50 -0600 Subject: [PATCH 05/35] Revert "send restrictions as json" This reverts commit 180bc15306c02c25ad9df09a09e5057bce46ee9b. --- .../engine/util/input/InputTableUpdater.java | 13 +- extensions/barrage/build.gradle | 1 - .../extensions/barrage/util/BarrageUtil.java | 30 +- .../client/api/barrage/WebBarrageUtils.java | 301 ++++++++++++++++-- 4 files changed, 281 insertions(+), 64 deletions(-) diff --git a/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java b/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java index e09f774997c..450229e3d78 100644 --- a/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java +++ b/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java @@ -57,12 +57,17 @@ default List getValueNames() { } /** - * If there are client-side defined restrictions on this column; return them as a JSON string to be interpreted by - * the client for properly displaying the edit field. + * If there are client-side defined restrictions on this column, return them as a list of protobuf Any messages. + * These restrictions are used by the client for properly displaying and validating the edit field. + * + *

+ * The restrictions are packed as {@code google.protobuf.Any} messages, which allows for different restriction + * types (e.g., {@code IntegerRangeRestriction}, {@code DoubleRangeRestriction}, {@code StringListRestriction}, + * etc.) to be sent to the client. The client is responsible for unpacking and interpreting these restrictions. * * @param columnName the column name to query - * @return a string representing the restrictions for this column, or null if no client-side restrictions are - * supplied for this column + * @return a list of protobuf Any messages representing the restrictions for this column, or null if no + * client-side restrictions are supplied for this column */ @Nullable default List getColumnRestrictions(final String columnName) { diff --git a/extensions/barrage/build.gradle b/extensions/barrage/build.gradle index 350e81803c4..dff44595c32 100644 --- a/extensions/barrage/build.gradle +++ b/extensions/barrage/build.gradle @@ -15,7 +15,6 @@ dependencies { api project(':engine-table') implementation project(':proto:proto-backplane-grpc-flight') - implementation libs.protobuf.java.util implementation project(':log-factory') api libs.deephaven.barrage.format implementation libs.hdrhistogram diff --git a/extensions/barrage/src/main/java/io/deephaven/extensions/barrage/util/BarrageUtil.java b/extensions/barrage/src/main/java/io/deephaven/extensions/barrage/util/BarrageUtil.java index dcb37270134..da8c32a5df3 100755 --- a/extensions/barrage/src/main/java/io/deephaven/extensions/barrage/util/BarrageUtil.java +++ b/extensions/barrage/src/main/java/io/deephaven/extensions/barrage/util/BarrageUtil.java @@ -8,8 +8,6 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.ByteStringAccess; -import com.google.protobuf.util.JsonFormat; -import com.google.protobuf.util.JsonFormat.TypeRegistry; import com.google.rpc.Code; import io.deephaven.UncheckedDeephavenException; import io.deephaven.api.util.NameValidator; @@ -55,14 +53,9 @@ import io.deephaven.internal.log.LoggerFactory; import io.deephaven.io.logger.Logger; import io.deephaven.proto.backplane.grpc.DeephavenTableMetadata; -import io.deephaven.proto.backplane.grpc.DoubleRangeRestriction; import io.deephaven.proto.backplane.grpc.ExportedTableCreationResponse; import io.deephaven.proto.backplane.grpc.InputTableMetadata; import io.deephaven.proto.backplane.grpc.InputTableColumnInfo; -import io.deephaven.proto.backplane.grpc.IntegerRangeRestriction; -import io.deephaven.proto.backplane.grpc.NonEmptyRestriction; -import io.deephaven.proto.backplane.grpc.NotNullRestriction; -import io.deephaven.proto.backplane.grpc.StringListRestriction; import io.deephaven.proto.flight.util.MessageHelper; import io.deephaven.proto.flight.util.SchemaHelper; import io.deephaven.proto.util.Exceptions; @@ -135,17 +128,6 @@ public class BarrageUtil { private static final Logger log = LoggerFactory.getLogger(BarrageUtil.class); - /** - * TypeRegistry for JSON serialization of InputTableMetadata with restriction types. - */ - private static final TypeRegistry INPUT_TABLE_TYPE_REGISTRY = TypeRegistry.newBuilder() - .add(IntegerRangeRestriction.getDescriptor()) - .add(DoubleRangeRestriction.getDescriptor()) - .add(NotNullRestriction.getDescriptor()) - .add(NonEmptyRestriction.getDescriptor()) - .add(StringListRestriction.getDescriptor()) - .build(); - public static final double TARGET_SNAPSHOT_PERCENTAGE = Configuration.getInstance().getDoubleForClassWithDefault(BarrageUtil.class, "targetSnapshotPercentage", 0.25); @@ -573,15 +555,9 @@ private static void maybeAddInputTableMetadata(@NotNull TableDefinition tableDef final DeephavenTableMetadata metadata = DeephavenTableMetadata.newBuilder().setInputTableMetadata(builder).build(); - try { - final String json = JsonFormat.printer() - .usingTypeRegistry(INPUT_TABLE_TYPE_REGISTRY) - .print(metadata); - final String base64 = Base64.getEncoder().encodeToString(json.getBytes(java.nio.charset.StandardCharsets.UTF_8)); - putMetadata(schemaMetadata, ATTR_PROTO_METADATA_TAG, base64); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw new UncheckedDeephavenException("Failed to convert InputTableMetadata to JSON", e); - } + final byte[] bytes = metadata.toByteArray(); + final String base64 = Base64.getEncoder().encodeToString(bytes); + putMetadata(schemaMetadata, ATTR_PROTO_METADATA_TAG, base64); } @NotNull diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 67180c7c387..572d472a9fe 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -94,40 +94,27 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD InputTableMetadata metadata = new InputTableMetadata(); try { - // Decode base64 to JSON string using atob - consoleLog("parseInputTableMetadata: Decoding base64 to JSON..."); - String jsonString = DomGlobal.atob(tableMetadataBase64); - consoleLog("parseInputTableMetadata: Decoded JSON string: " + jsonString); - - // Parse JSON string to JavaScript object - consoleLog("parseInputTableMetadata: Parsing JSON..."); - jsinterop.base.Any jsonObject = parseJson(jsonString); - - if (jsonObject == null) { - consoleLog("parseInputTableMetadata: JSON parsing returned null"); - return metadata; - } - - consoleLog("parseInputTableMetadata: Successfully parsed JSON, extracting column info"); - - // Extract inputTableMetadata from the parsed JSON - jsinterop.base.Any inputTableMetadata = getProperty(jsonObject, "inputTableMetadata"); - if (inputTableMetadata == null) { - consoleLog("parseInputTableMetadata: No inputTableMetadata found in JSON"); + // Decode base64 to Uint8Array (like Java's Base64.getDecoder().decode()) + consoleLog("parseInputTableMetadata: Decoding base64..."); + Uint8Array bytes = decodeBase64(tableMetadataBase64); + consoleLog("parseInputTableMetadata: Decoded to " + bytes.length + " bytes"); + + // The issue: JavaScript protobuf deserialization fails on google.protobuf.Any types + // Solution: Parse the protobuf manually at the binary level to extract what we need + consoleLog("parseInputTableMetadata: Attempting manual protobuf parsing..."); + jsinterop.base.Any result = parseProtoManually(bytes); + + if (result == null) { + consoleLog("parseInputTableMetadata: Manual parsing returned null"); return metadata; } - // Extract columnInfo from inputTableMetadata - jsinterop.base.Any columnInfoMap = getProperty(inputTableMetadata, "columnInfo"); - if (columnInfoMap == null) { - consoleLog("parseInputTableMetadata: No columnInfo found in inputTableMetadata"); - return metadata; - } + consoleLog("parseInputTableMetadata: Successfully parsed, extracting column info"); - // Extract column restrictions from the JSON data + // Extract column restrictions from the manually parsed data for (ColumnDefinition col : cols) { String columnName = col.getName(); - jsinterop.base.Any columnInfo = getPropertyFromMap(columnInfoMap, columnName); + jsinterop.base.Any columnInfo = getPropertyFromMap(result, columnName); if (columnInfo != null) { consoleLog("parseInputTableMetadata: Found info for column '" + columnName + "'"); @@ -161,12 +148,240 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD return metadata; } - // Parse JSON string to JavaScript object - private static native jsinterop.base.Any parseJson(String jsonString) /*-{ + // Decode base64 string to Uint8Array using elemental2 + private static Uint8Array decodeBase64(String base64) { + // Use DomGlobal.atob() to decode base64 to binary string + String binaryString = DomGlobal.atob(base64); + + // Convert binary string to Uint8Array + Uint8Array bytes = new Uint8Array(binaryString.length()); + for (int i = 0; i < binaryString.length(); i++) { + bytes.setAt(i, (double) (binaryString.charAt(i) & 0xff)); + } + return bytes; + } + + private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /*-{ + // Manual protobuf parsing to work around missing google.protobuf.Any + // Based on the pattern from AddToInputTable.java + // Use the BinaryReader from dhinternal.jspb which is the JsInterop wrapper try { - return JSON.parse(jsonString); + console.log("parseProtoManually: Starting manual protobuf parsing"); + console.log("parseProtoManually: Bytes length =", bytes.length); + + // Use the JsInterop BinaryReader class + var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); + var reader = new BinaryReader(bytes); + var result = {}; + + // Parse the DeephavenTableMetadata message + // Field 1 is InputTableMetadata + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + console.log("parseProtoManually: Reading field", field); + + if (field === 1) { + // This is the InputTableMetadata field + var inputTableMetadata = {}; + reader.readMessage(inputTableMetadata, function(metadata, rdr) { + // Parse InputTableMetadata + // Field 1 is columnInfoMap (map) + while (rdr.nextField()) { + if (rdr.isEndGroup()) { + break; + } + var subfield = rdr.getFieldNumber(); + console.log("parseProtoManually: InputTableMetadata field", subfield); + + if (subfield === 1) { + // This is the columnInfoMap + var mapEntry = {}; + rdr.readMessage(mapEntry, function(entry, mapReader) { + var key = null; + var value = null; + + while (mapReader.nextField()) { + if (mapReader.isEndGroup()) { + break; + } + var mapField = mapReader.getFieldNumber(); + + if (mapField === 1) { + // Map key (column name) + key = mapReader.readString(); + console.log("parseProtoManually: Column name =", key); + } else if (mapField === 2) { + // Map value (InputTableColumnInfo) + value = {}; + var restrictions = []; + + mapReader.readMessage(value, function(columnInfo, colReader) { + while (colReader.nextField()) { + if (colReader.isEndGroup()) { + break; + } + var colField = colReader.getFieldNumber(); + console.log("parseProtoManually: InputTableColumnInfo field", colField); + + if (colField === 1) { + // kind field + columnInfo.kind = colReader.readEnum(); + } else if (colField === 2) { + // restrictions field (repeated google.protobuf.Any) + // Parse the Any message to extract type_url and value + try { + var anyBytes = colReader.readBytes(); + console.log("parseProtoManually: Got Any bytes, length =", anyBytes.length); + + // Parse the google.protobuf.Any message + // Field 1 = type_url (string) + // Field 2 = value (bytes) + var anyReader = new BinaryReader(anyBytes); + var typeUrl = null; + var valueBytes = null; + + while (anyReader.nextField()) { + if (anyReader.isEndGroup()) { + break; + } + var anyField = anyReader.getFieldNumber(); + if (anyField === 1) { + typeUrl = anyReader.readString(); + console.log("parseProtoManually: Restriction type_url =", typeUrl); + } else if (anyField === 2) { + valueBytes = anyReader.readBytes(); + console.log("parseProtoManually: Restriction value bytes length =", valueBytes.length); + } + } + + // Now parse the actual restriction based on type_url + var restriction = @io.deephaven.web.client.api.barrage.WebBarrageUtils::parseRestriction(*)(typeUrl, valueBytes); + if (restriction) { + restrictions.push(restriction); + } + } catch (e) { + console.warn("parseProtoManually: Failed to parse restriction:", e); + } + } + } + columnInfo.restrictions = restrictions; + }); + } + } + + if (key !== null && value !== null) { + if (!metadata.columnInfoMap) { + metadata.columnInfoMap = {}; + } + metadata.columnInfoMap[key] = value; + console.log("parseProtoManually: Added column", key, "with", value.restrictions ? value.restrictions.length : 0, "restrictions"); + } + }); + } + } + }); + + result = inputTableMetadata.columnInfoMap || {}; + console.log("parseProtoManually: Parsed", Object.keys(result).length, "columns"); + } + } + + return result; + } catch (e) { - console.error("Failed to parse JSON:", e); + console.error("parseProtoManually: Failed to manually parse protobuf:", e); + console.error("Stack:", e.stack); + return null; + } + }-*/; + + private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8Array valueBytes) /*-{ + // Parse specific restriction types based on typeUrl + // Types from inputtable.proto: + // - IntegerRangeRestriction + // - DoubleRangeRestriction + // - NotNullRestriction + // - NonEmptyRestriction + // - StringListRestriction + + try { + console.log("parseRestriction: Parsing restriction type:", typeUrl); + + var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); + var reader = new BinaryReader(valueBytes); + + // Extract the message type from the type URL + // Format: "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" + // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" + var typeName = typeUrl.substring(typeUrl.lastIndexOf('/') + 1); + var shortName = typeName.substring(typeName.lastIndexOf('.') + 1); + console.log("parseRestriction: Message type:", shortName); + + var restriction = { + type: shortName, + typeUrl: typeUrl + }; + + if (shortName === 'IntegerRangeRestriction') { + // Field 1 = min_inclusive (int64) + // Field 2 = max_inclusive (int64) + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + restriction.minInclusive = reader.readInt64(); + console.log("parseRestriction: minInclusive =", restriction.minInclusive); + } else if (field === 2) { + restriction.maxInclusive = reader.readInt64(); + console.log("parseRestriction: maxInclusive =", restriction.maxInclusive); + } + } + } else if (shortName === 'DoubleRangeRestriction') { + // Field 1 = min_inclusive (double) + // Field 2 = max_inclusive (double) + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + restriction.minInclusive = reader.readDouble(); + console.log("parseRestriction: minInclusive =", restriction.minInclusive); + } else if (field === 2) { + restriction.maxInclusive = reader.readDouble(); + console.log("parseRestriction: maxInclusive =", restriction.maxInclusive); + } + } + } else if (shortName === 'NotNullRestriction') { + // No fields - just the type + restriction.notNull = true; + console.log("parseRestriction: NotNull restriction"); + } else if (shortName === 'NonEmptyRestriction') { + // No fields - just the type + restriction.nonEmpty = true; + console.log("parseRestriction: NonEmpty restriction"); + } else if (shortName === 'StringListRestriction') { + // Field 1 = allowed_values (repeated string) + restriction.allowedValues = []; + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + var value = reader.readString(); + restriction.allowedValues.push(value); + console.log("parseRestriction: allowed value =", value); + } + } + } else { + console.warn("parseRestriction: Unknown restriction type:", shortName); + restriction.raw = valueBytes; + } + + return restriction; + + } catch (e) { + console.error("parseRestriction: Failed to parse restriction:", e); return null; } }-*/; @@ -185,6 +400,18 @@ private static native jsinterop.base.Any getProperty(jsinterop.base.Any obj, Str return obj[propertyName]; }-*/; + private static native jsinterop.base.Any getMapEntry(jsinterop.base.Any map, String key) /*-{ + if (map == null) return null; + if (typeof map.get === 'function') { + return map.get(key); + } + if (typeof map.getMap === 'function') { + var m = map.getMap(); + return m ? m[key] : null; + } + return map[key]; + }-*/; + private static native boolean isArray(jsinterop.base.Any obj) /*-{ return Array.isArray(obj); }-*/; @@ -197,6 +424,16 @@ private static native void consoleError(String message, Exception e) /*-{ console.error(message, e); }-*/; + private static native void logProtoObject(String label, jsinterop.base.Any obj) /*-{ + console.log(label + ":", obj); + if (obj != null) { + console.log(label + " keys:", Object.keys(obj)); + console.log(label + " methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(obj)).filter(function(name) { + return typeof obj[name] === 'function'; + })); + } + }-*/; + private static ColumnDefinition[] readColumnDefinitions(Schema schema) { ColumnDefinition[] cols = new ColumnDefinition[(int) schema.fieldsLength()]; for (int i = 0; i < schema.fieldsLength(); i++) { From 75bf43ea2d547c73694a85cb981caaa47fe9d553 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 8 Apr 2026 07:28:20 -0600 Subject: [PATCH 06/35] abstract base class for validating input tables --- .../AbstractBaseValidatingInputTable.java | 100 ++++++++++++++++++ .../RangeValidatingInputTable.java | 57 +--------- 2 files changed, 102 insertions(+), 55 deletions(-) create mode 100644 server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java new file mode 100644 index 00000000000..c7635e23a05 --- /dev/null +++ b/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java @@ -0,0 +1,100 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.server.table.inputtables; + +import com.google.protobuf.Any; +import io.deephaven.engine.table.Table; +import io.deephaven.engine.table.TableDefinition; +import io.deephaven.engine.util.input.InputTableStatusListener; +import io.deephaven.engine.util.input.InputTableUpdater; +import io.deephaven.util.annotations.TestUseOnly; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.List; + +/** + * An abstract base class for {@link InputTableUpdater} implementations that wrap an existing input table. + * + *

+ * This class provides a default implementation for most methods by delegating to the wrapped input table. + * Subclasses should override {@link #getColumnRestrictions(String)} and {@link #validateAddOrModify(Table)} + * to provide custom validation logic. + *

+ * + *

+ * This class is intended for testing and demonstrating validation functionality, it is not production ready and may + * be changed or removed at any time. + *

+ */ +@TestUseOnly +public abstract class AbstractBaseValidatingInputTable implements InputTableUpdater { + protected final InputTableUpdater wrapped; + + /** + * Construct a new validating input table that wraps the given input table. + * + * @param wrapped the input table to wrap + */ + protected AbstractBaseValidatingInputTable(InputTableUpdater wrapped) { + this.wrapped = wrapped; + } + + @Override + public List getKeyNames() { + return wrapped.getKeyNames(); + } + + @Override + public List getValueNames() { + return wrapped.getValueNames(); + } + + @Override + public abstract @Nullable List getColumnRestrictions(String columnName); + + @Override + public TableDefinition getTableDefinition() { + return wrapped.getTableDefinition(); + } + + @Override + public abstract void validateAddOrModify(Table tableToApply); + + @Override + public void validateDelete(Table tableToDelete) { + wrapped.validateDelete(tableToDelete); + } + + @Override + public void add(Table newData) throws IOException { + wrapped.add(newData); + } + + @Override + public void addAsync(Table newData, InputTableStatusListener listener) { + wrapped.addAsync(newData, listener); + } + + @Override + public void delete(Table table) throws IOException { + wrapped.delete(table); + } + + @Override + public void deleteAsync(Table table, InputTableStatusListener listener) { + wrapped.deleteAsync(table, listener); + } + + @Override + public boolean isKey(String columnName) { + return wrapped.isKey(columnName); + } + + @Override + public boolean hasColumn(String columnName) { + return wrapped.hasColumn(columnName); + } +} + diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java index 01dadfb7b0f..d45679ab1da 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java @@ -6,9 +6,6 @@ import com.google.protobuf.Any; import io.deephaven.engine.primitive.iterator.CloseablePrimitiveIteratorOfInt; import io.deephaven.engine.table.Table; -import io.deephaven.engine.table.TableDefinition; -import io.deephaven.engine.table.impl.UpdatableTable; -import io.deephaven.engine.util.input.InputTableStatusListener; import io.deephaven.engine.util.input.InputTableUpdater; import io.deephaven.engine.util.input.InputTableValidationException; import io.deephaven.engine.util.input.StructuredErrorImpl; @@ -17,7 +14,6 @@ import io.deephaven.util.mutable.MutableInt; import org.jetbrains.annotations.Nullable; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -37,8 +33,7 @@ *

*/ @TestUseOnly -public class RangeValidatingInputTable implements InputTableUpdater { - private final InputTableUpdater wrapped; +public class RangeValidatingInputTable extends AbstractBaseValidatingInputTable { private final String column; private final int min; private final int max; @@ -66,7 +61,7 @@ private RangeValidatingInputTable(InputTableUpdater wrapped, final String column, final int min, final int max) { - this.wrapped = wrapped; + super(wrapped); this.column = column; final Class dataType = getTableDefinition().getColumn(column).getDataType(); if (dataType != int.class) { @@ -76,15 +71,6 @@ private RangeValidatingInputTable(InputTableUpdater wrapped, this.max = max; } - @Override - public List getKeyNames() { - return wrapped.getKeyNames(); - } - - @Override - public List getValueNames() { - return wrapped.getValueNames(); - } @Override public @Nullable List getColumnRestrictions(String columnName) { @@ -103,10 +89,6 @@ public List getValueNames() { return result; } - @Override - public TableDefinition getTableDefinition() { - return wrapped.getTableDefinition(); - } @Override public void validateAddOrModify(Table tableToApply) { @@ -128,39 +110,4 @@ public void validateAddOrModify(Table tableToApply) { wrapped.validateAddOrModify(tableToApply); } - - @Override - public void validateDelete(Table tableToDelete) { - wrapped.validateDelete(tableToDelete); - } - - @Override - public void add(Table newData) throws IOException { - wrapped.add(newData); - } - - @Override - public void addAsync(Table newData, InputTableStatusListener listener) { - wrapped.addAsync(newData, listener); - } - - @Override - public void delete(Table table) throws IOException { - wrapped.delete(table); - } - - @Override - public void deleteAsync(Table table, InputTableStatusListener listener) { - wrapped.deleteAsync(table, listener); - } - - @Override - public boolean isKey(String columnName) { - return wrapped.isKey(columnName); - } - - @Override - public boolean hasColumn(String columnName) { - return wrapped.hasColumn(columnName); - } } From 89ac34ca79e807e3c216d2392f0af0be36937f86 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 8 Apr 2026 08:28:48 -0600 Subject: [PATCH 07/35] additional validators --- .../DoubleRangeValidatingInputTable.java | 114 ++++++++++++++++++ .../NonEmptyValidatingInputTable.java | 106 ++++++++++++++++ .../NotNullValidatingInputTable.java | 102 ++++++++++++++++ .../StringListValidatingInputTable.java | 114 ++++++++++++++++++ 4 files changed, 436 insertions(+) create mode 100644 server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java create mode 100644 server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java create mode 100644 server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java create mode 100644 server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java new file mode 100644 index 00000000000..6d4160b9bc6 --- /dev/null +++ b/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java @@ -0,0 +1,114 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.server.table.inputtables; + +import com.google.protobuf.Any; +import io.deephaven.engine.primitive.iterator.CloseablePrimitiveIteratorOfDouble; +import io.deephaven.engine.table.Table; +import io.deephaven.engine.util.input.InputTableUpdater; +import io.deephaven.engine.util.input.InputTableValidationException; +import io.deephaven.engine.util.input.StructuredErrorImpl; +import io.deephaven.proto.backplane.grpc.DoubleRangeRestriction; +import io.deephaven.util.annotations.TestUseOnly; +import io.deephaven.util.mutable.MutableInt; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * This is an example of an {@link InputTableUpdater} that validates that the values in a Double column are within a + * given range. + * + *

+ * This class wraps an existing input table, and before performing the underlying validation performs its own validation + * on the range of the column. + *

+ * + *

+ * This class is intended for testing and demonstrating validation functionality, it is not production ready and may + * be changed or removed at any time. + *

+ */ +@TestUseOnly +public class DoubleRangeValidatingInputTable extends AbstractBaseValidatingInputTable { + private final String column; + private final double min; + private final double max; + + /** + * Wrap {@code input}, which must be an input table into a new input table that validates that the values in + * {@code column} are within the range {@code ([min, max]}. + * + * @param input the table to wrap + * @param column the column to validate, must be a double type + * @param min the minimum value allowed, inclusive + * @param max the maximum value allowed, inclusive + * @return a new input table that validates the range of {@code column} + */ + public static Table make(Table input, final String column, + final double min, + final double max) { + final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); + final DoubleRangeValidatingInputTable validatedUpdater = new DoubleRangeValidatingInputTable(updater, column, min, max); + return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + } + + + private DoubleRangeValidatingInputTable(InputTableUpdater wrapped, + final String column, + final double min, + final double max) { + super(wrapped); + this.column = column; + final Class dataType = getTableDefinition().getColumn(column).getDataType(); + if (dataType != double.class) { + throw new IllegalArgumentException("Range column must be a double, but " + column + " is " + dataType); + } + this.min = min; + this.max = max; + } + + + @Override + public @Nullable List getColumnRestrictions(String columnName) { + final List columnRestrictions = wrapped.getColumnRestrictions(columnName); + if (!columnName.equals(column)) { + return columnRestrictions; + } + + final List result = new ArrayList<>(); + if (columnRestrictions != null) { + result.addAll(columnRestrictions); + } + final DoubleRangeRestriction rangeRestriction = + DoubleRangeRestriction.newBuilder().setMinInclusive(min).setMaxInclusive(max).build(); + result.add(Any.pack(rangeRestriction, "docs.deephaven.io")); + return result; + } + + + @Override + public void validateAddOrModify(Table tableToApply) { + final List errors = new ArrayList<>(); + final MutableInt position = new MutableInt(0); + try (final CloseablePrimitiveIteratorOfDouble vals = tableToApply.doubleColumnIterator(column)) { + vals.forEachRemaining((double val) -> { + if (val < min || val > max) { + errors.add(new StructuredErrorImpl( + "Value out of range: " + val + " must be between " + min + " and " + max + " inclusive", + column, position.get())); + } + position.increment(); + }); + } + if (!errors.isEmpty()) { + throw new InputTableValidationException(errors); + } + + wrapped.validateAddOrModify(tableToApply); + } +} + diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java new file mode 100644 index 00000000000..8052401ac52 --- /dev/null +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java @@ -0,0 +1,106 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.server.table.inputtables; + +import com.google.protobuf.Any; +import io.deephaven.engine.rowset.RowSequence; +import io.deephaven.engine.table.ColumnSource; +import io.deephaven.engine.table.Table; +import io.deephaven.engine.util.input.InputTableUpdater; +import io.deephaven.engine.util.input.InputTableValidationException; +import io.deephaven.engine.util.input.StructuredErrorImpl; +import io.deephaven.proto.backplane.grpc.NonEmptyRestriction; +import io.deephaven.util.annotations.TestUseOnly; +import io.deephaven.util.mutable.MutableInt; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * This is an example of an {@link InputTableUpdater} that validates that the values in a String column are not empty. + * + *

+ * This class wraps an existing input table, and before performing the underlying validation performs its own validation + * that the column does not contain empty strings. + *

+ * + *

+ * This class is intended for testing and demonstrating validation functionality, it is not production ready and may + * be changed or removed at any time. + *

+ */ +@TestUseOnly +public class NonEmptyValidatingInputTable extends AbstractBaseValidatingInputTable { + private final String column; + + /** + * Wrap {@code input}, which must be an input table into a new input table that validates that the values in + * {@code column} are not empty. + * + * @param input the table to wrap + * @param column the column to validate, must be a String type + * @return a new input table that validates {@code column} is not empty + */ + public static Table make(Table input, final String column) { + final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); + final NonEmptyValidatingInputTable validatedUpdater = new NonEmptyValidatingInputTable(updater, column); + return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + } + + + private NonEmptyValidatingInputTable(InputTableUpdater wrapped, final String column) { + super(wrapped); + this.column = column; + final Class dataType = getTableDefinition().getColumn(column).getDataType(); + if (dataType != String.class) { + throw new IllegalArgumentException("Non-empty validation only applies to String columns, but " + column + " is " + dataType); + } + } + + + @Override + public @Nullable List getColumnRestrictions(String columnName) { + final List columnRestrictions = wrapped.getColumnRestrictions(columnName); + if (!columnName.equals(column)) { + return columnRestrictions; + } + + final List result = new ArrayList<>(); + if (columnRestrictions != null) { + result.addAll(columnRestrictions); + } + final NonEmptyRestriction nonEmptyRestriction = NonEmptyRestriction.newBuilder().build(); + result.add(Any.pack(nonEmptyRestriction, "docs.deephaven.io")); + return result; + } + + + @Override + public void validateAddOrModify(Table tableToApply) { + final List errors = new ArrayList<>(); + final MutableInt position = new MutableInt(0); + final ColumnSource columnSource = tableToApply.getColumnSource(column, String.class); + + try (final RowSequence rowSequence = tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { + rowSequence.forAllRowKeys(rowKey -> { + final String value = columnSource.get(rowKey); + if (value != null && value.isEmpty()) { + errors.add(new StructuredErrorImpl( + "Value must not be empty", + column, position.get())); + } + position.increment(); + }); + } + + if (!errors.isEmpty()) { + throw new InputTableValidationException(errors); + } + + wrapped.validateAddOrModify(tableToApply); + } +} + diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java new file mode 100644 index 00000000000..f277c0b8469 --- /dev/null +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java @@ -0,0 +1,102 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.server.table.inputtables; + +import com.google.protobuf.Any; +import io.deephaven.engine.rowset.RowSequence; +import io.deephaven.engine.table.ColumnSource; +import io.deephaven.engine.table.Table; +import io.deephaven.engine.util.input.InputTableUpdater; +import io.deephaven.engine.util.input.InputTableValidationException; +import io.deephaven.engine.util.input.StructuredErrorImpl; +import io.deephaven.proto.backplane.grpc.NotNullRestriction; +import io.deephaven.util.annotations.TestUseOnly; +import io.deephaven.util.mutable.MutableInt; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * This is an example of an {@link InputTableUpdater} that validates that the values in a column are not null. + * + *

+ * This class wraps an existing input table, and before performing the underlying validation performs its own validation + * that the column does not contain null values. + *

+ * + *

+ * This class is intended for testing and demonstrating validation functionality, it is not production ready and may + * be changed or removed at any time. + *

+ */ +@TestUseOnly +public class NotNullValidatingInputTable extends AbstractBaseValidatingInputTable { + private final String column; + + /** + * Wrap {@code input}, which must be an input table into a new input table that validates that the values in + * {@code column} are not null. + * + * @param input the table to wrap + * @param column the column to validate + * @return a new input table that validates {@code column} is not null + */ + public static Table make(Table input, final String column) { + final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); + final NotNullValidatingInputTable validatedUpdater = new NotNullValidatingInputTable(updater, column); + return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + } + + + private NotNullValidatingInputTable(InputTableUpdater wrapped, final String column) { + super(wrapped); + this.column = column; + } + + + @Override + public @Nullable List getColumnRestrictions(String columnName) { + final List columnRestrictions = wrapped.getColumnRestrictions(columnName); + if (!columnName.equals(column)) { + return columnRestrictions; + } + + final List result = new ArrayList<>(); + if (columnRestrictions != null) { + result.addAll(columnRestrictions); + } + final NotNullRestriction notNullRestriction = NotNullRestriction.newBuilder().build(); + result.add(Any.pack(notNullRestriction, "docs.deephaven.io")); + return result; + } + + + @Override + public void validateAddOrModify(Table tableToApply) { + final List errors = new ArrayList<>(); + final MutableInt position = new MutableInt(0); + final ColumnSource columnSource = tableToApply.getColumnSource(column); + + try (final RowSequence rowSequence = tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { + rowSequence.forAllRowKeys(rowKey -> { + final Object value = columnSource.get(rowKey); + if (value == null) { + errors.add(new StructuredErrorImpl( + "Value must not be null", + column, position.get())); + } + position.increment(); + }); + } + + if (!errors.isEmpty()) { + throw new InputTableValidationException(errors); + } + + wrapped.validateAddOrModify(tableToApply); + } +} + diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java new file mode 100644 index 00000000000..2a6e0d6ec44 --- /dev/null +++ b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java @@ -0,0 +1,114 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.server.table.inputtables; + +import com.google.protobuf.Any; +import io.deephaven.engine.rowset.RowSequence; +import io.deephaven.engine.table.ColumnSource; +import io.deephaven.engine.table.Table; +import io.deephaven.engine.util.input.InputTableUpdater; +import io.deephaven.engine.util.input.InputTableValidationException; +import io.deephaven.engine.util.input.StructuredErrorImpl; +import io.deephaven.proto.backplane.grpc.StringListRestriction; +import io.deephaven.util.annotations.TestUseOnly; +import io.deephaven.util.mutable.MutableInt; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This is an example of an {@link InputTableUpdater} that validates that the values in a String column belong to a + * given set of allowed values. + * + *

+ * This class wraps an existing input table, and before performing the underlying validation performs its own validation + * that the column values are in the allowed set (or null). + *

+ * + *

+ * This class is intended for testing and demonstrating validation functionality, it is not production ready and may + * be changed or removed at any time. + *

+ */ +@TestUseOnly +public class StringListValidatingInputTable extends AbstractBaseValidatingInputTable { + private final String column; + private final Set allowedValues; + private final List allowedValuesList; + + /** + * Wrap {@code input}, which must be an input table into a new input table that validates that the values in + * {@code column} belong to the given set of allowed values. + * + * @param input the table to wrap + * @param column the column to validate, must be a String type + * @param allowedValues the array of allowed values + * @return a new input table that validates {@code column} values are in the allowed set + */ + public static Table make(Table input, final String column, final String... allowedValues) { + final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); + final StringListValidatingInputTable validatedUpdater = new StringListValidatingInputTable(updater, column, allowedValues); + return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + } + + + private StringListValidatingInputTable(InputTableUpdater wrapped, final String column, final String... allowedValues) { + super(wrapped); + this.column = column; + this.allowedValuesList = List.of(allowedValues); + this.allowedValues = Set.of(allowedValues); + final Class dataType = getTableDefinition().getColumn(column).getDataType(); + if (dataType != String.class) { + throw new IllegalArgumentException("String list validation only applies to String columns, but " + column + " is " + dataType); + } + } + + + @Override + public @Nullable List getColumnRestrictions(String columnName) { + final List columnRestrictions = wrapped.getColumnRestrictions(columnName); + if (!columnName.equals(column)) { + return columnRestrictions; + } + + final List result = new ArrayList<>(); + if (columnRestrictions != null) { + result.addAll(columnRestrictions); + } + final StringListRestriction stringListRestriction = + StringListRestriction.newBuilder().addAllAllowedValues(allowedValuesList).build(); + result.add(Any.pack(stringListRestriction, "docs.deephaven.io")); + return result; + } + + + @Override + public void validateAddOrModify(Table tableToApply) { + final List errors = new ArrayList<>(); + final MutableInt position = new MutableInt(0); + final ColumnSource columnSource = tableToApply.getColumnSource(column, String.class); + + try (final RowSequence rowSequence = tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { + rowSequence.forAllRowKeys(rowKey -> { + final String value = columnSource.get(rowKey); + if (!allowedValues.contains(value)) { + errors.add(new StructuredErrorImpl( + "Value '" + value + "' is not in the allowed list: " + allowedValuesList, + column, position.get())); + } + position.increment(); + }); + } + + if (!errors.isEmpty()) { + throw new InputTableValidationException(errors); + } + + wrapped.validateAddOrModify(tableToApply); + } +} + From b7cb56fb090eb9e6f5f685b2e95ceac6cc3cd6f4 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 8 Apr 2026 09:51:55 -0600 Subject: [PATCH 08/35] Add ColumnRestriction class --- .../io/deephaven/web/client/api/Column.java | 6 +- .../web/client/api/ColumnRestriction.java | 128 ++++++++++++++++++ .../api/barrage/def/ColumnDefinition.java | 10 +- .../api/barrage/def/InputTableMetadata.java | 40 +++++- 4 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java b/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java index 2353272e9db..3958c857e09 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java @@ -49,7 +49,7 @@ public class Column { private String description; private final boolean isInputTableKeyColumn; private final boolean isInputTableValueColumn; - private final JsArray columnRestrictions; + private final JsArray columnRestrictions; /** * Format entire rows colors using the expression specified. Returns a {@code CustomColumn} object to apply to a @@ -84,7 +84,7 @@ public static CustomColumn createCustomColumn( public Column(int jsIndex, int index, Integer formatColumnIndex, Integer styleColumnIndex, String type, String name, boolean isPartitionColumn, Integer formatStringColumnIndex, String description, boolean inputTableKeyColumn, boolean inputTableValueColumn, boolean isSortable, - JsArray columnRestrictions) { + JsArray columnRestrictions) { this.jsIndex = jsIndex; this.index = index; assert Objects.equals(formatColumnIndex, styleColumnIndex); @@ -246,7 +246,7 @@ public boolean getIsSortable() { */ @JsProperty @JsNullable - public JsArray getColumnRestrictions() { + public JsArray getColumnRestrictions() { return columnRestrictions; } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java b/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java new file mode 100644 index 00000000000..83bb2840ddd --- /dev/null +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java @@ -0,0 +1,128 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.web.client.api; + +import com.vertispan.tsdefs.annotations.TsName; +import elemental2.core.JsArray; +import jsinterop.annotations.JsNullable; +import jsinterop.annotations.JsProperty; +import jsinterop.base.Any; + +/** + * Represents a restriction on an input table column. Restrictions define constraints that the server enforces on + * column values. There are several types of restrictions: + *
    + *
  • IntegerRangeRestriction - validates integer values are within a range
  • + *
  • DoubleRangeRestriction - validates double values are within a range
  • + *
  • NotNullRestriction - validates values are not null
  • + *
  • NonEmptyRestriction - validates string values are not empty
  • + *
  • StringListRestriction - validates string values belong to a set of allowed values
  • + *
+ */ +@TsName(namespace = "dh") +public class ColumnRestriction { + private final String type; + private final double minValue; + private final double maxValue; + private final JsArray allowedValues; + + /** + * Creates a range restriction (IntegerRangeRestriction or DoubleRangeRestriction). + * + * @param type The type of restriction (e.g., "IntegerRangeRestriction" or "DoubleRangeRestriction") + * @param minValue The minimum value (inclusive), or NaN if no minimum + * @param maxValue The maximum value (inclusive), or NaN if no maximum + */ + public ColumnRestriction(String type, double minValue, double maxValue) { + this.type = type; + this.minValue = minValue; + this.maxValue = maxValue; + this.allowedValues = null; + } + + /** + * Creates a string list restriction (StringListRestriction). + * + * @param type The type of restriction (should be "StringListRestriction") + * @param allowedValues The array of allowed values + */ + public ColumnRestriction(String type, JsArray allowedValues) { + this.type = type; + this.minValue = Double.NaN; + this.maxValue = Double.NaN; + this.allowedValues = allowedValues; + } + + /** + * Creates a simple restriction with no parameters (NotNullRestriction or NonEmptyRestriction). + * + * @param type The type of restriction (e.g., "NotNullRestriction" or "NonEmptyRestriction") + */ + public ColumnRestriction(String type) { + this.type = type; + this.minValue = Double.NaN; + this.maxValue = Double.NaN; + this.allowedValues = null; + } + + /** + * The type of restriction. Possible values: + *
    + *
  • "IntegerRangeRestriction" - integer values must be within a range
  • + *
  • "DoubleRangeRestriction" - double values must be within a range
  • + *
  • "NotNullRestriction" - values must not be null
  • + *
  • "NonEmptyRestriction" - string values must not be empty
  • + *
  • "StringListRestriction" - string values must be in the allowed list
  • + *
+ * + * @return The restriction type + */ + @JsProperty + public String getType() { + return type; + } + + /** + * For range restrictions (IntegerRangeRestriction or DoubleRangeRestriction), returns an array with two elements: + * [minValue, maxValue]. Either value may be NaN if not specified. Returns null for non-range restrictions. + * + * @return Array of [min, max] values for range restrictions, or null for other restriction types + */ + @JsProperty + @JsNullable + public JsArray getRange() { + if (Double.isNaN(minValue) && Double.isNaN(maxValue)) { + return null; + } + JsArray range = new JsArray<>(); + range.push(minValue); + range.push(maxValue); + return range; + } + + /** + * For StringListRestriction, returns the array of allowed values. Returns null for other restriction types. + * + * @return Array of allowed values for StringListRestriction, or null for other restriction types + */ + @JsProperty + @JsNullable + public JsArray getValues() { + return allowedValues; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("ColumnRestriction{type='" + type + "'"); + if (!Double.isNaN(minValue) || !Double.isNaN(maxValue)) { + sb.append(", range=[").append(minValue).append(", ").append(maxValue).append("]"); + } + if (allowedValues != null) { + sb.append(", values=").append(allowedValues); + } + sb.append("}"); + return sb.toString(); + } +} + diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/ColumnDefinition.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/ColumnDefinition.java index c1d08e3dd62..aa297c73068 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/ColumnDefinition.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/ColumnDefinition.java @@ -5,7 +5,7 @@ import elemental2.core.JsArray; import io.deephaven.web.client.api.Column; -import jsinterop.base.Any; +import io.deephaven.web.client.api.ColumnRestriction; import org.apache.arrow.flatbuf.Field; import java.util.Map; @@ -38,7 +38,7 @@ public class ColumnDefinition { private final boolean isInputTableKeyColumn; private final boolean isInputTableValueColumn; private final String description; - private JsArray columnRestrictions; + private JsArray columnRestrictions; public ColumnDefinition(int index, Field field) { Map fieldMetadata = @@ -133,11 +133,11 @@ public String getDescription() { return description; } - public JsArray getColumnRestrictions() { + public JsArray getColumnRestrictions() { return columnRestrictions; } - public void setColumnRestrictions(JsArray columnRestrictions) { + public void setColumnRestrictions(JsArray columnRestrictions) { this.columnRestrictions = columnRestrictions; } @@ -163,7 +163,7 @@ public Column makeJsColumn(int index, Map private static Column makeColumn(int jsIndex, ColumnDefinition definition, Integer numberFormatIndex, Integer styleIndex, boolean isPartitionColumn, Integer formatStringIndex, String description, - boolean inputTableKeyColumn, boolean inputTableValueColumn, JsArray columnRestrictions) { + boolean inputTableKeyColumn, boolean inputTableValueColumn, JsArray columnRestrictions) { return new Column(jsIndex, definition.getColumnIndex(), numberFormatIndex, styleIndex, definition.getType(), definition.getName(), isPartitionColumn, formatStringIndex, description, inputTableKeyColumn, inputTableValueColumn, diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java index ab375473050..110772f8a77 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java @@ -4,6 +4,7 @@ package io.deephaven.web.client.api.barrage.def; import elemental2.core.JsArray; +import io.deephaven.web.client.api.ColumnRestriction; import jsinterop.annotations.JsIgnore; import jsinterop.annotations.JsNullable; import jsinterop.base.Any; @@ -37,7 +38,7 @@ public ColumnRestrictions getColumnRestrictions(String columnName) { * Represents restrictions on a column's values. */ public static class ColumnRestrictions { - private final JsArray restrictions; + private final JsArray restrictions; @JsIgnore public ColumnRestrictions() { @@ -45,14 +46,45 @@ public ColumnRestrictions() { } @JsIgnore - public void addRestriction(Any restriction) { - restrictions.push(restriction); + public void addRestriction(Any restrictionData) { + // Convert the parsed restriction data into a ColumnRestriction object + ColumnRestriction restriction = convertRestriction(restrictionData); + if (restriction != null) { + restrictions.push(restriction); + } } @JsIgnore - public JsArray getRestrictions() { + public JsArray getRestrictions() { return restrictions; } + + private static native ColumnRestriction convertRestriction(Any restrictionData) /*-{ + if (!restrictionData) return null; + + var type = restrictionData.type || "Unknown"; + console.log("convertRestriction: Converting restriction of type:", type); + + // Create the appropriate ColumnRestriction based on type + if (type === 'IntegerRangeRestriction' || type === 'DoubleRangeRestriction') { + var minValue = restrictionData.minInclusive !== undefined ? restrictionData.minInclusive : NaN; + var maxValue = restrictionData.maxInclusive !== undefined ? restrictionData.maxInclusive : NaN; + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;DD)( + type, minValue, maxValue + ); + } else if (type === 'StringListRestriction') { + var allowedValues = restrictionData.allowedValues || []; + // Use the JS array directly - it will be cast to JsArray automatically + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;Lelemental2/core/JsArray;)( + type, allowedValues + ); + } else if (type === 'NotNullRestriction' || type === 'NonEmptyRestriction') { + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)(type); + } else { + console.warn("convertRestriction: Unknown restriction type:", type); + return null; + } + }-*/; } } From 962de5e7d6ec5df0d079f8a2c63b7d88fd3d0e0d Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 8 Apr 2026 10:25:17 -0600 Subject: [PATCH 09/35] clean up logging --- .../client/api/barrage/WebBarrageUtils.java | 78 ++----------------- 1 file changed, 5 insertions(+), 73 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 572d472a9fe..b83dd6ed16d 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -12,6 +12,7 @@ import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; import io.deephaven.web.client.api.barrage.def.InputTableMetadata; import io.deephaven.web.client.api.barrage.def.TableAttributesDefinition; +import io.deephaven.web.client.fu.JsLog; import io.deephaven.web.shared.data.*; import org.apache.arrow.flatbuf.KeyValue; import org.apache.arrow.flatbuf.Message; @@ -82,43 +83,31 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD Map schemaMetadata = keyValuePairs("deephaven:", schema.customMetadataLength(), schema::customMetadata); - consoleLog("parseInputTableMetadata: Checking for tableMetadata in schema"); - String tableMetadataBase64 = schemaMetadata.get("tableMetadata"); if (tableMetadataBase64 == null || tableMetadataBase64.isEmpty()) { - consoleLog("parseInputTableMetadata: No tableMetadata found in schema (null or empty)"); return null; } - consoleLog("parseInputTableMetadata: Found tableMetadata, length=" + tableMetadataBase64.length()); InputTableMetadata metadata = new InputTableMetadata(); try { // Decode base64 to Uint8Array (like Java's Base64.getDecoder().decode()) - consoleLog("parseInputTableMetadata: Decoding base64..."); Uint8Array bytes = decodeBase64(tableMetadataBase64); - consoleLog("parseInputTableMetadata: Decoded to " + bytes.length + " bytes"); // The issue: JavaScript protobuf deserialization fails on google.protobuf.Any types // Solution: Parse the protobuf manually at the binary level to extract what we need - consoleLog("parseInputTableMetadata: Attempting manual protobuf parsing..."); jsinterop.base.Any result = parseProtoManually(bytes); if (result == null) { - consoleLog("parseInputTableMetadata: Manual parsing returned null"); return metadata; } - consoleLog("parseInputTableMetadata: Successfully parsed, extracting column info"); - // Extract column restrictions from the manually parsed data for (ColumnDefinition col : cols) { String columnName = col.getName(); jsinterop.base.Any columnInfo = getPropertyFromMap(result, columnName); if (columnInfo != null) { - consoleLog("parseInputTableMetadata: Found info for column '" + columnName + "'"); - // Get restrictions array if available jsinterop.base.Any restrictionsList = getProperty(columnInfo, "restrictions"); if (restrictionsList != null && isArray(restrictionsList)) { @@ -126,9 +115,6 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD jsinterop.base.Js.uncheckedCast(restrictionsList); if (restrictions.length > 0) { - consoleLog("parseInputTableMetadata: Column '" + columnName + "' has " + - restrictions.length + " restrictions"); - InputTableMetadata.ColumnRestrictions colRestrictions = new InputTableMetadata.ColumnRestrictions(); for (int i = 0; i < restrictions.length; i++) { @@ -139,10 +125,8 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD } } } - - consoleLog("parseInputTableMetadata: Successfully parsed metadata"); } catch (Exception e) { - consoleError("parseInputTableMetadata: Exception during parsing", e); + JsLog.warn("Failed to parse input table metadata:", e); } return metadata; @@ -166,9 +150,6 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* // Based on the pattern from AddToInputTable.java // Use the BinaryReader from dhinternal.jspb which is the JsInterop wrapper try { - console.log("parseProtoManually: Starting manual protobuf parsing"); - console.log("parseProtoManually: Bytes length =", bytes.length); - // Use the JsInterop BinaryReader class var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); var reader = new BinaryReader(bytes); @@ -181,7 +162,6 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* break; } var field = reader.getFieldNumber(); - console.log("parseProtoManually: Reading field", field); if (field === 1) { // This is the InputTableMetadata field @@ -194,7 +174,6 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* break; } var subfield = rdr.getFieldNumber(); - console.log("parseProtoManually: InputTableMetadata field", subfield); if (subfield === 1) { // This is the columnInfoMap @@ -212,7 +191,6 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* if (mapField === 1) { // Map key (column name) key = mapReader.readString(); - console.log("parseProtoManually: Column name =", key); } else if (mapField === 2) { // Map value (InputTableColumnInfo) value = {}; @@ -224,7 +202,6 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* break; } var colField = colReader.getFieldNumber(); - console.log("parseProtoManually: InputTableColumnInfo field", colField); if (colField === 1) { // kind field @@ -234,7 +211,6 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* // Parse the Any message to extract type_url and value try { var anyBytes = colReader.readBytes(); - console.log("parseProtoManually: Got Any bytes, length =", anyBytes.length); // Parse the google.protobuf.Any message // Field 1 = type_url (string) @@ -250,10 +226,8 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* var anyField = anyReader.getFieldNumber(); if (anyField === 1) { typeUrl = anyReader.readString(); - console.log("parseProtoManually: Restriction type_url =", typeUrl); } else if (anyField === 2) { valueBytes = anyReader.readBytes(); - console.log("parseProtoManually: Restriction value bytes length =", valueBytes.length); } } @@ -263,7 +237,7 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* restrictions.push(restriction); } } catch (e) { - console.warn("parseProtoManually: Failed to parse restriction:", e); + @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to parse restriction:", e); } } } @@ -277,7 +251,6 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* metadata.columnInfoMap = {}; } metadata.columnInfoMap[key] = value; - console.log("parseProtoManually: Added column", key, "with", value.restrictions ? value.restrictions.length : 0, "restrictions"); } }); } @@ -285,15 +258,13 @@ private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /* }); result = inputTableMetadata.columnInfoMap || {}; - console.log("parseProtoManually: Parsed", Object.keys(result).length, "columns"); } } return result; } catch (e) { - console.error("parseProtoManually: Failed to manually parse protobuf:", e); - console.error("Stack:", e.stack); + @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to manually parse protobuf:", e); return null; } }-*/; @@ -308,8 +279,6 @@ private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8A // - StringListRestriction try { - console.log("parseRestriction: Parsing restriction type:", typeUrl); - var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); var reader = new BinaryReader(valueBytes); @@ -318,7 +287,6 @@ private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8A // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" var typeName = typeUrl.substring(typeUrl.lastIndexOf('/') + 1); var shortName = typeName.substring(typeName.lastIndexOf('.') + 1); - console.log("parseRestriction: Message type:", shortName); var restriction = { type: shortName, @@ -333,10 +301,8 @@ private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8A var field = reader.getFieldNumber(); if (field === 1) { restriction.minInclusive = reader.readInt64(); - console.log("parseRestriction: minInclusive =", restriction.minInclusive); } else if (field === 2) { restriction.maxInclusive = reader.readInt64(); - console.log("parseRestriction: maxInclusive =", restriction.maxInclusive); } } } else if (shortName === 'DoubleRangeRestriction') { @@ -347,20 +313,16 @@ private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8A var field = reader.getFieldNumber(); if (field === 1) { restriction.minInclusive = reader.readDouble(); - console.log("parseRestriction: minInclusive =", restriction.minInclusive); } else if (field === 2) { restriction.maxInclusive = reader.readDouble(); - console.log("parseRestriction: maxInclusive =", restriction.maxInclusive); } } } else if (shortName === 'NotNullRestriction') { // No fields - just the type restriction.notNull = true; - console.log("parseRestriction: NotNull restriction"); } else if (shortName === 'NonEmptyRestriction') { // No fields - just the type restriction.nonEmpty = true; - console.log("parseRestriction: NonEmpty restriction"); } else if (shortName === 'StringListRestriction') { // Field 1 = allowed_values (repeated string) restriction.allowedValues = []; @@ -370,18 +332,16 @@ private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8A if (field === 1) { var value = reader.readString(); restriction.allowedValues.push(value); - console.log("parseRestriction: allowed value =", value); } } } else { - console.warn("parseRestriction: Unknown restriction type:", shortName); restriction.raw = valueBytes; } return restriction; } catch (e) { - console.error("parseRestriction: Failed to parse restriction:", e); + @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to parse restriction:", e); return null; } }-*/; @@ -400,39 +360,11 @@ private static native jsinterop.base.Any getProperty(jsinterop.base.Any obj, Str return obj[propertyName]; }-*/; - private static native jsinterop.base.Any getMapEntry(jsinterop.base.Any map, String key) /*-{ - if (map == null) return null; - if (typeof map.get === 'function') { - return map.get(key); - } - if (typeof map.getMap === 'function') { - var m = map.getMap(); - return m ? m[key] : null; - } - return map[key]; - }-*/; private static native boolean isArray(jsinterop.base.Any obj) /*-{ return Array.isArray(obj); }-*/; - private static native void consoleLog(String message) /*-{ - console.log(message); - }-*/; - - private static native void consoleError(String message, Exception e) /*-{ - console.error(message, e); - }-*/; - - private static native void logProtoObject(String label, jsinterop.base.Any obj) /*-{ - console.log(label + ":", obj); - if (obj != null) { - console.log(label + " keys:", Object.keys(obj)); - console.log(label + " methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(obj)).filter(function(name) { - return typeof obj[name] === 'function'; - })); - } - }-*/; private static ColumnDefinition[] readColumnDefinitions(Schema schema) { ColumnDefinition[] cols = new ColumnDefinition[(int) schema.fieldsLength()]; From e7d828eb78bf4e21ce84b885e236a4d19bbe0cbf Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 8 Apr 2026 10:35:51 -0600 Subject: [PATCH 10/35] remove more logging --- .../web/client/api/barrage/def/InputTableMetadata.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java index 110772f8a77..a2b88624c10 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java @@ -63,7 +63,6 @@ private static native ColumnRestriction convertRestriction(Any restrictionData) if (!restrictionData) return null; var type = restrictionData.type || "Unknown"; - console.log("convertRestriction: Converting restriction of type:", type); // Create the appropriate ColumnRestriction based on type if (type === 'IntegerRangeRestriction' || type === 'DoubleRangeRestriction') { @@ -80,10 +79,9 @@ private static native ColumnRestriction convertRestriction(Any restrictionData) ); } else if (type === 'NotNullRestriction' || type === 'NonEmptyRestriction') { return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)(type); - } else { - console.warn("convertRestriction: Unknown restriction type:", type); - return null; } + + return null; }-*/; } } From 65f3327a24b7592337b9e276344bb8b6da7d48e4 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 8 Apr 2026 11:25:58 -0600 Subject: [PATCH 11/35] refactor to util --- .../client/api/barrage/WebBarrageUtils.java | 203 +------------ .../api/barrage/def/InputTableMetadata.java | 28 +- .../barrage/util/ColumnRestrictionUtils.java | 267 ++++++++++++++++++ 3 files changed, 271 insertions(+), 227 deletions(-) create mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index b83dd6ed16d..2221b569f7b 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -12,6 +12,7 @@ import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; import io.deephaven.web.client.api.barrage.def.InputTableMetadata; import io.deephaven.web.client.api.barrage.def.TableAttributesDefinition; +import io.deephaven.web.client.api.barrage.util.ColumnRestrictionUtils; import io.deephaven.web.client.fu.JsLog; import io.deephaven.web.shared.data.*; import org.apache.arrow.flatbuf.KeyValue; @@ -96,7 +97,7 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD // The issue: JavaScript protobuf deserialization fails on google.protobuf.Any types // Solution: Parse the protobuf manually at the binary level to extract what we need - jsinterop.base.Any result = parseProtoManually(bytes); + jsinterop.base.Any result = ColumnRestrictionUtils.parseProtoManually(bytes); if (result == null) { return metadata; @@ -145,206 +146,6 @@ private static Uint8Array decodeBase64(String base64) { return bytes; } - private static native jsinterop.base.Any parseProtoManually(Uint8Array bytes) /*-{ - // Manual protobuf parsing to work around missing google.protobuf.Any - // Based on the pattern from AddToInputTable.java - // Use the BinaryReader from dhinternal.jspb which is the JsInterop wrapper - try { - // Use the JsInterop BinaryReader class - var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); - var reader = new BinaryReader(bytes); - var result = {}; - - // Parse the DeephavenTableMetadata message - // Field 1 is InputTableMetadata - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - - if (field === 1) { - // This is the InputTableMetadata field - var inputTableMetadata = {}; - reader.readMessage(inputTableMetadata, function(metadata, rdr) { - // Parse InputTableMetadata - // Field 1 is columnInfoMap (map) - while (rdr.nextField()) { - if (rdr.isEndGroup()) { - break; - } - var subfield = rdr.getFieldNumber(); - - if (subfield === 1) { - // This is the columnInfoMap - var mapEntry = {}; - rdr.readMessage(mapEntry, function(entry, mapReader) { - var key = null; - var value = null; - - while (mapReader.nextField()) { - if (mapReader.isEndGroup()) { - break; - } - var mapField = mapReader.getFieldNumber(); - - if (mapField === 1) { - // Map key (column name) - key = mapReader.readString(); - } else if (mapField === 2) { - // Map value (InputTableColumnInfo) - value = {}; - var restrictions = []; - - mapReader.readMessage(value, function(columnInfo, colReader) { - while (colReader.nextField()) { - if (colReader.isEndGroup()) { - break; - } - var colField = colReader.getFieldNumber(); - - if (colField === 1) { - // kind field - columnInfo.kind = colReader.readEnum(); - } else if (colField === 2) { - // restrictions field (repeated google.protobuf.Any) - // Parse the Any message to extract type_url and value - try { - var anyBytes = colReader.readBytes(); - - // Parse the google.protobuf.Any message - // Field 1 = type_url (string) - // Field 2 = value (bytes) - var anyReader = new BinaryReader(anyBytes); - var typeUrl = null; - var valueBytes = null; - - while (anyReader.nextField()) { - if (anyReader.isEndGroup()) { - break; - } - var anyField = anyReader.getFieldNumber(); - if (anyField === 1) { - typeUrl = anyReader.readString(); - } else if (anyField === 2) { - valueBytes = anyReader.readBytes(); - } - } - - // Now parse the actual restriction based on type_url - var restriction = @io.deephaven.web.client.api.barrage.WebBarrageUtils::parseRestriction(*)(typeUrl, valueBytes); - if (restriction) { - restrictions.push(restriction); - } - } catch (e) { - @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to parse restriction:", e); - } - } - } - columnInfo.restrictions = restrictions; - }); - } - } - - if (key !== null && value !== null) { - if (!metadata.columnInfoMap) { - metadata.columnInfoMap = {}; - } - metadata.columnInfoMap[key] = value; - } - }); - } - } - }); - - result = inputTableMetadata.columnInfoMap || {}; - } - } - - return result; - - } catch (e) { - @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to manually parse protobuf:", e); - return null; - } - }-*/; - - private static native jsinterop.base.Any parseRestriction(String typeUrl, Uint8Array valueBytes) /*-{ - // Parse specific restriction types based on typeUrl - // Types from inputtable.proto: - // - IntegerRangeRestriction - // - DoubleRangeRestriction - // - NotNullRestriction - // - NonEmptyRestriction - // - StringListRestriction - - try { - var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); - var reader = new BinaryReader(valueBytes); - - // Extract the message type from the type URL - // Format: "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" - // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" - var typeName = typeUrl.substring(typeUrl.lastIndexOf('/') + 1); - var shortName = typeName.substring(typeName.lastIndexOf('.') + 1); - - var restriction = { - type: shortName, - typeUrl: typeUrl - }; - - if (shortName === 'IntegerRangeRestriction') { - // Field 1 = min_inclusive (int64) - // Field 2 = max_inclusive (int64) - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - restriction.minInclusive = reader.readInt64(); - } else if (field === 2) { - restriction.maxInclusive = reader.readInt64(); - } - } - } else if (shortName === 'DoubleRangeRestriction') { - // Field 1 = min_inclusive (double) - // Field 2 = max_inclusive (double) - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - restriction.minInclusive = reader.readDouble(); - } else if (field === 2) { - restriction.maxInclusive = reader.readDouble(); - } - } - } else if (shortName === 'NotNullRestriction') { - // No fields - just the type - restriction.notNull = true; - } else if (shortName === 'NonEmptyRestriction') { - // No fields - just the type - restriction.nonEmpty = true; - } else if (shortName === 'StringListRestriction') { - // Field 1 = allowed_values (repeated string) - restriction.allowedValues = []; - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - var value = reader.readString(); - restriction.allowedValues.push(value); - } - } - } else { - restriction.raw = valueBytes; - } - - return restriction; - - } catch (e) { - @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to parse restriction:", e); - return null; - } - }-*/; private static native jsinterop.base.Any getPropertyFromMap(jsinterop.base.Any map, String key) /*-{ if (map == null) return null; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java index a2b88624c10..ea457366ea0 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java @@ -5,6 +5,7 @@ import elemental2.core.JsArray; import io.deephaven.web.client.api.ColumnRestriction; +import io.deephaven.web.client.api.barrage.util.ColumnRestrictionUtils; import jsinterop.annotations.JsIgnore; import jsinterop.annotations.JsNullable; import jsinterop.base.Any; @@ -48,7 +49,7 @@ public ColumnRestrictions() { @JsIgnore public void addRestriction(Any restrictionData) { // Convert the parsed restriction data into a ColumnRestriction object - ColumnRestriction restriction = convertRestriction(restrictionData); + ColumnRestriction restriction = ColumnRestrictionUtils.convertRestriction(restrictionData); if (restriction != null) { restrictions.push(restriction); } @@ -58,31 +59,6 @@ public void addRestriction(Any restrictionData) { public JsArray getRestrictions() { return restrictions; } - - private static native ColumnRestriction convertRestriction(Any restrictionData) /*-{ - if (!restrictionData) return null; - - var type = restrictionData.type || "Unknown"; - - // Create the appropriate ColumnRestriction based on type - if (type === 'IntegerRangeRestriction' || type === 'DoubleRangeRestriction') { - var minValue = restrictionData.minInclusive !== undefined ? restrictionData.minInclusive : NaN; - var maxValue = restrictionData.maxInclusive !== undefined ? restrictionData.maxInclusive : NaN; - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;DD)( - type, minValue, maxValue - ); - } else if (type === 'StringListRestriction') { - var allowedValues = restrictionData.allowedValues || []; - // Use the JS array directly - it will be cast to JsArray automatically - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;Lelemental2/core/JsArray;)( - type, allowedValues - ); - } else if (type === 'NotNullRestriction' || type === 'NonEmptyRestriction') { - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)(type); - } - - return null; - }-*/; } } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java new file mode 100644 index 00000000000..4787e116bd1 --- /dev/null +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -0,0 +1,267 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.web.client.api.barrage.util; + +import elemental2.core.JsArray; +import elemental2.core.Uint8Array; +import io.deephaven.web.client.api.ColumnRestriction; +import io.deephaven.web.client.fu.JsLog; +import jsinterop.base.Any; + +/** + * Utility class for parsing column restrictions from protobuf data. + */ +public class ColumnRestrictionUtils { + + private ColumnRestrictionUtils() { + // Utility class - no instances + } + + /** + * Manually parse protobuf bytes to extract input table metadata and column restrictions. + * This works around issues with google.protobuf.Any deserialization in JavaScript. + * + * @param bytes The protobuf bytes to parse + * @return A map of column names to their metadata, or null if parsing fails + */ + public static native Any parseProtoManually(Uint8Array bytes) /*-{ + // Manual protobuf parsing to work around missing google.protobuf.Any + // Based on the pattern from AddToInputTable.java + // Use the BinaryReader from dhinternal.jspb which is the JsInterop wrapper + try { + // Use the JsInterop BinaryReader class + var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); + var reader = new BinaryReader(bytes); + var result = {}; + + // Parse the DeephavenTableMetadata message + // Field 1 is InputTableMetadata + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + + if (field === 1) { + // This is the InputTableMetadata field + var inputTableMetadata = {}; + reader.readMessage(inputTableMetadata, function(metadata, rdr) { + // Parse InputTableMetadata + // Field 1 is columnInfoMap (map) + while (rdr.nextField()) { + if (rdr.isEndGroup()) { + break; + } + var subfield = rdr.getFieldNumber(); + + if (subfield === 1) { + // This is the columnInfoMap + var mapEntry = {}; + rdr.readMessage(mapEntry, function(entry, mapReader) { + var key = null; + var value = null; + + while (mapReader.nextField()) { + if (mapReader.isEndGroup()) { + break; + } + var mapField = mapReader.getFieldNumber(); + + if (mapField === 1) { + // Map key (column name) + key = mapReader.readString(); + } else if (mapField === 2) { + // Map value (InputTableColumnInfo) + value = {}; + var restrictions = []; + + mapReader.readMessage(value, function(columnInfo, colReader) { + while (colReader.nextField()) { + if (colReader.isEndGroup()) { + break; + } + var colField = colReader.getFieldNumber(); + + if (colField === 1) { + // kind field + columnInfo.kind = colReader.readEnum(); + } else if (colField === 2) { + // restrictions field (repeated google.protobuf.Any) + // Parse the Any message to extract type_url and value + try { + var anyBytes = colReader.readBytes(); + + // Parse the google.protobuf.Any message + // Field 1 = type_url (string) + // Field 2 = value (bytes) + var anyReader = new BinaryReader(anyBytes); + var typeUrl = null; + var valueBytes = null; + + while (anyReader.nextField()) { + if (anyReader.isEndGroup()) { + break; + } + var anyField = anyReader.getFieldNumber(); + if (anyField === 1) { + typeUrl = anyReader.readString(); + } else if (anyField === 2) { + valueBytes = anyReader.readBytes(); + } + } + + // Now parse the actual restriction based on type_url + var restriction = @io.deephaven.web.client.api.barrage.util.ColumnRestrictionUtils::parseRestriction(*)(typeUrl, valueBytes); + if (restriction) { + restrictions.push(restriction); + } + } catch (e) { + @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to parse restriction:", e); + } + } + } + columnInfo.restrictions = restrictions; + }); + } + } + + if (key !== null && value !== null) { + if (!metadata.columnInfoMap) { + metadata.columnInfoMap = {}; + } + metadata.columnInfoMap[key] = value; + } + }); + } + } + }); + + result = inputTableMetadata.columnInfoMap || {}; + } + } + + return result; + + } catch (e) { + @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to manually parse protobuf:", e); + return null; + } + }-*/; + + /** + * Parse a specific restriction type from protobuf bytes. + * + * @param typeUrl The type URL of the restriction + * @param valueBytes The protobuf bytes containing the restriction data + * @return The parsed restriction data, or null if parsing fails + */ + private static native Any parseRestriction(String typeUrl, Uint8Array valueBytes) /*-{ + // Parse specific restriction types based on typeUrl + // Types from inputtable.proto: + // - IntegerRangeRestriction + // - DoubleRangeRestriction + // - NotNullRestriction + // - NonEmptyRestriction + // - StringListRestriction + + try { + var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); + var reader = new BinaryReader(valueBytes); + + // Extract the message type from the type URL + // Format: "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" + // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" + var typeName = typeUrl.substring(typeUrl.lastIndexOf('/') + 1); + var shortName = typeName.substring(typeName.lastIndexOf('.') + 1); + + var restriction = { + type: shortName, + typeUrl: typeUrl + }; + + if (shortName === 'IntegerRangeRestriction') { + // Field 1 = min_inclusive (int64) + // Field 2 = max_inclusive (int64) + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + restriction.minInclusive = reader.readInt64(); + } else if (field === 2) { + restriction.maxInclusive = reader.readInt64(); + } + } + } else if (shortName === 'DoubleRangeRestriction') { + // Field 1 = min_inclusive (double) + // Field 2 = max_inclusive (double) + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + restriction.minInclusive = reader.readDouble(); + } else if (field === 2) { + restriction.maxInclusive = reader.readDouble(); + } + } + } else if (shortName === 'NotNullRestriction') { + // No fields - just the type + restriction.notNull = true; + } else if (shortName === 'NonEmptyRestriction') { + // No fields - just the type + restriction.nonEmpty = true; + } else if (shortName === 'StringListRestriction') { + // Field 1 = allowed_values (repeated string) + restriction.allowedValues = []; + while (reader.nextField()) { + if (reader.isEndGroup()) break; + var field = reader.getFieldNumber(); + if (field === 1) { + var value = reader.readString(); + restriction.allowedValues.push(value); + } + } + } else { + restriction.raw = valueBytes; + } + + return restriction; + + } catch (e) { + @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to parse restriction:", e); + return null; + } + }-*/; + + /** + * Convert parsed restriction data into a ColumnRestriction object. + * + * @param restrictionData The parsed restriction data from protobuf + * @return A ColumnRestriction object, or null if conversion fails + */ + public static native ColumnRestriction convertRestriction(Any restrictionData) /*-{ + if (!restrictionData) return null; + + var type = restrictionData.type || "Unknown"; + + // Create the appropriate ColumnRestriction based on type + if (type === 'IntegerRangeRestriction' || type === 'DoubleRangeRestriction') { + var minValue = restrictionData.minInclusive !== undefined ? restrictionData.minInclusive : NaN; + var maxValue = restrictionData.maxInclusive !== undefined ? restrictionData.maxInclusive : NaN; + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;DD)( + type, minValue, maxValue + ); + } else if (type === 'StringListRestriction') { + var allowedValues = restrictionData.allowedValues || []; + // Use the JS array directly - it will be cast to JsArray automatically + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;Lelemental2/core/JsArray;)( + type, allowedValues + ); + } else if (type === 'NotNullRestriction' || type === 'NonEmptyRestriction') { + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)(type); + } + + return null; + }-*/; +} + From fce1f27377b4a0f5ab6da90401e1b900a052a50b Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 8 Apr 2026 14:31:29 -0600 Subject: [PATCH 12/35] register converter --- .../client/api/barrage/WebBarrageUtils.java | 48 +++++++++++- .../api/barrage/def/InputTableMetadata.java | 7 +- .../util/ColumnRestrictionConverter.java | 22 ++++++ .../barrage/util/ColumnRestrictionUtils.java | 76 ++++++++++++++----- 4 files changed, 127 insertions(+), 26 deletions(-) create mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 2221b569f7b..e0f2beb8f1b 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -12,6 +12,7 @@ import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; import io.deephaven.web.client.api.barrage.def.InputTableMetadata; import io.deephaven.web.client.api.barrage.def.TableAttributesDefinition; +import io.deephaven.web.client.api.barrage.util.ColumnRestrictionConverter; import io.deephaven.web.client.api.barrage.util.ColumnRestrictionUtils; import io.deephaven.web.client.fu.JsLog; import io.deephaven.web.shared.data.*; @@ -33,6 +34,37 @@ public class WebBarrageUtils { public static final int FLATBUFFER_MAGIC = 0x6E687064; + private static final Map restrictionConverters = new HashMap<>(); + + static { + // Register default converters + registerColumnRestrictionConverter("IntegerRangeRestriction", ColumnRestrictionUtils::convertIntegerRangeRestriction); + registerColumnRestrictionConverter("DoubleRangeRestriction", ColumnRestrictionUtils::convertDoubleRangeRestriction); + registerColumnRestrictionConverter("NotNullRestriction", ColumnRestrictionUtils::convertNotNullRestriction); + registerColumnRestrictionConverter("NonEmptyRestriction", ColumnRestrictionUtils::convertNonEmptyRestriction); + registerColumnRestrictionConverter("StringListRestriction", ColumnRestrictionUtils::convertStringListRestriction); + } + + /** + * Register a converter for a specific restriction type. + * + * @param restrictionType The type name of the restriction (e.g., "IntegerRangeRestriction") + * @param converter The converter function to convert the restriction data + */ + public static void registerColumnRestrictionConverter(String restrictionType, ColumnRestrictionConverter converter) { + restrictionConverters.put(restrictionType, converter); + } + + /** + * Get the converter for a specific restriction type. + * + * @param restrictionType The type name of the restriction + * @return The converter, or null if none is registered + */ + static ColumnRestrictionConverter getColumnRestrictionConverter(String restrictionType) { + return restrictionConverters.get(restrictionType); + } + public static Uint8Array wrapMessage(FlatBufferBuilder innerBuilder, byte messageType) { FlatBufferBuilder outerBuilder = new FlatBufferBuilder(1024); int messageOffset = BarrageMessageWrapper.createMsgPayloadVector(outerBuilder, innerBuilder.dataBuffer()); @@ -119,7 +151,17 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD InputTableMetadata.ColumnRestrictions colRestrictions = new InputTableMetadata.ColumnRestrictions(); for (int i = 0; i < restrictions.length; i++) { - colRestrictions.addRestriction(restrictions.getAt(i)); + jsinterop.base.Any restrictionData = restrictions.getAt(i); + + // Get the restriction type and convert it + String restrictionType = getRestrictionType(restrictionData); + if (restrictionType != null) { + ColumnRestrictionConverter converter = getColumnRestrictionConverter(restrictionType); + if (converter != null) { + io.deephaven.web.client.api.ColumnRestriction restriction = converter.convert(restrictionData); + colRestrictions.addRestriction(restriction); + } + } } metadata.addColumnRestrictions(columnName, colRestrictions); } @@ -146,6 +188,10 @@ private static Uint8Array decodeBase64(String base64) { return bytes; } + private static native String getRestrictionType(jsinterop.base.Any restrictionData) /*-{ + if (!restrictionData) return null; + return restrictionData.type || null; + }-*/; private static native jsinterop.base.Any getPropertyFromMap(jsinterop.base.Any map, String key) /*-{ if (map == null) return null; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java index ea457366ea0..6fd4b62a8cd 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java @@ -5,10 +5,8 @@ import elemental2.core.JsArray; import io.deephaven.web.client.api.ColumnRestriction; -import io.deephaven.web.client.api.barrage.util.ColumnRestrictionUtils; import jsinterop.annotations.JsIgnore; import jsinterop.annotations.JsNullable; -import jsinterop.base.Any; import java.util.HashMap; import java.util.Map; @@ -47,14 +45,13 @@ public ColumnRestrictions() { } @JsIgnore - public void addRestriction(Any restrictionData) { - // Convert the parsed restriction data into a ColumnRestriction object - ColumnRestriction restriction = ColumnRestrictionUtils.convertRestriction(restrictionData); + public void addRestriction(ColumnRestriction restriction) { if (restriction != null) { restrictions.push(restriction); } } + @JsIgnore public JsArray getRestrictions() { return restrictions; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java new file mode 100644 index 00000000000..cab0c71677e --- /dev/null +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java @@ -0,0 +1,22 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.web.client.api.barrage.util; + +import io.deephaven.web.client.api.ColumnRestriction; +import jsinterop.base.Any; + +/** + * Functional interface for converting parsed restriction data into a ColumnRestriction object. + */ +@FunctionalInterface +public interface ColumnRestrictionConverter { + /** + * Convert parsed restriction data into a ColumnRestriction object. + * + * @param restrictionData The parsed restriction data from protobuf + * @return A ColumnRestriction object, or null if conversion fails + */ + ColumnRestriction convert(Any restrictionData); +} + diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java index 4787e116bd1..858d217c820 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -234,34 +234,70 @@ private static native Any parseRestriction(String typeUrl, Uint8Array valueBytes }-*/; /** - * Convert parsed restriction data into a ColumnRestriction object. + * Convert IntegerRangeRestriction data into a ColumnRestriction object. * * @param restrictionData The parsed restriction data from protobuf * @return A ColumnRestriction object, or null if conversion fails */ - public static native ColumnRestriction convertRestriction(Any restrictionData) /*-{ + public static native ColumnRestriction convertIntegerRangeRestriction(Any restrictionData) /*-{ if (!restrictionData) return null; + var minValue = restrictionData.minInclusive !== undefined ? restrictionData.minInclusive : NaN; + var maxValue = restrictionData.maxInclusive !== undefined ? restrictionData.maxInclusive : NaN; + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;DD)( + "IntegerRangeRestriction", minValue, maxValue + ); + }-*/; - var type = restrictionData.type || "Unknown"; + /** + * Convert DoubleRangeRestriction data into a ColumnRestriction object. + * + * @param restrictionData The parsed restriction data from protobuf + * @return A ColumnRestriction object, or null if conversion fails + */ + public static native ColumnRestriction convertDoubleRangeRestriction(Any restrictionData) /*-{ + if (!restrictionData) return null; + var minValue = restrictionData.minInclusive !== undefined ? restrictionData.minInclusive : NaN; + var maxValue = restrictionData.maxInclusive !== undefined ? restrictionData.maxInclusive : NaN; + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;DD)( + "DoubleRangeRestriction", minValue, maxValue + ); + }-*/; - // Create the appropriate ColumnRestriction based on type - if (type === 'IntegerRangeRestriction' || type === 'DoubleRangeRestriction') { - var minValue = restrictionData.minInclusive !== undefined ? restrictionData.minInclusive : NaN; - var maxValue = restrictionData.maxInclusive !== undefined ? restrictionData.maxInclusive : NaN; - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;DD)( - type, minValue, maxValue - ); - } else if (type === 'StringListRestriction') { - var allowedValues = restrictionData.allowedValues || []; - // Use the JS array directly - it will be cast to JsArray automatically - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;Lelemental2/core/JsArray;)( - type, allowedValues - ); - } else if (type === 'NotNullRestriction' || type === 'NonEmptyRestriction') { - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)(type); - } + /** + * Convert NotNullRestriction data into a ColumnRestriction object. + * + * @param restrictionData The parsed restriction data from protobuf + * @return A ColumnRestriction object, or null if conversion fails + */ + public static native ColumnRestriction convertNotNullRestriction(Any restrictionData) /*-{ + if (!restrictionData) return null; + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)("NotNullRestriction"); + }-*/; - return null; + /** + * Convert NonEmptyRestriction data into a ColumnRestriction object. + * + * @param restrictionData The parsed restriction data from protobuf + * @return A ColumnRestriction object, or null if conversion fails + */ + public static native ColumnRestriction convertNonEmptyRestriction(Any restrictionData) /*-{ + if (!restrictionData) return null; + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)("NonEmptyRestriction"); + }-*/; + + /** + * Convert StringListRestriction data into a ColumnRestriction object. + * + * @param restrictionData The parsed restriction data from protobuf + * @return A ColumnRestriction object, or null if conversion fails + */ + public static native ColumnRestriction convertStringListRestriction(Any restrictionData) /*-{ + if (!restrictionData) return null; + var allowedValues = restrictionData.allowedValues || []; + // Use the JS array directly - it will be cast to JsArray automatically + return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;Lelemental2/core/JsArray;)( + "StringListRestriction", allowedValues + ); }-*/; } From feed9b9a0d7f42832a08bf3eac3858d463dc7fba Mon Sep 17 00:00:00 2001 From: Colin Alworth Date: Thu, 9 Apr 2026 11:03:22 -0500 Subject: [PATCH 13/35] Generated input table types --- .../inputtable_pb/DeephavenTableMetadata.java | 113 ++++++++ .../inputtable_pb/DoubleRangeRestriction.java | 90 +++++++ .../inputtable_pb/InputTableColumnInfo.java | 245 ++++++++++++++++++ .../inputtable_pb/InputTableMetadata.java | 77 ++++++ .../IntegerRangeRestriction.java | 90 +++++++ .../inputtable_pb/NonEmptyRestriction.java | 29 +++ .../inputtable_pb/NotNullRestriction.java | 29 +++ .../inputtable_pb/StringListRestriction.java | 88 +++++++ .../inputtablecolumninfo/KindMap.java | 40 +++ 9 files changed, 801 insertions(+) create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DeephavenTableMetadata.java create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DoubleRangeRestriction.java create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableColumnInfo.java create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableMetadata.java create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/IntegerRangeRestriction.java create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NonEmptyRestriction.java create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NotNullRestriction.java create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/StringListRestriction.java create mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/inputtablecolumninfo/KindMap.java diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DeephavenTableMetadata.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DeephavenTableMetadata.java new file mode 100644 index 00000000000..60381f04764 --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DeephavenTableMetadata.java @@ -0,0 +1,113 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; + +import elemental2.core.JsArray; +import elemental2.core.Uint8Array; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsProperty; +import jsinterop.annotations.JsType; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.DeephavenTableMetadata", + namespace = JsPackage.GLOBAL) +public class DeephavenTableMetadata { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface InputTableMetadataFieldType { + @JsOverlay + static DeephavenTableMetadata.ToObjectReturnType.InputTableMetadataFieldType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + JsArray> getColumnInfoMap(); + + @JsProperty + void setColumnInfoMap(JsArray> columnInfoMap); + + @JsOverlay + default void setColumnInfoMap(Object[][] columnInfoMap) { + setColumnInfoMap(Js.>>uncheckedCast(columnInfoMap)); + } + } + + @JsOverlay + static DeephavenTableMetadata.ToObjectReturnType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + DeephavenTableMetadata.ToObjectReturnType.InputTableMetadataFieldType getInputTableMetadata(); + + @JsProperty + void setInputTableMetadata( + DeephavenTableMetadata.ToObjectReturnType.InputTableMetadataFieldType inputTableMetadata); + } + + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType0 { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface InputTableMetadataFieldType { + @JsOverlay + static DeephavenTableMetadata.ToObjectReturnType0.InputTableMetadataFieldType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + JsArray> getColumnInfoMap(); + + @JsProperty + void setColumnInfoMap(JsArray> columnInfoMap); + + @JsOverlay + default void setColumnInfoMap(Object[][] columnInfoMap) { + setColumnInfoMap(Js.>>uncheckedCast(columnInfoMap)); + } + } + + @JsOverlay + static DeephavenTableMetadata.ToObjectReturnType0 create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + DeephavenTableMetadata.ToObjectReturnType0.InputTableMetadataFieldType getInputTableMetadata(); + + @JsProperty + void setInputTableMetadata( + DeephavenTableMetadata.ToObjectReturnType0.InputTableMetadataFieldType inputTableMetadata); + } + + public static native DeephavenTableMetadata deserializeBinary(Uint8Array bytes); + + public static native DeephavenTableMetadata deserializeBinaryFromReader( + DeephavenTableMetadata message, Object reader); + + public static native void serializeBinaryToWriter(DeephavenTableMetadata message, Object writer); + + public static native DeephavenTableMetadata.ToObjectReturnType toObject( + boolean includeInstance, DeephavenTableMetadata msg); + + public native void clearInputTableMetadata(); + + public native InputTableMetadata getInputTableMetadata(); + + public native boolean hasInputTableMetadata(); + + public native Uint8Array serializeBinary(); + + public native void setInputTableMetadata(); + + public native void setInputTableMetadata(InputTableMetadata value); + + public native DeephavenTableMetadata.ToObjectReturnType0 toObject(); + + public native DeephavenTableMetadata.ToObjectReturnType0 toObject(boolean includeInstance); +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DoubleRangeRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DoubleRangeRestriction.java new file mode 100644 index 00000000000..19378461d48 --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DoubleRangeRestriction.java @@ -0,0 +1,90 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; + +import elemental2.core.Uint8Array; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsProperty; +import jsinterop.annotations.JsType; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.DoubleRangeRestriction", + namespace = JsPackage.GLOBAL) +public class DoubleRangeRestriction { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType { + @JsOverlay + static DoubleRangeRestriction.ToObjectReturnType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + double getMaxInclusive(); + + @JsProperty + double getMinInclusive(); + + @JsProperty + void setMaxInclusive(double maxInclusive); + + @JsProperty + void setMinInclusive(double minInclusive); + } + + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType0 { + @JsOverlay + static DoubleRangeRestriction.ToObjectReturnType0 create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + double getMaxInclusive(); + + @JsProperty + double getMinInclusive(); + + @JsProperty + void setMaxInclusive(double maxInclusive); + + @JsProperty + void setMinInclusive(double minInclusive); + } + + public static native DoubleRangeRestriction deserializeBinary(Uint8Array bytes); + + public static native DoubleRangeRestriction deserializeBinaryFromReader( + DoubleRangeRestriction message, Object reader); + + public static native void serializeBinaryToWriter(DoubleRangeRestriction message, Object writer); + + public static native DoubleRangeRestriction.ToObjectReturnType toObject( + boolean includeInstance, DoubleRangeRestriction msg); + + public native void clearMaxInclusive(); + + public native void clearMinInclusive(); + + public native double getMaxInclusive(); + + public native double getMinInclusive(); + + public native boolean hasMaxInclusive(); + + public native boolean hasMinInclusive(); + + public native Uint8Array serializeBinary(); + + public native void setMaxInclusive(double value); + + public native void setMinInclusive(double value); + + public native DoubleRangeRestriction.ToObjectReturnType0 toObject(); + + public native DoubleRangeRestriction.ToObjectReturnType0 toObject(boolean includeInstance); +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableColumnInfo.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableColumnInfo.java new file mode 100644 index 00000000000..a18790b13ae --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableColumnInfo.java @@ -0,0 +1,245 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; + +import elemental2.core.JsArray; +import elemental2.core.Uint8Array; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.inputtablecolumninfo.KindMap; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsProperty; +import jsinterop.annotations.JsType; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.InputTableColumnInfo", + namespace = JsPackage.GLOBAL) +public class InputTableColumnInfo { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface RestrictionsListFieldType { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface GetValueUnionType { + @JsOverlay + static InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType.GetValueUnionType of( + Object o) { + return Js.cast(o); + } + + @JsOverlay + default String asString() { + return Js.asString(this); + } + + @JsOverlay + default Uint8Array asUint8Array() { + return Js.cast(this); + } + + @JsOverlay + default boolean isString() { + return (Object) this instanceof String; + } + + @JsOverlay + default boolean isUint8Array() { + return (Object) this instanceof Uint8Array; + } + } + + @JsOverlay + static InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + String getTypeUrl(); + + @JsProperty + InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType.GetValueUnionType getValue(); + + @JsProperty + void setTypeUrl(String typeUrl); + + @JsProperty + void setValue( + InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType.GetValueUnionType value); + + @JsOverlay + default void setValue(String value) { + setValue( + Js.uncheckedCast( + value)); + } + + @JsOverlay + default void setValue(Uint8Array value) { + setValue( + Js.uncheckedCast( + value)); + } + } + + @JsOverlay + static InputTableColumnInfo.ToObjectReturnType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + double getKind(); + + @JsProperty + JsArray getRestrictionsList(); + + @JsProperty + void setKind(double kind); + + @JsProperty + void setRestrictionsList( + JsArray restrictionsList); + + @JsOverlay + default void setRestrictionsList( + InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType[] restrictionsList) { + setRestrictionsList( + Js.>uncheckedCast( + restrictionsList)); + } + } + + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType0 { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface RestrictionsListFieldType { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface GetValueUnionType { + @JsOverlay + static InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType.GetValueUnionType of( + Object o) { + return Js.cast(o); + } + + @JsOverlay + default String asString() { + return Js.asString(this); + } + + @JsOverlay + default Uint8Array asUint8Array() { + return Js.cast(this); + } + + @JsOverlay + default boolean isString() { + return (Object) this instanceof String; + } + + @JsOverlay + default boolean isUint8Array() { + return (Object) this instanceof Uint8Array; + } + } + + @JsOverlay + static InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + String getTypeUrl(); + + @JsProperty + InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType.GetValueUnionType getValue(); + + @JsProperty + void setTypeUrl(String typeUrl); + + @JsProperty + void setValue( + InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType.GetValueUnionType value); + + @JsOverlay + default void setValue(String value) { + setValue( + Js.uncheckedCast( + value)); + } + + @JsOverlay + default void setValue(Uint8Array value) { + setValue( + Js.uncheckedCast( + value)); + } + } + + @JsOverlay + static InputTableColumnInfo.ToObjectReturnType0 create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + double getKind(); + + @JsProperty + JsArray getRestrictionsList(); + + @JsProperty + void setKind(double kind); + + @JsProperty + void setRestrictionsList( + JsArray restrictionsList); + + @JsOverlay + default void setRestrictionsList( + InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType[] restrictionsList) { + setRestrictionsList( + Js.>uncheckedCast( + restrictionsList)); + } + } + + public static KindMap Kind; + + public static native InputTableColumnInfo deserializeBinary(Uint8Array bytes); + + public static native InputTableColumnInfo deserializeBinaryFromReader( + InputTableColumnInfo message, Object reader); + + public static native void serializeBinaryToWriter(InputTableColumnInfo message, Object writer); + + public static native InputTableColumnInfo.ToObjectReturnType toObject( + boolean includeInstance, InputTableColumnInfo msg); + + public native Object addRestrictions(); + + public native Object addRestrictions(Object value, double index); + + public native Object addRestrictions(Object value); + + public native void clearRestrictionsList(); + + public native double getKind(); + + public native JsArray getRestrictionsList(); + + public native Uint8Array serializeBinary(); + + public native void setKind(double value); + + public native void setRestrictionsList(JsArray value); + + @JsOverlay + public final void setRestrictionsList(Object[] value) { + setRestrictionsList(Js.>uncheckedCast(value)); + } + + public native InputTableColumnInfo.ToObjectReturnType0 toObject(); + + public native InputTableColumnInfo.ToObjectReturnType0 toObject(boolean includeInstance); +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableMetadata.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableMetadata.java new file mode 100644 index 00000000000..b648aaabb65 --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableMetadata.java @@ -0,0 +1,77 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; + +import elemental2.core.JsArray; +import elemental2.core.Uint8Array; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsProperty; +import jsinterop.annotations.JsType; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.InputTableMetadata", + namespace = JsPackage.GLOBAL) +public class InputTableMetadata { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType { + @JsOverlay + static InputTableMetadata.ToObjectReturnType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + JsArray> getColumnInfoMap(); + + @JsProperty + void setColumnInfoMap(JsArray> columnInfoMap); + + @JsOverlay + default void setColumnInfoMap(Object[][] columnInfoMap) { + setColumnInfoMap(Js.>>uncheckedCast(columnInfoMap)); + } + } + + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType0 { + @JsOverlay + static InputTableMetadata.ToObjectReturnType0 create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + JsArray> getColumnInfoMap(); + + @JsProperty + void setColumnInfoMap(JsArray> columnInfoMap); + + @JsOverlay + default void setColumnInfoMap(Object[][] columnInfoMap) { + setColumnInfoMap(Js.>>uncheckedCast(columnInfoMap)); + } + } + + public static native InputTableMetadata deserializeBinary(Uint8Array bytes); + + public static native InputTableMetadata deserializeBinaryFromReader( + InputTableMetadata message, Object reader); + + public static native void serializeBinaryToWriter(InputTableMetadata message, Object writer); + + public static native InputTableMetadata.ToObjectReturnType toObject( + boolean includeInstance, InputTableMetadata msg); + + public native void clearColumnInfoMap(); + + public native Object getColumnInfoMap(); + + public native Uint8Array serializeBinary(); + + public native InputTableMetadata.ToObjectReturnType0 toObject(); + + public native InputTableMetadata.ToObjectReturnType0 toObject(boolean includeInstance); +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/IntegerRangeRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/IntegerRangeRestriction.java new file mode 100644 index 00000000000..080104220c7 --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/IntegerRangeRestriction.java @@ -0,0 +1,90 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; + +import elemental2.core.Uint8Array; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsProperty; +import jsinterop.annotations.JsType; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.IntegerRangeRestriction", + namespace = JsPackage.GLOBAL) +public class IntegerRangeRestriction { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType { + @JsOverlay + static IntegerRangeRestriction.ToObjectReturnType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + double getMaxInclusive(); + + @JsProperty + double getMinInclusive(); + + @JsProperty + void setMaxInclusive(double maxInclusive); + + @JsProperty + void setMinInclusive(double minInclusive); + } + + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType0 { + @JsOverlay + static IntegerRangeRestriction.ToObjectReturnType0 create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + double getMaxInclusive(); + + @JsProperty + double getMinInclusive(); + + @JsProperty + void setMaxInclusive(double maxInclusive); + + @JsProperty + void setMinInclusive(double minInclusive); + } + + public static native IntegerRangeRestriction deserializeBinary(Uint8Array bytes); + + public static native IntegerRangeRestriction deserializeBinaryFromReader( + IntegerRangeRestriction message, Object reader); + + public static native void serializeBinaryToWriter(IntegerRangeRestriction message, Object writer); + + public static native IntegerRangeRestriction.ToObjectReturnType toObject( + boolean includeInstance, IntegerRangeRestriction msg); + + public native void clearMaxInclusive(); + + public native void clearMinInclusive(); + + public native double getMaxInclusive(); + + public native double getMinInclusive(); + + public native boolean hasMaxInclusive(); + + public native boolean hasMinInclusive(); + + public native Uint8Array serializeBinary(); + + public native void setMaxInclusive(double value); + + public native void setMinInclusive(double value); + + public native IntegerRangeRestriction.ToObjectReturnType0 toObject(); + + public native IntegerRangeRestriction.ToObjectReturnType0 toObject(boolean includeInstance); +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NonEmptyRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NonEmptyRestriction.java new file mode 100644 index 00000000000..06b62df8e7a --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NonEmptyRestriction.java @@ -0,0 +1,29 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; + +import elemental2.core.Uint8Array; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsType; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.NonEmptyRestriction", + namespace = JsPackage.GLOBAL) +public class NonEmptyRestriction { + public static native NonEmptyRestriction deserializeBinary(Uint8Array bytes); + + public static native NonEmptyRestriction deserializeBinaryFromReader( + NonEmptyRestriction message, Object reader); + + public static native void serializeBinaryToWriter(NonEmptyRestriction message, Object writer); + + public static native Object toObject(boolean includeInstance, NonEmptyRestriction msg); + + public native Uint8Array serializeBinary(); + + public native Object toObject(); + + public native Object toObject(boolean includeInstance); +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NotNullRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NotNullRestriction.java new file mode 100644 index 00000000000..c8d4f19d133 --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NotNullRestriction.java @@ -0,0 +1,29 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; + +import elemental2.core.Uint8Array; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsType; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.NotNullRestriction", + namespace = JsPackage.GLOBAL) +public class NotNullRestriction { + public static native NotNullRestriction deserializeBinary(Uint8Array bytes); + + public static native NotNullRestriction deserializeBinaryFromReader( + NotNullRestriction message, Object reader); + + public static native void serializeBinaryToWriter(NotNullRestriction message, Object writer); + + public static native Object toObject(boolean includeInstance, NotNullRestriction msg); + + public native Uint8Array serializeBinary(); + + public native Object toObject(); + + public native Object toObject(boolean includeInstance); +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/StringListRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/StringListRestriction.java new file mode 100644 index 00000000000..bbbbf8b7896 --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/StringListRestriction.java @@ -0,0 +1,88 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; + +import elemental2.core.JsArray; +import elemental2.core.Uint8Array; +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsProperty; +import jsinterop.annotations.JsType; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.StringListRestriction", + namespace = JsPackage.GLOBAL) +public class StringListRestriction { + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType { + @JsOverlay + static StringListRestriction.ToObjectReturnType create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + JsArray getAllowedValuesList(); + + @JsProperty + void setAllowedValuesList(JsArray allowedValuesList); + + @JsOverlay + default void setAllowedValuesList(String[] allowedValuesList) { + setAllowedValuesList(Js.>uncheckedCast(allowedValuesList)); + } + } + + @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) + public interface ToObjectReturnType0 { + @JsOverlay + static StringListRestriction.ToObjectReturnType0 create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty + JsArray getAllowedValuesList(); + + @JsProperty + void setAllowedValuesList(JsArray allowedValuesList); + + @JsOverlay + default void setAllowedValuesList(String[] allowedValuesList) { + setAllowedValuesList(Js.>uncheckedCast(allowedValuesList)); + } + } + + public static native StringListRestriction deserializeBinary(Uint8Array bytes); + + public static native StringListRestriction deserializeBinaryFromReader( + StringListRestriction message, Object reader); + + public static native void serializeBinaryToWriter(StringListRestriction message, Object writer); + + public static native StringListRestriction.ToObjectReturnType toObject( + boolean includeInstance, StringListRestriction msg); + + public native String addAllowedValues(String value, double index); + + public native String addAllowedValues(String value); + + public native void clearAllowedValuesList(); + + public native JsArray getAllowedValuesList(); + + public native Uint8Array serializeBinary(); + + public native void setAllowedValuesList(JsArray value); + + @JsOverlay + public final void setAllowedValuesList(String[] value) { + setAllowedValuesList(Js.>uncheckedCast(value)); + } + + public native StringListRestriction.ToObjectReturnType0 toObject(); + + public native StringListRestriction.ToObjectReturnType0 toObject(boolean includeInstance); +} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/inputtablecolumninfo/KindMap.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/inputtablecolumninfo/KindMap.java new file mode 100644 index 00000000000..2e1c4d52d29 --- /dev/null +++ b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/inputtablecolumninfo/KindMap.java @@ -0,0 +1,40 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// +package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.inputtablecolumninfo; + +import jsinterop.annotations.JsOverlay; +import jsinterop.annotations.JsPackage; +import jsinterop.annotations.JsProperty; +import jsinterop.annotations.JsType; +import jsinterop.base.Js; +import jsinterop.base.JsPropertyMap; + +@JsType( + isNative = true, + name = "dhinternal.io.deephaven_core.proto.inputtable_pb.InputTableColumnInfo.KindMap", + namespace = JsPackage.GLOBAL) +public interface KindMap { + @JsOverlay + static KindMap create() { + return Js.uncheckedCast(JsPropertyMap.of()); + } + + @JsProperty(name = "KIND_KEY") + double getKIND_KEY(); + + @JsProperty(name = "KIND_UNKNOWN") + double getKIND_UNKNOWN(); + + @JsProperty(name = "KIND_VALUE") + double getKIND_VALUE(); + + @JsProperty(name = "KIND_KEY") + void setKIND_KEY(double KIND_KEY); + + @JsProperty(name = "KIND_UNKNOWN") + void setKIND_UNKNOWN(double KIND_UNKNOWN); + + @JsProperty(name = "KIND_VALUE") + void setKIND_VALUE(double KIND_VALUE); +} From 14a5807a2c6ff82653c5909aec88b8ac4f441d24 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Mon, 20 Apr 2026 09:41:21 -0600 Subject: [PATCH 14/35] use generated protobug messages failing on Any --- .../client/api/barrage/WebBarrageUtils.java | 123 +++--- .../barrage/util/ColumnRestrictionUtils.java | 379 ++++++------------ 2 files changed, 194 insertions(+), 308 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index e0f2beb8f1b..08932a862f5 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -8,6 +8,8 @@ import elemental2.dom.DomGlobal; import io.deephaven.barrage.flatbuf.BarrageMessageType; import io.deephaven.barrage.flatbuf.BarrageMessageWrapper; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.DeephavenTableMetadata; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.InputTableColumnInfo; import io.deephaven.web.client.api.barrage.def.ColumnDefinition; import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; import io.deephaven.web.client.api.barrage.def.InputTableMetadata; @@ -37,6 +39,7 @@ public class WebBarrageUtils { private static final Map restrictionConverters = new HashMap<>(); static { + // Register default converters registerColumnRestrictionConverter("IntegerRangeRestriction", ColumnRestrictionUtils::convertIntegerRangeRestriction); registerColumnRestrictionConverter("DoubleRangeRestriction", ColumnRestrictionUtils::convertDoubleRangeRestriction); @@ -55,16 +58,6 @@ public static void registerColumnRestrictionConverter(String restrictionType, Co restrictionConverters.put(restrictionType, converter); } - /** - * Get the converter for a specific restriction type. - * - * @param restrictionType The type name of the restriction - * @return The converter, or null if none is registered - */ - static ColumnRestrictionConverter getColumnRestrictionConverter(String restrictionType) { - return restrictionConverters.get(restrictionType); - } - public static Uint8Array wrapMessage(FlatBufferBuilder innerBuilder, byte messageType) { FlatBufferBuilder outerBuilder = new FlatBufferBuilder(1024); int messageOffset = BarrageMessageWrapper.createMsgPayloadVector(outerBuilder, innerBuilder.dataBuffer()); @@ -124,50 +117,79 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD InputTableMetadata metadata = new InputTableMetadata(); try { - // Decode base64 to Uint8Array (like Java's Base64.getDecoder().decode()) + JsLog.warn("parseInputTableMetadata: Decoding base64..."); + // Decode base64 to Uint8Array Uint8Array bytes = decodeBase64(tableMetadataBase64); + JsLog.warn("parseInputTableMetadata: Decoded " + bytes.length + " bytes"); + + JsLog.warn("parseInputTableMetadata: Deserializing DeephavenTableMetadata..."); + // Deserialize using generated protobuf class (google.protobuf.Any stub is now available) + DeephavenTableMetadata tableMetadata = DeephavenTableMetadata.deserializeBinary(bytes); + JsLog.warn("parseInputTableMetadata: Successfully deserialized DeephavenTableMetadata"); - // The issue: JavaScript protobuf deserialization fails on google.protobuf.Any types - // Solution: Parse the protobuf manually at the binary level to extract what we need - jsinterop.base.Any result = ColumnRestrictionUtils.parseProtoManually(bytes); + if (!tableMetadata.hasInputTableMetadata()) { + JsLog.warn("parseInputTableMetadata: No input table metadata present"); + return metadata; + } + + io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.InputTableMetadata protoInputTableMetadata = + tableMetadata.getInputTableMetadata(); + JsLog.warn("parseInputTableMetadata: Got InputTableMetadata"); + + // Get the column info map + Object columnInfoMapRaw = protoInputTableMetadata.getColumnInfoMap(); - if (result == null) { + if (columnInfoMapRaw == null) { + JsLog.warn("parseInputTableMetadata: columnInfoMap is null"); return metadata; } - // Extract column restrictions from the manually parsed data + JsLog.warn("parseInputTableMetadata: Processing " + cols.length + " columns"); + // Extract column restrictions from the column info map for (ColumnDefinition col : cols) { String columnName = col.getName(); - jsinterop.base.Any columnInfo = getPropertyFromMap(result, columnName); + InputTableColumnInfo columnInfo = getColumnInfoFromMap(columnInfoMapRaw, columnName); if (columnInfo != null) { - // Get restrictions array if available - jsinterop.base.Any restrictionsList = getProperty(columnInfo, "restrictions"); - if (restrictionsList != null && isArray(restrictionsList)) { - elemental2.core.JsArray restrictions = - jsinterop.base.Js.uncheckedCast(restrictionsList); - - if (restrictions.length > 0) { - InputTableMetadata.ColumnRestrictions colRestrictions = - new InputTableMetadata.ColumnRestrictions(); - for (int i = 0; i < restrictions.length; i++) { - jsinterop.base.Any restrictionData = restrictions.getAt(i); - - // Get the restriction type and convert it - String restrictionType = getRestrictionType(restrictionData); - if (restrictionType != null) { - ColumnRestrictionConverter converter = getColumnRestrictionConverter(restrictionType); - if (converter != null) { - io.deephaven.web.client.api.ColumnRestriction restriction = converter.convert(restrictionData); + JsLog.warn("parseInputTableMetadata: Found column info for: " + columnName); + JsArray restrictionsList = columnInfo.getRestrictionsList(); + + if (restrictionsList != null && restrictionsList.length > 0) { + JsLog.warn("parseInputTableMetadata: Column " + columnName + " has " + restrictionsList.length + " restrictions"); + InputTableMetadata.ColumnRestrictions colRestrictions = + new InputTableMetadata.ColumnRestrictions(); + + for (int i = 0; i < restrictionsList.length; i++) { + Object restrictionAny = restrictionsList.getAt(i); + + // Get the restriction type and look up the converter + String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny); + JsLog.warn("parseInputTableMetadata: Restriction " + i + " type: " + restrictionType); + + if (restrictionType != null) { + ColumnRestrictionConverter converter = restrictionConverters.get(restrictionType); + + if (converter != null) { + JsLog.warn("parseInputTableMetadata: Converting restriction..."); + io.deephaven.web.client.api.ColumnRestriction restriction = + converter.convert(jsinterop.base.Js.cast(restrictionAny)); + if (restriction != null) { colRestrictions.addRestriction(restriction); + JsLog.warn("parseInputTableMetadata: Successfully added restriction"); + } else { + JsLog.warn("parseInputTableMetadata: Converter returned null for " + restrictionType); } + } else { + JsLog.warn("No converter registered for restriction type: " + restrictionType); } } - metadata.addColumnRestrictions(columnName, colRestrictions); } + + metadata.addColumnRestrictions(columnName, colRestrictions); } } } + JsLog.warn("parseInputTableMetadata: Successfully parsed all restrictions"); } catch (Exception e) { JsLog.warn("Failed to parse input table metadata:", e); } @@ -175,6 +197,12 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD return metadata; } + // Helper method to extract InputTableColumnInfo from the map + private static native InputTableColumnInfo getColumnInfoFromMap(Object map, String key) /*-{ + if (!map || !map.get) return null; + return map.get(key); + }-*/; + // Decode base64 string to Uint8Array using elemental2 private static Uint8Array decodeBase64(String base64) { // Use DomGlobal.atob() to decode base64 to binary string @@ -188,29 +216,6 @@ private static Uint8Array decodeBase64(String base64) { return bytes; } - private static native String getRestrictionType(jsinterop.base.Any restrictionData) /*-{ - if (!restrictionData) return null; - return restrictionData.type || null; - }-*/; - - private static native jsinterop.base.Any getPropertyFromMap(jsinterop.base.Any map, String key) /*-{ - if (map == null) return null; - return map[key] || null; - }-*/; - - private static native jsinterop.base.Any getProperty(jsinterop.base.Any obj, String propertyName) /*-{ - if (obj == null) return null; - var getter = 'get' + propertyName.charAt(0).toUpperCase() + propertyName.slice(1); - if (typeof obj[getter] === 'function') { - return obj[getter](); - } - return obj[propertyName]; - }-*/; - - - private static native boolean isArray(jsinterop.base.Any obj) /*-{ - return Array.isArray(obj); - }-*/; private static ColumnDefinition[] readColumnDefinitions(Schema schema) { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java index 858d217c820..9c48d3c4808 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -5,12 +5,18 @@ import elemental2.core.JsArray; import elemental2.core.Uint8Array; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.DoubleRangeRestriction; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.IntegerRangeRestriction; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.NonEmptyRestriction; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.NotNullRestriction; +import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.StringListRestriction; import io.deephaven.web.client.api.ColumnRestriction; import io.deephaven.web.client.fu.JsLog; import jsinterop.base.Any; +import jsinterop.base.Js; /** - * Utility class for parsing column restrictions from protobuf data. + * Utility class for converting protobuf column restrictions to ColumnRestriction objects. */ public class ColumnRestrictionUtils { @@ -19,285 +25,160 @@ private ColumnRestrictionUtils() { } /** - * Manually parse protobuf bytes to extract input table metadata and column restrictions. - * This works around issues with google.protobuf.Any deserialization in JavaScript. + * Extract the restriction type short name from a google.protobuf.Any object. * - * @param bytes The protobuf bytes to parse - * @return A map of column names to their metadata, or null if parsing fails + * @param restrictionAny The google.protobuf.Any object containing the restriction + * @return The short type name (e.g., "IntegerRangeRestriction"), or null if not found */ - public static native Any parseProtoManually(Uint8Array bytes) /*-{ - // Manual protobuf parsing to work around missing google.protobuf.Any - // Based on the pattern from AddToInputTable.java - // Use the BinaryReader from dhinternal.jspb which is the JsInterop wrapper - try { - // Use the JsInterop BinaryReader class - var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); - var reader = new BinaryReader(bytes); - var result = {}; - - // Parse the DeephavenTableMetadata message - // Field 1 is InputTableMetadata - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - - if (field === 1) { - // This is the InputTableMetadata field - var inputTableMetadata = {}; - reader.readMessage(inputTableMetadata, function(metadata, rdr) { - // Parse InputTableMetadata - // Field 1 is columnInfoMap (map) - while (rdr.nextField()) { - if (rdr.isEndGroup()) { - break; - } - var subfield = rdr.getFieldNumber(); - - if (subfield === 1) { - // This is the columnInfoMap - var mapEntry = {}; - rdr.readMessage(mapEntry, function(entry, mapReader) { - var key = null; - var value = null; - - while (mapReader.nextField()) { - if (mapReader.isEndGroup()) { - break; - } - var mapField = mapReader.getFieldNumber(); - - if (mapField === 1) { - // Map key (column name) - key = mapReader.readString(); - } else if (mapField === 2) { - // Map value (InputTableColumnInfo) - value = {}; - var restrictions = []; - - mapReader.readMessage(value, function(columnInfo, colReader) { - while (colReader.nextField()) { - if (colReader.isEndGroup()) { - break; - } - var colField = colReader.getFieldNumber(); - - if (colField === 1) { - // kind field - columnInfo.kind = colReader.readEnum(); - } else if (colField === 2) { - // restrictions field (repeated google.protobuf.Any) - // Parse the Any message to extract type_url and value - try { - var anyBytes = colReader.readBytes(); - - // Parse the google.protobuf.Any message - // Field 1 = type_url (string) - // Field 2 = value (bytes) - var anyReader = new BinaryReader(anyBytes); - var typeUrl = null; - var valueBytes = null; - - while (anyReader.nextField()) { - if (anyReader.isEndGroup()) { - break; - } - var anyField = anyReader.getFieldNumber(); - if (anyField === 1) { - typeUrl = anyReader.readString(); - } else if (anyField === 2) { - valueBytes = anyReader.readBytes(); - } - } - - // Now parse the actual restriction based on type_url - var restriction = @io.deephaven.web.client.api.barrage.util.ColumnRestrictionUtils::parseRestriction(*)(typeUrl, valueBytes); - if (restriction) { - restrictions.push(restriction); - } - } catch (e) { - @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to parse restriction:", e); - } - } - } - columnInfo.restrictions = restrictions; - }); - } - } - - if (key !== null && value !== null) { - if (!metadata.columnInfoMap) { - metadata.columnInfoMap = {}; - } - metadata.columnInfoMap[key] = value; - } - }); - } - } - }); - - result = inputTableMetadata.columnInfoMap || {}; - } - } + public static String getRestrictionType(Object restrictionAny) { + String typeUrl = getTypeUrl(restrictionAny); + if (typeUrl == null) { + return null; + } - return result; + // Extract the restriction type from the type URL + // Format: "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" + // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" + String typeName = typeUrl.substring(typeUrl.lastIndexOf('/') + 1); + return typeName.substring(typeName.lastIndexOf('.') + 1); + } - } catch (e) { - @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to manually parse protobuf:", e); - return null; + /** + * Extract the type URL from a google.protobuf.Any object. + */ + private static native String getTypeUrl(Object anyObj) /*-{ + if (!anyObj) return null; + if (typeof anyObj.getTypeUrl === 'function') { + return anyObj.getTypeUrl(); + } + if (anyObj.type_url) { + return anyObj.type_url; } + return null; }-*/; /** - * Parse a specific restriction type from protobuf bytes. - * - * @param typeUrl The type URL of the restriction - * @param valueBytes The protobuf bytes containing the restriction data - * @return The parsed restriction data, or null if parsing fails + * Extract the value bytes from a google.protobuf.Any object. */ - private static native Any parseRestriction(String typeUrl, Uint8Array valueBytes) /*-{ - // Parse specific restriction types based on typeUrl - // Types from inputtable.proto: - // - IntegerRangeRestriction - // - DoubleRangeRestriction - // - NotNullRestriction - // - NonEmptyRestriction - // - StringListRestriction + private static native Uint8Array getValue(Object anyObj) /*-{ + if (!anyObj) return null; + if (typeof anyObj.getValue_asU8 === 'function') { + return anyObj.getValue_asU8(); + } + if (typeof anyObj.getValue === 'function') { + var value = anyObj.getValue(); + if (value) return value; + } + if (anyObj.value && anyObj.value instanceof Uint8Array) { + return anyObj.value; + } + return null; + }-*/; + /** + * Convert IntegerRangeRestriction data into a ColumnRestriction object. + */ + public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAny) { try { - var BinaryReader = @io.deephaven.javascript.proto.dhinternal.jspb.BinaryReader::new(Lelemental2/core/Uint8Array;); - var reader = new BinaryReader(valueBytes); - - // Extract the message type from the type URL - // Format: "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" - // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" - var typeName = typeUrl.substring(typeUrl.lastIndexOf('/') + 1); - var shortName = typeName.substring(typeName.lastIndexOf('.') + 1); - - var restriction = { - type: shortName, - typeUrl: typeUrl - }; - - if (shortName === 'IntegerRangeRestriction') { - // Field 1 = min_inclusive (int64) - // Field 2 = max_inclusive (int64) - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - restriction.minInclusive = reader.readInt64(); - } else if (field === 2) { - restriction.maxInclusive = reader.readInt64(); - } - } - } else if (shortName === 'DoubleRangeRestriction') { - // Field 1 = min_inclusive (double) - // Field 2 = max_inclusive (double) - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - restriction.minInclusive = reader.readDouble(); - } else if (field === 2) { - restriction.maxInclusive = reader.readDouble(); - } - } - } else if (shortName === 'NotNullRestriction') { - // No fields - just the type - restriction.notNull = true; - } else if (shortName === 'NonEmptyRestriction') { - // No fields - just the type - restriction.nonEmpty = true; - } else if (shortName === 'StringListRestriction') { - // Field 1 = allowed_values (repeated string) - restriction.allowedValues = []; - while (reader.nextField()) { - if (reader.isEndGroup()) break; - var field = reader.getFieldNumber(); - if (field === 1) { - var value = reader.readString(); - restriction.allowedValues.push(value); - } - } - } else { - restriction.raw = valueBytes; + Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); + if (valueBytes == null) { + JsLog.warn("No value bytes found in IntegerRangeRestriction Any object"); + return null; } - return restriction; + IntegerRangeRestriction restriction = IntegerRangeRestriction.deserializeBinary(valueBytes); + double minValue = restriction.hasMinInclusive() ? restriction.getMinInclusive() : Double.NaN; + double maxValue = restriction.hasMaxInclusive() ? restriction.getMaxInclusive() : Double.NaN; - } catch (e) { - @io.deephaven.web.client.fu.JsLog::warn(*)("Failed to parse restriction:", e); + return new ColumnRestriction("IntegerRangeRestriction", minValue, maxValue); + } catch (Exception e) { + JsLog.warn("Failed to convert IntegerRangeRestriction:", e); return null; } - }-*/; - - /** - * Convert IntegerRangeRestriction data into a ColumnRestriction object. - * - * @param restrictionData The parsed restriction data from protobuf - * @return A ColumnRestriction object, or null if conversion fails - */ - public static native ColumnRestriction convertIntegerRangeRestriction(Any restrictionData) /*-{ - if (!restrictionData) return null; - var minValue = restrictionData.minInclusive !== undefined ? restrictionData.minInclusive : NaN; - var maxValue = restrictionData.maxInclusive !== undefined ? restrictionData.maxInclusive : NaN; - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;DD)( - "IntegerRangeRestriction", minValue, maxValue - ); - }-*/; + } /** * Convert DoubleRangeRestriction data into a ColumnRestriction object. - * - * @param restrictionData The parsed restriction data from protobuf - * @return A ColumnRestriction object, or null if conversion fails */ - public static native ColumnRestriction convertDoubleRangeRestriction(Any restrictionData) /*-{ - if (!restrictionData) return null; - var minValue = restrictionData.minInclusive !== undefined ? restrictionData.minInclusive : NaN; - var maxValue = restrictionData.maxInclusive !== undefined ? restrictionData.maxInclusive : NaN; - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;DD)( - "DoubleRangeRestriction", minValue, maxValue - ); - }-*/; + public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny) { + try { + Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); + if (valueBytes == null) { + JsLog.warn("No value bytes found in DoubleRangeRestriction Any object"); + return null; + } + + DoubleRangeRestriction restriction = DoubleRangeRestriction.deserializeBinary(valueBytes); + double minValue = restriction.hasMinInclusive() ? restriction.getMinInclusive() : Double.NaN; + double maxValue = restriction.hasMaxInclusive() ? restriction.getMaxInclusive() : Double.NaN; + + return new ColumnRestriction("DoubleRangeRestriction", minValue, maxValue); + } catch (Exception e) { + JsLog.warn("Failed to convert DoubleRangeRestriction:", e); + return null; + } + } /** * Convert NotNullRestriction data into a ColumnRestriction object. - * - * @param restrictionData The parsed restriction data from protobuf - * @return A ColumnRestriction object, or null if conversion fails */ - public static native ColumnRestriction convertNotNullRestriction(Any restrictionData) /*-{ - if (!restrictionData) return null; - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)("NotNullRestriction"); - }-*/; + public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) { + try { + Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); + if (valueBytes == null) { + JsLog.warn("No value bytes found in NotNullRestriction Any object"); + return null; + } + + NotNullRestriction.deserializeBinary(valueBytes); // Just to validate + return new ColumnRestriction("NotNullRestriction"); + } catch (Exception e) { + JsLog.warn("Failed to convert NotNullRestriction:", e); + return null; + } + } /** * Convert NonEmptyRestriction data into a ColumnRestriction object. - * - * @param restrictionData The parsed restriction data from protobuf - * @return A ColumnRestriction object, or null if conversion fails */ - public static native ColumnRestriction convertNonEmptyRestriction(Any restrictionData) /*-{ - if (!restrictionData) return null; - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;)("NonEmptyRestriction"); - }-*/; + public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) { + try { + Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); + if (valueBytes == null) { + JsLog.warn("No value bytes found in NonEmptyRestriction Any object"); + return null; + } + + NonEmptyRestriction.deserializeBinary(valueBytes); // Just to validate + return new ColumnRestriction("NonEmptyRestriction"); + } catch (Exception e) { + JsLog.warn("Failed to convert NonEmptyRestriction:", e); + return null; + } + } /** * Convert StringListRestriction data into a ColumnRestriction object. - * - * @param restrictionData The parsed restriction data from protobuf - * @return A ColumnRestriction object, or null if conversion fails */ - public static native ColumnRestriction convertStringListRestriction(Any restrictionData) /*-{ - if (!restrictionData) return null; - var allowedValues = restrictionData.allowedValues || []; - // Use the JS array directly - it will be cast to JsArray automatically - return @io.deephaven.web.client.api.ColumnRestriction::new(Ljava/lang/String;Lelemental2/core/JsArray;)( - "StringListRestriction", allowedValues - ); - }-*/; + public static ColumnRestriction convertStringListRestriction(Any restrictionAny) { + try { + Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); + if (valueBytes == null) { + JsLog.warn("No value bytes found in StringListRestriction Any object"); + return null; + } + + StringListRestriction restriction = StringListRestriction.deserializeBinary(valueBytes); + JsArray allowedValues = restriction.getAllowedValuesList(); + + // Cast JsArray to JsArray to match constructor signature + JsArray allowedValuesAsAny = Js.uncheckedCast(allowedValues); + + return new ColumnRestriction("StringListRestriction", allowedValuesAsAny); + } catch (Exception e) { + JsLog.warn("Failed to convert StringListRestriction:", e); + return null; + } + } } From 3822538409554eee54e219e408f6adbc053a7b13 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Mon, 20 Apr 2026 12:25:12 -0600 Subject: [PATCH 15/35] Revert "Generated input table types" This reverts commit feed9b9a0d7f42832a08bf3eac3858d463dc7fba. --- .../inputtable_pb/DeephavenTableMetadata.java | 113 -------- .../inputtable_pb/DoubleRangeRestriction.java | 90 ------- .../inputtable_pb/InputTableColumnInfo.java | 245 ------------------ .../inputtable_pb/InputTableMetadata.java | 77 ------ .../IntegerRangeRestriction.java | 90 ------- .../inputtable_pb/NonEmptyRestriction.java | 29 --- .../inputtable_pb/NotNullRestriction.java | 29 --- .../inputtable_pb/StringListRestriction.java | 88 ------- .../inputtablecolumninfo/KindMap.java | 40 --- 9 files changed, 801 deletions(-) delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DeephavenTableMetadata.java delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DoubleRangeRestriction.java delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableColumnInfo.java delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableMetadata.java delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/IntegerRangeRestriction.java delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NonEmptyRestriction.java delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NotNullRestriction.java delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/StringListRestriction.java delete mode 100644 web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/inputtablecolumninfo/KindMap.java diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DeephavenTableMetadata.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DeephavenTableMetadata.java deleted file mode 100644 index 60381f04764..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DeephavenTableMetadata.java +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; - -import elemental2.core.JsArray; -import elemental2.core.Uint8Array; -import jsinterop.annotations.JsOverlay; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; -import jsinterop.base.Js; -import jsinterop.base.JsPropertyMap; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.DeephavenTableMetadata", - namespace = JsPackage.GLOBAL) -public class DeephavenTableMetadata { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface InputTableMetadataFieldType { - @JsOverlay - static DeephavenTableMetadata.ToObjectReturnType.InputTableMetadataFieldType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - JsArray> getColumnInfoMap(); - - @JsProperty - void setColumnInfoMap(JsArray> columnInfoMap); - - @JsOverlay - default void setColumnInfoMap(Object[][] columnInfoMap) { - setColumnInfoMap(Js.>>uncheckedCast(columnInfoMap)); - } - } - - @JsOverlay - static DeephavenTableMetadata.ToObjectReturnType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - DeephavenTableMetadata.ToObjectReturnType.InputTableMetadataFieldType getInputTableMetadata(); - - @JsProperty - void setInputTableMetadata( - DeephavenTableMetadata.ToObjectReturnType.InputTableMetadataFieldType inputTableMetadata); - } - - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType0 { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface InputTableMetadataFieldType { - @JsOverlay - static DeephavenTableMetadata.ToObjectReturnType0.InputTableMetadataFieldType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - JsArray> getColumnInfoMap(); - - @JsProperty - void setColumnInfoMap(JsArray> columnInfoMap); - - @JsOverlay - default void setColumnInfoMap(Object[][] columnInfoMap) { - setColumnInfoMap(Js.>>uncheckedCast(columnInfoMap)); - } - } - - @JsOverlay - static DeephavenTableMetadata.ToObjectReturnType0 create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - DeephavenTableMetadata.ToObjectReturnType0.InputTableMetadataFieldType getInputTableMetadata(); - - @JsProperty - void setInputTableMetadata( - DeephavenTableMetadata.ToObjectReturnType0.InputTableMetadataFieldType inputTableMetadata); - } - - public static native DeephavenTableMetadata deserializeBinary(Uint8Array bytes); - - public static native DeephavenTableMetadata deserializeBinaryFromReader( - DeephavenTableMetadata message, Object reader); - - public static native void serializeBinaryToWriter(DeephavenTableMetadata message, Object writer); - - public static native DeephavenTableMetadata.ToObjectReturnType toObject( - boolean includeInstance, DeephavenTableMetadata msg); - - public native void clearInputTableMetadata(); - - public native InputTableMetadata getInputTableMetadata(); - - public native boolean hasInputTableMetadata(); - - public native Uint8Array serializeBinary(); - - public native void setInputTableMetadata(); - - public native void setInputTableMetadata(InputTableMetadata value); - - public native DeephavenTableMetadata.ToObjectReturnType0 toObject(); - - public native DeephavenTableMetadata.ToObjectReturnType0 toObject(boolean includeInstance); -} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DoubleRangeRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DoubleRangeRestriction.java deleted file mode 100644 index 19378461d48..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/DoubleRangeRestriction.java +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; - -import elemental2.core.Uint8Array; -import jsinterop.annotations.JsOverlay; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; -import jsinterop.base.Js; -import jsinterop.base.JsPropertyMap; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.DoubleRangeRestriction", - namespace = JsPackage.GLOBAL) -public class DoubleRangeRestriction { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType { - @JsOverlay - static DoubleRangeRestriction.ToObjectReturnType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - double getMaxInclusive(); - - @JsProperty - double getMinInclusive(); - - @JsProperty - void setMaxInclusive(double maxInclusive); - - @JsProperty - void setMinInclusive(double minInclusive); - } - - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType0 { - @JsOverlay - static DoubleRangeRestriction.ToObjectReturnType0 create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - double getMaxInclusive(); - - @JsProperty - double getMinInclusive(); - - @JsProperty - void setMaxInclusive(double maxInclusive); - - @JsProperty - void setMinInclusive(double minInclusive); - } - - public static native DoubleRangeRestriction deserializeBinary(Uint8Array bytes); - - public static native DoubleRangeRestriction deserializeBinaryFromReader( - DoubleRangeRestriction message, Object reader); - - public static native void serializeBinaryToWriter(DoubleRangeRestriction message, Object writer); - - public static native DoubleRangeRestriction.ToObjectReturnType toObject( - boolean includeInstance, DoubleRangeRestriction msg); - - public native void clearMaxInclusive(); - - public native void clearMinInclusive(); - - public native double getMaxInclusive(); - - public native double getMinInclusive(); - - public native boolean hasMaxInclusive(); - - public native boolean hasMinInclusive(); - - public native Uint8Array serializeBinary(); - - public native void setMaxInclusive(double value); - - public native void setMinInclusive(double value); - - public native DoubleRangeRestriction.ToObjectReturnType0 toObject(); - - public native DoubleRangeRestriction.ToObjectReturnType0 toObject(boolean includeInstance); -} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableColumnInfo.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableColumnInfo.java deleted file mode 100644 index a18790b13ae..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableColumnInfo.java +++ /dev/null @@ -1,245 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; - -import elemental2.core.JsArray; -import elemental2.core.Uint8Array; -import io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.inputtablecolumninfo.KindMap; -import jsinterop.annotations.JsOverlay; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; -import jsinterop.base.Js; -import jsinterop.base.JsPropertyMap; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.InputTableColumnInfo", - namespace = JsPackage.GLOBAL) -public class InputTableColumnInfo { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface RestrictionsListFieldType { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface GetValueUnionType { - @JsOverlay - static InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType.GetValueUnionType of( - Object o) { - return Js.cast(o); - } - - @JsOverlay - default String asString() { - return Js.asString(this); - } - - @JsOverlay - default Uint8Array asUint8Array() { - return Js.cast(this); - } - - @JsOverlay - default boolean isString() { - return (Object) this instanceof String; - } - - @JsOverlay - default boolean isUint8Array() { - return (Object) this instanceof Uint8Array; - } - } - - @JsOverlay - static InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - String getTypeUrl(); - - @JsProperty - InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType.GetValueUnionType getValue(); - - @JsProperty - void setTypeUrl(String typeUrl); - - @JsProperty - void setValue( - InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType.GetValueUnionType value); - - @JsOverlay - default void setValue(String value) { - setValue( - Js.uncheckedCast( - value)); - } - - @JsOverlay - default void setValue(Uint8Array value) { - setValue( - Js.uncheckedCast( - value)); - } - } - - @JsOverlay - static InputTableColumnInfo.ToObjectReturnType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - double getKind(); - - @JsProperty - JsArray getRestrictionsList(); - - @JsProperty - void setKind(double kind); - - @JsProperty - void setRestrictionsList( - JsArray restrictionsList); - - @JsOverlay - default void setRestrictionsList( - InputTableColumnInfo.ToObjectReturnType.RestrictionsListFieldType[] restrictionsList) { - setRestrictionsList( - Js.>uncheckedCast( - restrictionsList)); - } - } - - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType0 { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface RestrictionsListFieldType { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface GetValueUnionType { - @JsOverlay - static InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType.GetValueUnionType of( - Object o) { - return Js.cast(o); - } - - @JsOverlay - default String asString() { - return Js.asString(this); - } - - @JsOverlay - default Uint8Array asUint8Array() { - return Js.cast(this); - } - - @JsOverlay - default boolean isString() { - return (Object) this instanceof String; - } - - @JsOverlay - default boolean isUint8Array() { - return (Object) this instanceof Uint8Array; - } - } - - @JsOverlay - static InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - String getTypeUrl(); - - @JsProperty - InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType.GetValueUnionType getValue(); - - @JsProperty - void setTypeUrl(String typeUrl); - - @JsProperty - void setValue( - InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType.GetValueUnionType value); - - @JsOverlay - default void setValue(String value) { - setValue( - Js.uncheckedCast( - value)); - } - - @JsOverlay - default void setValue(Uint8Array value) { - setValue( - Js.uncheckedCast( - value)); - } - } - - @JsOverlay - static InputTableColumnInfo.ToObjectReturnType0 create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - double getKind(); - - @JsProperty - JsArray getRestrictionsList(); - - @JsProperty - void setKind(double kind); - - @JsProperty - void setRestrictionsList( - JsArray restrictionsList); - - @JsOverlay - default void setRestrictionsList( - InputTableColumnInfo.ToObjectReturnType0.RestrictionsListFieldType[] restrictionsList) { - setRestrictionsList( - Js.>uncheckedCast( - restrictionsList)); - } - } - - public static KindMap Kind; - - public static native InputTableColumnInfo deserializeBinary(Uint8Array bytes); - - public static native InputTableColumnInfo deserializeBinaryFromReader( - InputTableColumnInfo message, Object reader); - - public static native void serializeBinaryToWriter(InputTableColumnInfo message, Object writer); - - public static native InputTableColumnInfo.ToObjectReturnType toObject( - boolean includeInstance, InputTableColumnInfo msg); - - public native Object addRestrictions(); - - public native Object addRestrictions(Object value, double index); - - public native Object addRestrictions(Object value); - - public native void clearRestrictionsList(); - - public native double getKind(); - - public native JsArray getRestrictionsList(); - - public native Uint8Array serializeBinary(); - - public native void setKind(double value); - - public native void setRestrictionsList(JsArray value); - - @JsOverlay - public final void setRestrictionsList(Object[] value) { - setRestrictionsList(Js.>uncheckedCast(value)); - } - - public native InputTableColumnInfo.ToObjectReturnType0 toObject(); - - public native InputTableColumnInfo.ToObjectReturnType0 toObject(boolean includeInstance); -} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableMetadata.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableMetadata.java deleted file mode 100644 index b648aaabb65..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/InputTableMetadata.java +++ /dev/null @@ -1,77 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; - -import elemental2.core.JsArray; -import elemental2.core.Uint8Array; -import jsinterop.annotations.JsOverlay; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; -import jsinterop.base.Js; -import jsinterop.base.JsPropertyMap; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.InputTableMetadata", - namespace = JsPackage.GLOBAL) -public class InputTableMetadata { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType { - @JsOverlay - static InputTableMetadata.ToObjectReturnType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - JsArray> getColumnInfoMap(); - - @JsProperty - void setColumnInfoMap(JsArray> columnInfoMap); - - @JsOverlay - default void setColumnInfoMap(Object[][] columnInfoMap) { - setColumnInfoMap(Js.>>uncheckedCast(columnInfoMap)); - } - } - - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType0 { - @JsOverlay - static InputTableMetadata.ToObjectReturnType0 create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - JsArray> getColumnInfoMap(); - - @JsProperty - void setColumnInfoMap(JsArray> columnInfoMap); - - @JsOverlay - default void setColumnInfoMap(Object[][] columnInfoMap) { - setColumnInfoMap(Js.>>uncheckedCast(columnInfoMap)); - } - } - - public static native InputTableMetadata deserializeBinary(Uint8Array bytes); - - public static native InputTableMetadata deserializeBinaryFromReader( - InputTableMetadata message, Object reader); - - public static native void serializeBinaryToWriter(InputTableMetadata message, Object writer); - - public static native InputTableMetadata.ToObjectReturnType toObject( - boolean includeInstance, InputTableMetadata msg); - - public native void clearColumnInfoMap(); - - public native Object getColumnInfoMap(); - - public native Uint8Array serializeBinary(); - - public native InputTableMetadata.ToObjectReturnType0 toObject(); - - public native InputTableMetadata.ToObjectReturnType0 toObject(boolean includeInstance); -} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/IntegerRangeRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/IntegerRangeRestriction.java deleted file mode 100644 index 080104220c7..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/IntegerRangeRestriction.java +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; - -import elemental2.core.Uint8Array; -import jsinterop.annotations.JsOverlay; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; -import jsinterop.base.Js; -import jsinterop.base.JsPropertyMap; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.IntegerRangeRestriction", - namespace = JsPackage.GLOBAL) -public class IntegerRangeRestriction { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType { - @JsOverlay - static IntegerRangeRestriction.ToObjectReturnType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - double getMaxInclusive(); - - @JsProperty - double getMinInclusive(); - - @JsProperty - void setMaxInclusive(double maxInclusive); - - @JsProperty - void setMinInclusive(double minInclusive); - } - - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType0 { - @JsOverlay - static IntegerRangeRestriction.ToObjectReturnType0 create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - double getMaxInclusive(); - - @JsProperty - double getMinInclusive(); - - @JsProperty - void setMaxInclusive(double maxInclusive); - - @JsProperty - void setMinInclusive(double minInclusive); - } - - public static native IntegerRangeRestriction deserializeBinary(Uint8Array bytes); - - public static native IntegerRangeRestriction deserializeBinaryFromReader( - IntegerRangeRestriction message, Object reader); - - public static native void serializeBinaryToWriter(IntegerRangeRestriction message, Object writer); - - public static native IntegerRangeRestriction.ToObjectReturnType toObject( - boolean includeInstance, IntegerRangeRestriction msg); - - public native void clearMaxInclusive(); - - public native void clearMinInclusive(); - - public native double getMaxInclusive(); - - public native double getMinInclusive(); - - public native boolean hasMaxInclusive(); - - public native boolean hasMinInclusive(); - - public native Uint8Array serializeBinary(); - - public native void setMaxInclusive(double value); - - public native void setMinInclusive(double value); - - public native IntegerRangeRestriction.ToObjectReturnType0 toObject(); - - public native IntegerRangeRestriction.ToObjectReturnType0 toObject(boolean includeInstance); -} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NonEmptyRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NonEmptyRestriction.java deleted file mode 100644 index 06b62df8e7a..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NonEmptyRestriction.java +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; - -import elemental2.core.Uint8Array; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsType; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.NonEmptyRestriction", - namespace = JsPackage.GLOBAL) -public class NonEmptyRestriction { - public static native NonEmptyRestriction deserializeBinary(Uint8Array bytes); - - public static native NonEmptyRestriction deserializeBinaryFromReader( - NonEmptyRestriction message, Object reader); - - public static native void serializeBinaryToWriter(NonEmptyRestriction message, Object writer); - - public static native Object toObject(boolean includeInstance, NonEmptyRestriction msg); - - public native Uint8Array serializeBinary(); - - public native Object toObject(); - - public native Object toObject(boolean includeInstance); -} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NotNullRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NotNullRestriction.java deleted file mode 100644 index c8d4f19d133..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/NotNullRestriction.java +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; - -import elemental2.core.Uint8Array; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsType; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.NotNullRestriction", - namespace = JsPackage.GLOBAL) -public class NotNullRestriction { - public static native NotNullRestriction deserializeBinary(Uint8Array bytes); - - public static native NotNullRestriction deserializeBinaryFromReader( - NotNullRestriction message, Object reader); - - public static native void serializeBinaryToWriter(NotNullRestriction message, Object writer); - - public static native Object toObject(boolean includeInstance, NotNullRestriction msg); - - public native Uint8Array serializeBinary(); - - public native Object toObject(); - - public native Object toObject(boolean includeInstance); -} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/StringListRestriction.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/StringListRestriction.java deleted file mode 100644 index bbbbf8b7896..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/StringListRestriction.java +++ /dev/null @@ -1,88 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb; - -import elemental2.core.JsArray; -import elemental2.core.Uint8Array; -import jsinterop.annotations.JsOverlay; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; -import jsinterop.base.Js; -import jsinterop.base.JsPropertyMap; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.StringListRestriction", - namespace = JsPackage.GLOBAL) -public class StringListRestriction { - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType { - @JsOverlay - static StringListRestriction.ToObjectReturnType create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - JsArray getAllowedValuesList(); - - @JsProperty - void setAllowedValuesList(JsArray allowedValuesList); - - @JsOverlay - default void setAllowedValuesList(String[] allowedValuesList) { - setAllowedValuesList(Js.>uncheckedCast(allowedValuesList)); - } - } - - @JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL) - public interface ToObjectReturnType0 { - @JsOverlay - static StringListRestriction.ToObjectReturnType0 create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty - JsArray getAllowedValuesList(); - - @JsProperty - void setAllowedValuesList(JsArray allowedValuesList); - - @JsOverlay - default void setAllowedValuesList(String[] allowedValuesList) { - setAllowedValuesList(Js.>uncheckedCast(allowedValuesList)); - } - } - - public static native StringListRestriction deserializeBinary(Uint8Array bytes); - - public static native StringListRestriction deserializeBinaryFromReader( - StringListRestriction message, Object reader); - - public static native void serializeBinaryToWriter(StringListRestriction message, Object writer); - - public static native StringListRestriction.ToObjectReturnType toObject( - boolean includeInstance, StringListRestriction msg); - - public native String addAllowedValues(String value, double index); - - public native String addAllowedValues(String value); - - public native void clearAllowedValuesList(); - - public native JsArray getAllowedValuesList(); - - public native Uint8Array serializeBinary(); - - public native void setAllowedValuesList(JsArray value); - - @JsOverlay - public final void setAllowedValuesList(String[] value) { - setAllowedValuesList(Js.>uncheckedCast(value)); - } - - public native StringListRestriction.ToObjectReturnType0 toObject(); - - public native StringListRestriction.ToObjectReturnType0 toObject(boolean includeInstance); -} diff --git a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/inputtablecolumninfo/KindMap.java b/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/inputtablecolumninfo/KindMap.java deleted file mode 100644 index 2e1c4d52d29..00000000000 --- a/web/client-backplane/src/main/java/io/deephaven/javascript/proto/dhinternal/io/deephaven_core/proto/inputtable_pb/inputtablecolumninfo/KindMap.java +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending -// -package io.deephaven.javascript.proto.dhinternal.io.deephaven_core.proto.inputtable_pb.inputtablecolumninfo; - -import jsinterop.annotations.JsOverlay; -import jsinterop.annotations.JsPackage; -import jsinterop.annotations.JsProperty; -import jsinterop.annotations.JsType; -import jsinterop.base.Js; -import jsinterop.base.JsPropertyMap; - -@JsType( - isNative = true, - name = "dhinternal.io.deephaven_core.proto.inputtable_pb.InputTableColumnInfo.KindMap", - namespace = JsPackage.GLOBAL) -public interface KindMap { - @JsOverlay - static KindMap create() { - return Js.uncheckedCast(JsPropertyMap.of()); - } - - @JsProperty(name = "KIND_KEY") - double getKIND_KEY(); - - @JsProperty(name = "KIND_UNKNOWN") - double getKIND_UNKNOWN(); - - @JsProperty(name = "KIND_VALUE") - double getKIND_VALUE(); - - @JsProperty(name = "KIND_KEY") - void setKIND_KEY(double KIND_KEY); - - @JsProperty(name = "KIND_UNKNOWN") - void setKIND_UNKNOWN(double KIND_UNKNOWN); - - @JsProperty(name = "KIND_VALUE") - void setKIND_VALUE(double KIND_VALUE); -} From 367b53417d49e7b5cfe4e5c88ea426dc088520d1 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Mon, 20 Apr 2026 13:38:03 -0600 Subject: [PATCH 16/35] fixes --- .../web/client/api/barrage/WebBarrageUtils.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index b0b59514d43..b7c06b7c343 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -123,7 +123,7 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD // Convert Uint8Array to ByteBuffer and deserialize using Java protobuf parseFrom ByteBuffer buffer = TypedArrayHelper.wrap(bytes); DeephavenTableMetadata tableMetadata = DeephavenTableMetadata.parseFrom(buffer); - JsLog.warn("parseInputTableMetadata: Successfully deserialized DeephavenTableMetadata"); + JsLog.warn("parseInputTableMetadata: Successfully deserialized DeephavenTableMetadata", tableMetadata); if (!tableMetadata.hasInputTableMetadata()) { JsLog.warn("parseInputTableMetadata: No input table metadata present"); @@ -132,21 +132,18 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD io.deephaven.proto.backplane.grpc.InputTableMetadata protoInputTableMetadata = tableMetadata.getInputTableMetadata(); - JsLog.warn("parseInputTableMetadata: Got InputTableMetadata"); + JsLog.warn("parseInputTableMetadata: Got InputTableMetadata", protoInputTableMetadata); // Get the column info map - Object columnInfoMapRaw = protoInputTableMetadata.getColumnInfoMap(); + Map columnInfoMap = protoInputTableMetadata.getColumnInfoMap(); - if (columnInfoMapRaw == null) { - JsLog.warn("parseInputTableMetadata: columnInfoMap is null"); - return metadata; - } +// JsLog.warn("parseInputTableMetadata: Retrieved column info map " + columnInfoMap); JsLog.warn("parseInputTableMetadata: Processing " + cols.length + " columns"); // Extract column restrictions from the column info map for (ColumnDefinition col : cols) { String columnName = col.getName(); - InputTableColumnInfo columnInfo = getColumnInfoFromMap(columnInfoMapRaw, columnName); + InputTableColumnInfo columnInfo = columnInfoMap.get(columnName); if (columnInfo != null) { JsLog.warn("parseInputTableMetadata: Found column info for: " + columnName); @@ -160,6 +157,8 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD for (int i = 0; i < restrictionsList.size(); i++) { Object restrictionAny = restrictionsList.get(i); + JsLog.warn("parseInputTableMetadata: Processing restriction " + i + " for column " + columnName, restrictionAny); + // Get the restriction type and look up the converter String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny); JsLog.warn("parseInputTableMetadata: Restriction " + i + " type: " + restrictionType); From 7166a51e59b5128c6d150e8b6dd3b24fb12d887b Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Mon, 20 Apr 2026 14:23:28 -0600 Subject: [PATCH 17/35] more fixes --- .../client/api/barrage/WebBarrageUtils.java | 4 +- .../barrage/util/ColumnRestrictionUtils.java | 42 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index b7c06b7c343..4a808abde83 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -155,12 +155,12 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD new InputTableMetadata.ColumnRestrictions(); for (int i = 0; i < restrictionsList.size(); i++) { - Object restrictionAny = restrictionsList.get(i); + Any restrictionAny = restrictionsList.get(i); JsLog.warn("parseInputTableMetadata: Processing restriction " + i + " for column " + columnName, restrictionAny); // Get the restriction type and look up the converter - String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny); + String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny.getTypeUrl()); JsLog.warn("parseInputTableMetadata: Restriction " + i + " type: " + restrictionType); if (restrictionType != null) { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java index 2366713d46d..f54eb13ddc6 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -28,17 +28,12 @@ private ColumnRestrictionUtils() { } /** - * Extract the restriction type short name from a google.protobuf.Any object. + * Extract the restriction type short name from a google.protobuf.Any object type url. * - * @param restrictionAny The google.protobuf.Any object containing the restriction + * @param typeUrl The type URL from the Any object (e.g., "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction") * @return The short type name (e.g., "IntegerRangeRestriction"), or null if not found */ - public static String getRestrictionType(Object restrictionAny) { - String typeUrl = getTypeUrl(restrictionAny); - if (typeUrl == null) { - return null; - } - + public static String getRestrictionType(String typeUrl) { // Extract the restriction type from the type URL // Format: "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" // or "docs.deephaven.io/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction" @@ -46,25 +41,25 @@ public static String getRestrictionType(Object restrictionAny) { return typeName.substring(typeName.lastIndexOf('.') + 1); } - /** - * Extract the type URL from a google.protobuf.Any object. - */ - private static native String getTypeUrl(Object anyObj) /*-{ - if (!anyObj) return null; - if (typeof anyObj.getTypeUrl === 'function') { - return anyObj.getTypeUrl(); - } - if (anyObj.type_url) { - return anyObj.type_url; - } - return null; - }-*/; - /** * Extract the value bytes from a google.protobuf.Any object. */ private static native Uint8Array getValue(Object anyObj) /*-{ if (!anyObj) return null; + + // Try Java protobuf structure (value_.bytes_0) + if (anyObj.value_ && anyObj.value_.bytes_0) { + // Convert byte array to Uint8Array + var bytes = anyObj.value_.bytes_0; + if (Array.isArray(bytes)) { + return new Uint8Array(bytes); + } + if (bytes instanceof Uint8Array) { + return bytes; + } + } + + // Try method accessor if (typeof anyObj.getValue_asU8 === 'function') { return anyObj.getValue_asU8(); } @@ -72,9 +67,12 @@ private static native Uint8Array getValue(Object anyObj) /*-{ var value = anyObj.getValue(); if (value) return value; } + + // Try direct value field if (anyObj.value && anyObj.value instanceof Uint8Array) { return anyObj.value; } + return null; }-*/; From a8f75ec0bc00164c9a3eecda93a509b572614549 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Mon, 20 Apr 2026 14:33:21 -0600 Subject: [PATCH 18/35] working fix --- .../util/ColumnRestrictionConverter.java | 2 +- .../barrage/util/ColumnRestrictionUtils.java | 80 ++----------------- 2 files changed, 8 insertions(+), 74 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java index cab0c71677e..03b61e076ce 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java @@ -3,8 +3,8 @@ // package io.deephaven.web.client.api.barrage.util; +import com.google.protobuf.Any; import io.deephaven.web.client.api.ColumnRestriction; -import jsinterop.base.Any; /** * Functional interface for converting parsed restriction data into a ColumnRestriction object. diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java index f54eb13ddc6..3d28d05c62c 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -3,6 +3,7 @@ // package io.deephaven.web.client.api.barrage.util; +import com.google.protobuf.Any; import elemental2.core.JsArray; import elemental2.core.Uint8Array; import io.deephaven.proto.backplane.grpc.NonEmptyRestriction; @@ -12,9 +13,7 @@ import io.deephaven.proto.backplane.grpc.StringListRestriction; import io.deephaven.web.client.api.ColumnRestriction; import io.deephaven.web.client.fu.JsLog; -import jsinterop.base.Any; import jsinterop.base.Js; -import org.gwtproject.nio.TypedArrayHelper; import java.nio.ByteBuffer; @@ -41,53 +40,12 @@ public static String getRestrictionType(String typeUrl) { return typeName.substring(typeName.lastIndexOf('.') + 1); } - /** - * Extract the value bytes from a google.protobuf.Any object. - */ - private static native Uint8Array getValue(Object anyObj) /*-{ - if (!anyObj) return null; - - // Try Java protobuf structure (value_.bytes_0) - if (anyObj.value_ && anyObj.value_.bytes_0) { - // Convert byte array to Uint8Array - var bytes = anyObj.value_.bytes_0; - if (Array.isArray(bytes)) { - return new Uint8Array(bytes); - } - if (bytes instanceof Uint8Array) { - return bytes; - } - } - - // Try method accessor - if (typeof anyObj.getValue_asU8 === 'function') { - return anyObj.getValue_asU8(); - } - if (typeof anyObj.getValue === 'function') { - var value = anyObj.getValue(); - if (value) return value; - } - - // Try direct value field - if (anyObj.value && anyObj.value instanceof Uint8Array) { - return anyObj.value; - } - - return null; - }-*/; - /** * Convert IntegerRangeRestriction data into a ColumnRestriction object. */ public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAny) { try { - Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); - if (valueBytes == null) { - JsLog.warn("No value bytes found in IntegerRangeRestriction Any object"); - return null; - } - - ByteBuffer buffer = TypedArrayHelper.wrap(valueBytes); + ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); IntegerRangeRestriction restriction = IntegerRangeRestriction.parseFrom(buffer); double minValue = restriction.hasMinInclusive() ? restriction.getMinInclusive() : Double.NaN; double maxValue = restriction.hasMaxInclusive() ? restriction.getMaxInclusive() : Double.NaN; @@ -104,13 +62,7 @@ public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAn */ public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny) { try { - Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); - if (valueBytes == null) { - JsLog.warn("No value bytes found in DoubleRangeRestriction Any object"); - return null; - } - - ByteBuffer buffer = TypedArrayHelper.wrap(valueBytes); + ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); DoubleRangeRestriction restriction = DoubleRangeRestriction.parseFrom(buffer); double minValue = restriction.hasMinInclusive() ? restriction.getMinInclusive() : Double.NaN; double maxValue = restriction.hasMaxInclusive() ? restriction.getMaxInclusive() : Double.NaN; @@ -127,13 +79,7 @@ public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny */ public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) { try { - Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); - if (valueBytes == null) { - JsLog.warn("No value bytes found in NotNullRestriction Any object"); - return null; - } - - ByteBuffer buffer = TypedArrayHelper.wrap(valueBytes); + ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); NotNullRestriction.parseFrom(buffer); // Just to validate return new ColumnRestriction("NotNullRestriction"); } catch (Exception e) { @@ -147,13 +93,7 @@ public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) { */ public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) { try { - Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); - if (valueBytes == null) { - JsLog.warn("No value bytes found in NonEmptyRestriction Any object"); - return null; - } - - ByteBuffer buffer = TypedArrayHelper.wrap(valueBytes); + ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); NonEmptyRestriction.parseFrom(buffer); // Just to validate return new ColumnRestriction("NonEmptyRestriction"); } catch (Exception e) { @@ -167,17 +107,11 @@ public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) { */ public static ColumnRestriction convertStringListRestriction(Any restrictionAny) { try { - Uint8Array valueBytes = getValue(Js.cast(restrictionAny)); - if (valueBytes == null) { - JsLog.warn("No value bytes found in StringListRestriction Any object"); - return null; - } - - ByteBuffer buffer = TypedArrayHelper.wrap(valueBytes); + ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); StringListRestriction restriction = StringListRestriction.parseFrom(buffer); // Convert ProtocolStringList to JsArray - JsArray allowedValuesAsAny = new JsArray<>(); + JsArray allowedValuesAsAny = new JsArray<>(); for (String value : restriction.getAllowedValuesList()) { allowedValuesAsAny.push(Js.cast(value)); } From 5d70e421663d5e70e1d8435eb8034957205178b8 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 21 Apr 2026 08:50:27 -0600 Subject: [PATCH 19/35] fix decode --- .../client/api/barrage/WebBarrageUtils.java | 37 +++---------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 4a808abde83..3cfc8848c04 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -23,10 +23,10 @@ import org.apache.arrow.flatbuf.MessageHeader; import org.apache.arrow.flatbuf.Schema; import com.google.protobuf.Any; -import org.gwtproject.nio.TypedArrayHelper; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -114,14 +114,12 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD InputTableMetadata metadata = new InputTableMetadata(); try { - JsLog.warn("parseInputTableMetadata: Decoding base64..."); - // Decode base64 to Uint8Array - Uint8Array bytes = decodeBase64(tableMetadataBase64); - JsLog.warn("parseInputTableMetadata: Decoded " + bytes.length + " bytes"); - JsLog.warn("parseInputTableMetadata: Deserializing DeephavenTableMetadata..."); - // Convert Uint8Array to ByteBuffer and deserialize using Java protobuf parseFrom - ByteBuffer buffer = TypedArrayHelper.wrap(bytes); + // Decode the base64 string to bytes and parse the DeephavenTableMetadata + final byte[] bytes = DomGlobal.atob(tableMetadataBase64).getBytes(StandardCharsets.ISO_8859_1); + ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length); + buffer.put(bytes); + buffer.flip(); DeephavenTableMetadata tableMetadata = DeephavenTableMetadata.parseFrom(buffer); JsLog.warn("parseInputTableMetadata: Successfully deserialized DeephavenTableMetadata", tableMetadata); @@ -137,8 +135,6 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD // Get the column info map Map columnInfoMap = protoInputTableMetadata.getColumnInfoMap(); -// JsLog.warn("parseInputTableMetadata: Retrieved column info map " + columnInfoMap); - JsLog.warn("parseInputTableMetadata: Processing " + cols.length + " columns"); // Extract column restrictions from the column info map for (ColumnDefinition col : cols) { @@ -194,27 +190,6 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD return metadata; } - // Helper method to extract InputTableColumnInfo from the map - private static native InputTableColumnInfo getColumnInfoFromMap(Object map, String key) /*-{ - if (!map || !map.get) return null; - return map.get(key); - }-*/; - - // Decode base64 string to Uint8Array using elemental2 - private static Uint8Array decodeBase64(String base64) { - // Use DomGlobal.atob() to decode base64 to binary string - String binaryString = DomGlobal.atob(base64); - - // Convert binary string to Uint8Array - Uint8Array bytes = new Uint8Array(binaryString.length()); - for (int i = 0; i < binaryString.length(); i++) { - bytes.setAt(i, (double) (binaryString.charAt(i) & 0xff)); - } - return bytes; - } - - - private static ColumnDefinition[] readColumnDefinitions(Schema schema) { ColumnDefinition[] cols = new ColumnDefinition[(int) schema.fieldsLength()]; for (int i = 0; i < schema.fieldsLength(); i++) { From 1aebf502806fb5b9c2b6488e2615d60a38f98036 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 21 Apr 2026 09:21:23 -0600 Subject: [PATCH 20/35] fix class cast exception --- .../io/deephaven/web/client/api/ColumnRestriction.java | 9 ++++----- .../client/api/barrage/util/ColumnRestrictionUtils.java | 5 ++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java b/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java index 83bb2840ddd..f4b814f1598 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java @@ -7,7 +7,6 @@ import elemental2.core.JsArray; import jsinterop.annotations.JsNullable; import jsinterop.annotations.JsProperty; -import jsinterop.base.Any; /** * Represents a restriction on an input table column. Restrictions define constraints that the server enforces on @@ -25,7 +24,7 @@ public class ColumnRestriction { private final String type; private final double minValue; private final double maxValue; - private final JsArray allowedValues; + private final JsArray allowedValues; /** * Creates a range restriction (IntegerRangeRestriction or DoubleRangeRestriction). @@ -44,10 +43,10 @@ public ColumnRestriction(String type, double minValue, double maxValue) { /** * Creates a string list restriction (StringListRestriction). * - * @param type The type of restriction (should be "StringListRestriction") + * @param type The type of restriction (e.g. "StringListRestriction") * @param allowedValues The array of allowed values */ - public ColumnRestriction(String type, JsArray allowedValues) { + public ColumnRestriction(String type, JsArray allowedValues) { this.type = type; this.minValue = Double.NaN; this.maxValue = Double.NaN; @@ -108,7 +107,7 @@ public JsArray getRange() { */ @JsProperty @JsNullable - public JsArray getValues() { + public JsArray getValues() { return allowedValues; } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java index 3d28d05c62c..beea609d3b0 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -110,10 +110,9 @@ public static ColumnRestriction convertStringListRestriction(Any restrictionAny) ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); StringListRestriction restriction = StringListRestriction.parseFrom(buffer); - // Convert ProtocolStringList to JsArray - JsArray allowedValuesAsAny = new JsArray<>(); + JsArray allowedValuesAsAny = new JsArray<>(); for (String value : restriction.getAllowedValuesList()) { - allowedValuesAsAny.push(Js.cast(value)); + allowedValuesAsAny.push(value); } return new ColumnRestriction("StringListRestriction", allowedValuesAsAny); From 3f79edea3d6ce9f2ea7a42879a63e58069e53825 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 21 Apr 2026 10:07:10 -0600 Subject: [PATCH 21/35] code cleanup --- .../client/api/barrage/WebBarrageUtils.java | 73 ++++++++----------- .../barrage/util/ColumnRestrictionUtils.java | 12 ++- 2 files changed, 35 insertions(+), 50 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 3cfc8848c04..f7d7c160274 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -10,6 +10,7 @@ import io.deephaven.barrage.flatbuf.BarrageMessageWrapper; import io.deephaven.proto.backplane.grpc.DeephavenTableMetadata; import io.deephaven.proto.backplane.grpc.InputTableColumnInfo; +import io.deephaven.web.client.api.ColumnRestriction; import io.deephaven.web.client.api.barrage.def.ColumnDefinition; import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; import io.deephaven.web.client.api.barrage.def.InputTableMetadata; @@ -114,77 +115,63 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD InputTableMetadata metadata = new InputTableMetadata(); try { - JsLog.warn("parseInputTableMetadata: Deserializing DeephavenTableMetadata..."); // Decode the base64 string to bytes and parse the DeephavenTableMetadata final byte[] bytes = DomGlobal.atob(tableMetadataBase64).getBytes(StandardCharsets.ISO_8859_1); ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length); buffer.put(bytes); buffer.flip(); DeephavenTableMetadata tableMetadata = DeephavenTableMetadata.parseFrom(buffer); - JsLog.warn("parseInputTableMetadata: Successfully deserialized DeephavenTableMetadata", tableMetadata); if (!tableMetadata.hasInputTableMetadata()) { - JsLog.warn("parseInputTableMetadata: No input table metadata present"); + // TODO what to do in this case? return metadata; } io.deephaven.proto.backplane.grpc.InputTableMetadata protoInputTableMetadata = tableMetadata.getInputTableMetadata(); - JsLog.warn("parseInputTableMetadata: Got InputTableMetadata", protoInputTableMetadata); // Get the column info map Map columnInfoMap = protoInputTableMetadata.getColumnInfoMap(); - JsLog.warn("parseInputTableMetadata: Processing " + cols.length + " columns"); // Extract column restrictions from the column info map for (ColumnDefinition col : cols) { String columnName = col.getName(); InputTableColumnInfo columnInfo = columnInfoMap.get(columnName); - if (columnInfo != null) { - JsLog.warn("parseInputTableMetadata: Found column info for: " + columnName); - List restrictionsList = columnInfo.getRestrictionsList(); - - if (restrictionsList != null && !restrictionsList.isEmpty()) { - JsLog.warn("parseInputTableMetadata: Column " + columnName + " has " + restrictionsList.size() + " restrictions"); - InputTableMetadata.ColumnRestrictions colRestrictions = - new InputTableMetadata.ColumnRestrictions(); - - for (int i = 0; i < restrictionsList.size(); i++) { - Any restrictionAny = restrictionsList.get(i); - - JsLog.warn("parseInputTableMetadata: Processing restriction " + i + " for column " + columnName, restrictionAny); - - // Get the restriction type and look up the converter - String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny.getTypeUrl()); - JsLog.warn("parseInputTableMetadata: Restriction " + i + " type: " + restrictionType); - - if (restrictionType != null) { - ColumnRestrictionConverter converter = restrictionConverters.get(restrictionType); - - if (converter != null) { - JsLog.warn("parseInputTableMetadata: Converting restriction..."); - io.deephaven.web.client.api.ColumnRestriction restriction = - converter.convert(jsinterop.base.Js.cast(restrictionAny)); - if (restriction != null) { - colRestrictions.addRestriction(restriction); - JsLog.warn("parseInputTableMetadata: Successfully added restriction"); - } else { - JsLog.warn("parseInputTableMetadata: Converter returned null for " + restrictionType); - } - } else { - JsLog.warn("No converter registered for restriction type: " + restrictionType); - } + if (columnInfo == null) { + JsLog.warn("parseInputTableMetadata: No column info found for column " + columnName); + continue; + } + + List restrictionsList = columnInfo.getRestrictionsList(); + + if (!restrictionsList.isEmpty()) { + InputTableMetadata.ColumnRestrictions colRestrictions = + new InputTableMetadata.ColumnRestrictions(); + + for (Any restrictionAny : restrictionsList) { + // Get the restriction type and look up the converter + String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny.getTypeUrl()); + + ColumnRestrictionConverter converter = restrictionConverters.get(restrictionType); + + if (converter != null) { + // TODO make this throw + ColumnRestriction restriction = converter.convert(restrictionAny); + if (restriction != null) { + colRestrictions.addRestriction(restriction); } + } else { + JsLog.error("No converter registered for restriction type: " + restrictionType); } - - metadata.addColumnRestrictions(columnName, colRestrictions); } + + metadata.addColumnRestrictions(columnName, colRestrictions); } + } - JsLog.warn("parseInputTableMetadata: Successfully parsed all restrictions"); } catch (Exception e) { - JsLog.warn("Failed to parse input table metadata:", e); + JsLog.error("Failed to parse input table metadata:", e); } return metadata; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java index beea609d3b0..d50232f813b 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -5,7 +5,6 @@ import com.google.protobuf.Any; import elemental2.core.JsArray; -import elemental2.core.Uint8Array; import io.deephaven.proto.backplane.grpc.NonEmptyRestriction; import io.deephaven.proto.backplane.grpc.NotNullRestriction; import io.deephaven.proto.backplane.grpc.DoubleRangeRestriction; @@ -13,7 +12,6 @@ import io.deephaven.proto.backplane.grpc.StringListRestriction; import io.deephaven.web.client.api.ColumnRestriction; import io.deephaven.web.client.fu.JsLog; -import jsinterop.base.Js; import java.nio.ByteBuffer; @@ -52,7 +50,7 @@ public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAn return new ColumnRestriction("IntegerRangeRestriction", minValue, maxValue); } catch (Exception e) { - JsLog.warn("Failed to convert IntegerRangeRestriction:", e); + JsLog.error("Failed to convert IntegerRangeRestriction:", e); return null; } } @@ -69,7 +67,7 @@ public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny return new ColumnRestriction("DoubleRangeRestriction", minValue, maxValue); } catch (Exception e) { - JsLog.warn("Failed to convert DoubleRangeRestriction:", e); + JsLog.error("Failed to convert DoubleRangeRestriction:", e); return null; } } @@ -83,7 +81,7 @@ public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) { NotNullRestriction.parseFrom(buffer); // Just to validate return new ColumnRestriction("NotNullRestriction"); } catch (Exception e) { - JsLog.warn("Failed to convert NotNullRestriction:", e); + JsLog.error("Failed to convert NotNullRestriction:", e); return null; } } @@ -97,7 +95,7 @@ public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) { NonEmptyRestriction.parseFrom(buffer); // Just to validate return new ColumnRestriction("NonEmptyRestriction"); } catch (Exception e) { - JsLog.warn("Failed to convert NonEmptyRestriction:", e); + JsLog.error("Failed to convert NonEmptyRestriction:", e); return null; } } @@ -117,7 +115,7 @@ public static ColumnRestriction convertStringListRestriction(Any restrictionAny) return new ColumnRestriction("StringListRestriction", allowedValuesAsAny); } catch (Exception e) { - JsLog.warn("Failed to convert StringListRestriction:", e); + JsLog.error("Failed to convert StringListRestriction:", e); return null; } } From 5a994f82638afc786feec407a4d366468ddc30ca Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 21 Apr 2026 10:24:23 -0600 Subject: [PATCH 22/35] converters throw exception --- .../client/api/barrage/WebBarrageUtils.java | 14 ++-------- .../util/ColumnRestrictionConverter.java | 3 ++- .../ColumnRestrictionConverterException.java | 14 ++++++++++ .../barrage/util/ColumnRestrictionUtils.java | 27 +++++++------------ 4 files changed, 28 insertions(+), 30 deletions(-) create mode 100644 web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverterException.java diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index f7d7c160274..5f06c96c55c 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -10,7 +10,6 @@ import io.deephaven.barrage.flatbuf.BarrageMessageWrapper; import io.deephaven.proto.backplane.grpc.DeephavenTableMetadata; import io.deephaven.proto.backplane.grpc.InputTableColumnInfo; -import io.deephaven.web.client.api.ColumnRestriction; import io.deephaven.web.client.api.barrage.def.ColumnDefinition; import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; import io.deephaven.web.client.api.barrage.def.InputTableMetadata; @@ -127,11 +126,8 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD return metadata; } - io.deephaven.proto.backplane.grpc.InputTableMetadata protoInputTableMetadata = - tableMetadata.getInputTableMetadata(); - // Get the column info map - Map columnInfoMap = protoInputTableMetadata.getColumnInfoMap(); + Map columnInfoMap = tableMetadata.getInputTableMetadata().getColumnInfoMap(); // Extract column restrictions from the column info map for (ColumnDefinition col : cols) { @@ -152,15 +148,9 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD for (Any restrictionAny : restrictionsList) { // Get the restriction type and look up the converter String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny.getTypeUrl()); - ColumnRestrictionConverter converter = restrictionConverters.get(restrictionType); - if (converter != null) { - // TODO make this throw - ColumnRestriction restriction = converter.convert(restrictionAny); - if (restriction != null) { - colRestrictions.addRestriction(restriction); - } + colRestrictions.addRestriction(converter.convert(restrictionAny)); } else { JsLog.error("No converter registered for restriction type: " + restrictionType); } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java index 03b61e076ce..e130eb56191 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverter.java @@ -16,7 +16,8 @@ public interface ColumnRestrictionConverter { * * @param restrictionData The parsed restriction data from protobuf * @return A ColumnRestriction object, or null if conversion fails + * @throws ColumnRestrictionConverterException if the restriction data cannot be converted to a ColumnRestriction */ - ColumnRestriction convert(Any restrictionData); + ColumnRestriction convert(Any restrictionData) throws ColumnRestrictionConverterException; } diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverterException.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverterException.java new file mode 100644 index 00000000000..21c6d82ca2d --- /dev/null +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverterException.java @@ -0,0 +1,14 @@ +package io.deephaven.web.client.api.barrage.util; + +/** + * Exception thrown when column restriction protobuf data cannot be converted to a ColumnRestriction. + */ +public class ColumnRestrictionConverterException extends Exception { + public ColumnRestrictionConverterException(String message) { + super(message); + } + + public ColumnRestrictionConverterException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java index d50232f813b..9600f9b54d4 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -41,69 +41,63 @@ public static String getRestrictionType(String typeUrl) { /** * Convert IntegerRangeRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAny) { + public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); IntegerRangeRestriction restriction = IntegerRangeRestriction.parseFrom(buffer); double minValue = restriction.hasMinInclusive() ? restriction.getMinInclusive() : Double.NaN; double maxValue = restriction.hasMaxInclusive() ? restriction.getMaxInclusive() : Double.NaN; - return new ColumnRestriction("IntegerRangeRestriction", minValue, maxValue); } catch (Exception e) { - JsLog.error("Failed to convert IntegerRangeRestriction:", e); - return null; + throw new ColumnRestrictionConverterException("Failed to convert IntegerRangeRestriction", e); } } /** * Convert DoubleRangeRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny) { + public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); DoubleRangeRestriction restriction = DoubleRangeRestriction.parseFrom(buffer); double minValue = restriction.hasMinInclusive() ? restriction.getMinInclusive() : Double.NaN; double maxValue = restriction.hasMaxInclusive() ? restriction.getMaxInclusive() : Double.NaN; - return new ColumnRestriction("DoubleRangeRestriction", minValue, maxValue); } catch (Exception e) { - JsLog.error("Failed to convert DoubleRangeRestriction:", e); - return null; + throw new ColumnRestrictionConverterException("Failed to convert DoubleRangeRestriction", e); } } /** * Convert NotNullRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) { + public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); NotNullRestriction.parseFrom(buffer); // Just to validate return new ColumnRestriction("NotNullRestriction"); } catch (Exception e) { - JsLog.error("Failed to convert NotNullRestriction:", e); - return null; + throw new ColumnRestrictionConverterException("Failed to convert NotNullRestriction", e); } } /** * Convert NonEmptyRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) { + public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); NonEmptyRestriction.parseFrom(buffer); // Just to validate return new ColumnRestriction("NonEmptyRestriction"); } catch (Exception e) { - JsLog.error("Failed to convert NonEmptyRestriction:", e); - return null; + throw new ColumnRestrictionConverterException("Failed to convert NonEmptyRestriction", e); } } /** * Convert StringListRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertStringListRestriction(Any restrictionAny) { + public static ColumnRestriction convertStringListRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); StringListRestriction restriction = StringListRestriction.parseFrom(buffer); @@ -115,8 +109,7 @@ public static ColumnRestriction convertStringListRestriction(Any restrictionAny) return new ColumnRestriction("StringListRestriction", allowedValuesAsAny); } catch (Exception e) { - JsLog.error("Failed to convert StringListRestriction:", e); - return null; + throw new ColumnRestrictionConverterException("Failed to convert StringListRestriction", e); } } } From 24a801c2a20d54fab27de628ee92fe08d86d9972 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 21 Apr 2026 11:49:19 -0600 Subject: [PATCH 23/35] more cleanup --- .../client/api/barrage/WebBarrageUtils.java | 57 ++++++++++--------- .../api/barrage/def/InputTableMetadata.java | 1 - 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 5f06c96c55c..125280f1e67 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -101,67 +101,70 @@ public static InitialTableDefinition readTableDefinition(Schema schema) { .setInputTableMetadata(inputTableMetadata); } + /** + * Parses input table metadata from the schema's custom metadata and column definitions. + * + * @param schema the schema containing the custom metadata with the base64-encoded table metadata + * @param cols the column definitions to match against the column info in the table metadata + * @return an InputTableMetadata object containing the column restrictions, or null if no valid metadata is found + */ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnDefinition[] cols) { // Extract the tableMetadata from schema custom metadata - Map schemaMetadata = + final Map schemaMetadata = keyValuePairs("deephaven:", schema.customMetadataLength(), schema::customMetadata); - String tableMetadataBase64 = schemaMetadata.get("tableMetadata"); + final String tableMetadataBase64 = schemaMetadata.get("tableMetadata"); if (tableMetadataBase64 == null || tableMetadataBase64.isEmpty()) { return null; } - InputTableMetadata metadata = new InputTableMetadata(); - + final InputTableMetadata metadata = new InputTableMetadata(); try { // Decode the base64 string to bytes and parse the DeephavenTableMetadata final byte[] bytes = DomGlobal.atob(tableMetadataBase64).getBytes(StandardCharsets.ISO_8859_1); - ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length); + final ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length); buffer.put(bytes); buffer.flip(); - DeephavenTableMetadata tableMetadata = DeephavenTableMetadata.parseFrom(buffer); + final DeephavenTableMetadata tableMetadata = DeephavenTableMetadata.parseFrom(buffer); if (!tableMetadata.hasInputTableMetadata()) { - // TODO what to do in this case? - return metadata; + return null; } // Get the column info map - Map columnInfoMap = tableMetadata.getInputTableMetadata().getColumnInfoMap(); + final Map columnInfoMap = tableMetadata.getInputTableMetadata().getColumnInfoMap(); // Extract column restrictions from the column info map for (ColumnDefinition col : cols) { - String columnName = col.getName(); - InputTableColumnInfo columnInfo = columnInfoMap.get(columnName); + final String columnName = col.getName(); + final InputTableColumnInfo columnInfo = columnInfoMap.get(columnName); if (columnInfo == null) { JsLog.warn("parseInputTableMetadata: No column info found for column " + columnName); continue; } - List restrictionsList = columnInfo.getRestrictionsList(); - - if (!restrictionsList.isEmpty()) { - InputTableMetadata.ColumnRestrictions colRestrictions = - new InputTableMetadata.ColumnRestrictions(); - - for (Any restrictionAny : restrictionsList) { - // Get the restriction type and look up the converter - String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny.getTypeUrl()); - ColumnRestrictionConverter converter = restrictionConverters.get(restrictionType); - if (converter != null) { - colRestrictions.addRestriction(converter.convert(restrictionAny)); - } else { - JsLog.error("No converter registered for restriction type: " + restrictionType); - } + final List restrictionsList = columnInfo.getRestrictionsList(); + final InputTableMetadata.ColumnRestrictions colRestrictions = new InputTableMetadata.ColumnRestrictions(); + + for (Any restrictionAny : restrictionsList) { + // Get the restriction type and look up the converter + String restrictionType = ColumnRestrictionUtils.getRestrictionType(restrictionAny.getTypeUrl()); + ColumnRestrictionConverter converter = restrictionConverters.get(restrictionType); + if (converter != null) { + colRestrictions.addRestriction(converter.convert(restrictionAny)); + } else { + JsLog.error("No converter registered for restriction type: " + restrictionType); } + } + if (colRestrictions.getRestrictions().length > 0) { metadata.addColumnRestrictions(columnName, colRestrictions); } - } } catch (Exception e) { JsLog.error("Failed to parse input table metadata:", e); + return null; } return metadata; diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java index 6fd4b62a8cd..1f4bdd5802d 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/def/InputTableMetadata.java @@ -51,7 +51,6 @@ public void addRestriction(ColumnRestriction restriction) { } } - @JsIgnore public JsArray getRestrictions() { return restrictions; From fb5411648babfe6c220507d624c1e38132f8ac4c Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 21 Apr 2026 11:53:03 -0600 Subject: [PATCH 24/35] spotless --- .../engine/util/input/InputTableUpdater.java | 10 +++++----- .../AbstractBaseValidatingInputTable.java | 6 +++--- .../DoubleRangeValidatingInputTable.java | 3 ++- .../NonEmptyValidatingInputTable.java | 6 ++++-- .../NotNullValidatingInputTable.java | 3 ++- .../StringListValidatingInputTable.java | 12 +++++++---- .../io/deephaven/web/client/api/Column.java | 8 ++++---- .../web/client/api/ColumnRestriction.java | 4 ++-- .../client/api/barrage/WebBarrageUtils.java | 20 ++++++++++++------- .../ColumnRestrictionConverterException.java | 3 +++ .../barrage/util/ColumnRestrictionUtils.java | 18 +++++++++++------ 11 files changed, 58 insertions(+), 35 deletions(-) diff --git a/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java b/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java index 450229e3d78..62d052a4509 100644 --- a/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java +++ b/engine/table/src/main/java/io/deephaven/engine/util/input/InputTableUpdater.java @@ -61,13 +61,13 @@ default List getValueNames() { * These restrictions are used by the client for properly displaying and validating the edit field. * *

- * The restrictions are packed as {@code google.protobuf.Any} messages, which allows for different restriction - * types (e.g., {@code IntegerRangeRestriction}, {@code DoubleRangeRestriction}, {@code StringListRestriction}, - * etc.) to be sent to the client. The client is responsible for unpacking and interpreting these restrictions. + * The restrictions are packed as {@code google.protobuf.Any} messages, which allows for different restriction types + * (e.g., {@code IntegerRangeRestriction}, {@code DoubleRangeRestriction}, {@code StringListRestriction}, etc.) to + * be sent to the client. The client is responsible for unpacking and interpreting these restrictions. * * @param columnName the column name to query - * @return a list of protobuf Any messages representing the restrictions for this column, or null if no - * client-side restrictions are supplied for this column + * @return a list of protobuf Any messages representing the restrictions for this column, or null if no client-side + * restrictions are supplied for this column */ @Nullable default List getColumnRestrictions(final String columnName) { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java index c7635e23a05..41a7c34eba7 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java @@ -18,9 +18,9 @@ * An abstract base class for {@link InputTableUpdater} implementations that wrap an existing input table. * *

- * This class provides a default implementation for most methods by delegating to the wrapped input table. - * Subclasses should override {@link #getColumnRestrictions(String)} and {@link #validateAddOrModify(Table)} - * to provide custom validation logic. + * This class provides a default implementation for most methods by delegating to the wrapped input table. Subclasses + * should override {@link #getColumnRestrictions(String)} and {@link #validateAddOrModify(Table)} to provide custom + * validation logic. *

* *

diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java index 6d4160b9bc6..4514d881e15 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java @@ -52,7 +52,8 @@ public static Table make(Table input, final String column, final double min, final double max) { final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); - final DoubleRangeValidatingInputTable validatedUpdater = new DoubleRangeValidatingInputTable(updater, column, min, max); + final DoubleRangeValidatingInputTable validatedUpdater = + new DoubleRangeValidatingInputTable(updater, column, min, max); return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); } diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java index 8052401ac52..58983e1490a 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java @@ -56,7 +56,8 @@ private NonEmptyValidatingInputTable(InputTableUpdater wrapped, final String col this.column = column; final Class dataType = getTableDefinition().getColumn(column).getDataType(); if (dataType != String.class) { - throw new IllegalArgumentException("Non-empty validation only applies to String columns, but " + column + " is " + dataType); + throw new IllegalArgumentException( + "Non-empty validation only applies to String columns, but " + column + " is " + dataType); } } @@ -84,7 +85,8 @@ public void validateAddOrModify(Table tableToApply) { final MutableInt position = new MutableInt(0); final ColumnSource columnSource = tableToApply.getColumnSource(column, String.class); - try (final RowSequence rowSequence = tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { + try (final RowSequence rowSequence = + tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { rowSequence.forAllRowKeys(rowKey -> { final String value = columnSource.get(rowKey); if (value != null && value.isEmpty()) { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java index f277c0b8469..8631ad4d6e2 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java @@ -80,7 +80,8 @@ public void validateAddOrModify(Table tableToApply) { final MutableInt position = new MutableInt(0); final ColumnSource columnSource = tableToApply.getColumnSource(column); - try (final RowSequence rowSequence = tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { + try (final RowSequence rowSequence = + tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { rowSequence.forAllRowKeys(rowKey -> { final Object value = columnSource.get(rowKey); if (value == null) { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java index 2a6e0d6ec44..aa4501069f7 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java @@ -51,19 +51,22 @@ public class StringListValidatingInputTable extends AbstractBaseValidatingInputT */ public static Table make(Table input, final String column, final String... allowedValues) { final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); - final StringListValidatingInputTable validatedUpdater = new StringListValidatingInputTable(updater, column, allowedValues); + final StringListValidatingInputTable validatedUpdater = + new StringListValidatingInputTable(updater, column, allowedValues); return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); } - private StringListValidatingInputTable(InputTableUpdater wrapped, final String column, final String... allowedValues) { + private StringListValidatingInputTable(InputTableUpdater wrapped, final String column, + final String... allowedValues) { super(wrapped); this.column = column; this.allowedValuesList = List.of(allowedValues); this.allowedValues = Set.of(allowedValues); final Class dataType = getTableDefinition().getColumn(column).getDataType(); if (dataType != String.class) { - throw new IllegalArgumentException("String list validation only applies to String columns, but " + column + " is " + dataType); + throw new IllegalArgumentException( + "String list validation only applies to String columns, but " + column + " is " + dataType); } } @@ -92,7 +95,8 @@ public void validateAddOrModify(Table tableToApply) { final MutableInt position = new MutableInt(0); final ColumnSource columnSource = tableToApply.getColumnSource(column, String.class); - try (final RowSequence rowSequence = tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { + try (final RowSequence rowSequence = + tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { rowSequence.forAllRowKeys(rowKey -> { final String value = columnSource.get(rowKey); if (!allowedValues.contains(value)) { diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java b/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java index 3958c857e09..88a4702754a 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/Column.java @@ -108,7 +108,7 @@ public Column(int jsIndex, int index, Integer formatColumnIndex, Integer styleCo boolean isPartitionColumn, Integer formatStringColumnIndex, String description, boolean inputTableKeyColumn, boolean inputTableValueColumn, boolean isSortable) { this(jsIndex, index, formatColumnIndex, styleColumnIndex, type, name, isPartitionColumn, - formatStringColumnIndex, description, inputTableKeyColumn, inputTableValueColumn, isSortable, null); + formatStringColumnIndex, description, inputTableKeyColumn, inputTableValueColumn, isSortable, null); } /** @@ -238,9 +238,9 @@ public boolean getIsSortable() { } /** - * Returns the column restrictions for input table columns, or null if this is not an input table column - * or if no restrictions are defined. The restrictions are implementation-specific constraints that the - * server enforces on column values. + * Returns the column restrictions for input table columns, or null if this is not an input table column or if no + * restrictions are defined. The restrictions are implementation-specific constraints that the server enforces on + * column values. * * @return Array of column restrictions, or null if none are defined */ diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java b/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java index f4b814f1598..bbd8f0850d9 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/ColumnRestriction.java @@ -9,8 +9,8 @@ import jsinterop.annotations.JsProperty; /** - * Represents a restriction on an input table column. Restrictions define constraints that the server enforces on - * column values. There are several types of restrictions: + * Represents a restriction on an input table column. Restrictions define constraints that the server enforces on column + * values. There are several types of restrictions: *

    *
  • IntegerRangeRestriction - validates integer values are within a range
  • *
  • DoubleRangeRestriction - validates double values are within a range
  • diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java index 125280f1e67..cb6ae7e4ff7 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/WebBarrageUtils.java @@ -44,11 +44,14 @@ public class WebBarrageUtils { static { // Register default converters - registerColumnRestrictionConverter("IntegerRangeRestriction", ColumnRestrictionUtils::convertIntegerRangeRestriction); - registerColumnRestrictionConverter("DoubleRangeRestriction", ColumnRestrictionUtils::convertDoubleRangeRestriction); + registerColumnRestrictionConverter("IntegerRangeRestriction", + ColumnRestrictionUtils::convertIntegerRangeRestriction); + registerColumnRestrictionConverter("DoubleRangeRestriction", + ColumnRestrictionUtils::convertDoubleRangeRestriction); registerColumnRestrictionConverter("NotNullRestriction", ColumnRestrictionUtils::convertNotNullRestriction); registerColumnRestrictionConverter("NonEmptyRestriction", ColumnRestrictionUtils::convertNonEmptyRestriction); - registerColumnRestrictionConverter("StringListRestriction", ColumnRestrictionUtils::convertStringListRestriction); + registerColumnRestrictionConverter("StringListRestriction", + ColumnRestrictionUtils::convertStringListRestriction); } /** @@ -57,7 +60,8 @@ public class WebBarrageUtils { * @param restrictionType The type name of the restriction (e.g., "IntegerRangeRestriction") * @param converter The converter function to convert the restriction data */ - public static void registerColumnRestrictionConverter(String restrictionType, ColumnRestrictionConverter converter) { + public static void registerColumnRestrictionConverter(String restrictionType, + ColumnRestrictionConverter converter) { restrictionConverters.put(restrictionType, converter); } @@ -132,7 +136,8 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD } // Get the column info map - final Map columnInfoMap = tableMetadata.getInputTableMetadata().getColumnInfoMap(); + final Map columnInfoMap = + tableMetadata.getInputTableMetadata().getColumnInfoMap(); // Extract column restrictions from the column info map for (ColumnDefinition col : cols) { @@ -145,7 +150,8 @@ private static InputTableMetadata parseInputTableMetadata(Schema schema, ColumnD } final List restrictionsList = columnInfo.getRestrictionsList(); - final InputTableMetadata.ColumnRestrictions colRestrictions = new InputTableMetadata.ColumnRestrictions(); + final InputTableMetadata.ColumnRestrictions colRestrictions = + new InputTableMetadata.ColumnRestrictions(); for (Any restrictionAny : restrictionsList) { // Get the restriction type and look up the converter @@ -210,7 +216,7 @@ public static Schema readSchemaMessage(ByteBuffer flightSchemaMessage) { } public static Map keyValuePairs(String filterPrefix, double count, - IntFunction accessor) { + IntFunction accessor) { Map map = new HashMap<>(); for (int i = 0; i < count; i++) { KeyValue pair = accessor.apply(i); diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverterException.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverterException.java index 21c6d82ca2d..e9e2192d557 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverterException.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionConverterException.java @@ -1,3 +1,6 @@ +// +// Copyright (c) 2016-2026 Deephaven Data Labs and Patent Pending +// package io.deephaven.web.client.api.barrage.util; /** diff --git a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java index 9600f9b54d4..8eb15d96962 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/api/barrage/util/ColumnRestrictionUtils.java @@ -27,7 +27,8 @@ private ColumnRestrictionUtils() { /** * Extract the restriction type short name from a google.protobuf.Any object type url. * - * @param typeUrl The type URL from the Any object (e.g., "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction") + * @param typeUrl The type URL from the Any object (e.g., + * "type.googleapis.com/io.deephaven.proto.backplane.grpc.IntegerRangeRestriction") * @return The short type name (e.g., "IntegerRangeRestriction"), or null if not found */ public static String getRestrictionType(String typeUrl) { @@ -41,7 +42,8 @@ public static String getRestrictionType(String typeUrl) { /** * Convert IntegerRangeRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { + public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAny) + throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); IntegerRangeRestriction restriction = IntegerRangeRestriction.parseFrom(buffer); @@ -56,7 +58,8 @@ public static ColumnRestriction convertIntegerRangeRestriction(Any restrictionAn /** * Convert DoubleRangeRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { + public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny) + throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); DoubleRangeRestriction restriction = DoubleRangeRestriction.parseFrom(buffer); @@ -71,7 +74,8 @@ public static ColumnRestriction convertDoubleRangeRestriction(Any restrictionAny /** * Convert NotNullRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { + public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) + throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); NotNullRestriction.parseFrom(buffer); // Just to validate @@ -84,7 +88,8 @@ public static ColumnRestriction convertNotNullRestriction(Any restrictionAny) th /** * Convert NonEmptyRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { + public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) + throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); NonEmptyRestriction.parseFrom(buffer); // Just to validate @@ -97,7 +102,8 @@ public static ColumnRestriction convertNonEmptyRestriction(Any restrictionAny) t /** * Convert StringListRestriction data into a ColumnRestriction object. */ - public static ColumnRestriction convertStringListRestriction(Any restrictionAny) throws ColumnRestrictionConverterException { + public static ColumnRestriction convertStringListRestriction(Any restrictionAny) + throws ColumnRestrictionConverterException { try { ByteBuffer buffer = restrictionAny.getValue().asReadOnlyByteBuffer(); StringListRestriction restriction = StringListRestriction.parseFrom(buffer); From 9cad8527cc04c9301f791a9cd0829916a04149c2 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Tue, 28 Apr 2026 10:13:10 -0600 Subject: [PATCH 25/35] use type instead of var --- .../java/io/deephaven/web/client/state/ClientTableState.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java b/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java index 831d0c139c3..35fbf256bd1 100644 --- a/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java +++ b/web/client-api/src/main/java/io/deephaven/web/client/state/ClientTableState.java @@ -13,6 +13,7 @@ import io.deephaven.web.client.api.barrage.WebBarrageUtils; import io.deephaven.web.client.api.barrage.def.ColumnDefinition; import io.deephaven.web.client.api.barrage.def.InitialTableDefinition; +import io.deephaven.web.client.api.barrage.def.InputTableMetadata; import io.deephaven.web.client.api.batch.TableConfig; import io.deephaven.web.client.api.event.HasEventHandling; import io.deephaven.web.client.api.filter.FilterCondition; @@ -448,7 +449,8 @@ private void setTableDef(InitialTableDefinition tableDef) { // Populate column restrictions from input table metadata if available if (tableDef.getInputTableMetadata() != null) { for (ColumnDefinition definition : columnDefinitions) { - var restrictions = tableDef.getInputTableMetadata().getColumnRestrictions(definition.getName()); + final InputTableMetadata.ColumnRestrictions restrictions = + tableDef.getInputTableMetadata().getColumnRestrictions(definition.getName()); if (restrictions != null) { definition.setColumnRestrictions(restrictions.getRestrictions()); } From 0c5b003fedc9a7a147a233781182e8237cd5fa34 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 07:24:55 -0600 Subject: [PATCH 26/35] use column iterator --- .../table/inputtables/NonEmptyValidatingInputTable.java | 7 +++---- .../table/inputtables/NotNullValidatingInputTable.java | 9 ++++----- .../inputtables/StringListValidatingInputTable.java | 7 +++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java index 58983e1490a..9fbbe6304b8 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java @@ -4,6 +4,7 @@ package io.deephaven.server.table.inputtables; import com.google.protobuf.Any; +import io.deephaven.engine.primitive.iterator.CloseableIterator; import io.deephaven.engine.rowset.RowSequence; import io.deephaven.engine.table.ColumnSource; import io.deephaven.engine.table.Table; @@ -85,10 +86,8 @@ public void validateAddOrModify(Table tableToApply) { final MutableInt position = new MutableInt(0); final ColumnSource columnSource = tableToApply.getColumnSource(column, String.class); - try (final RowSequence rowSequence = - tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { - rowSequence.forAllRowKeys(rowKey -> { - final String value = columnSource.get(rowKey); + try (final CloseableIterator it = tableToApply.columnIterator(column)) { + it.forEachRemaining(value -> { if (value != null && value.isEmpty()) { errors.add(new StructuredErrorImpl( "Value must not be empty", diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java index 8631ad4d6e2..df5f1162bb2 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java @@ -4,7 +4,7 @@ package io.deephaven.server.table.inputtables; import com.google.protobuf.Any; -import io.deephaven.engine.rowset.RowSequence; +import io.deephaven.engine.primitive.iterator.CloseableIterator; import io.deephaven.engine.table.ColumnSource; import io.deephaven.engine.table.Table; import io.deephaven.engine.util.input.InputTableUpdater; @@ -80,10 +80,8 @@ public void validateAddOrModify(Table tableToApply) { final MutableInt position = new MutableInt(0); final ColumnSource columnSource = tableToApply.getColumnSource(column); - try (final RowSequence rowSequence = - tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { - rowSequence.forAllRowKeys(rowKey -> { - final Object value = columnSource.get(rowKey); + try (final CloseableIterator it = tableToApply.columnIterator(column)) { + it.forEachRemaining(value -> { if (value == null) { errors.add(new StructuredErrorImpl( "Value must not be null", @@ -93,6 +91,7 @@ public void validateAddOrModify(Table tableToApply) { }); } + if (!errors.isEmpty()) { throw new InputTableValidationException(errors); } diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java index aa4501069f7..0525cf23dcd 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java @@ -4,6 +4,7 @@ package io.deephaven.server.table.inputtables; import com.google.protobuf.Any; +import io.deephaven.engine.primitive.iterator.CloseableIterator; import io.deephaven.engine.rowset.RowSequence; import io.deephaven.engine.table.ColumnSource; import io.deephaven.engine.table.Table; @@ -95,10 +96,8 @@ public void validateAddOrModify(Table tableToApply) { final MutableInt position = new MutableInt(0); final ColumnSource columnSource = tableToApply.getColumnSource(column, String.class); - try (final RowSequence rowSequence = - tableToApply.getRowSet().getRowSequenceByPosition(0, tableToApply.size())) { - rowSequence.forAllRowKeys(rowKey -> { - final String value = columnSource.get(rowKey); + try (final CloseableIterator it = tableToApply.columnIterator(column)) { + it.forEachRemaining(value -> { if (!allowedValues.contains(value)) { errors.add(new StructuredErrorImpl( "Value '" + value + "' is not in the allowed list: " + allowedValuesList, From a1d704f07ac37215a8970090ca2710282e4e007e Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 08:16:19 -0600 Subject: [PATCH 27/35] wrap updater method --- .../AbstractBaseValidatingInputTable.java | 16 ++++++++++++++++ .../DoubleRangeValidatingInputTable.java | 6 +----- .../NonEmptyValidatingInputTable.java | 8 +------- .../inputtables/NotNullValidatingInputTable.java | 7 +------ .../inputtables/RangeValidatingInputTable.java | 5 +---- .../StringListValidatingInputTable.java | 9 +-------- 6 files changed, 21 insertions(+), 30 deletions(-) diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java index 41a7c34eba7..86b070cdac9 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java @@ -13,6 +13,8 @@ import java.io.IOException; import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; /** * An abstract base class for {@link InputTableUpdater} implementations that wrap an existing input table. @@ -32,6 +34,20 @@ public abstract class AbstractBaseValidatingInputTable implements InputTableUpdater { protected final InputTableUpdater wrapped; + /** + * Wraps an existing input table updater with a new validating updater created by the provided {@code createUpdater} + * function. + * + * @param input the input table, must have an {@link InputTableUpdater} as its {@link Table#INPUT_TABLE_ATTRIBUTE} + * @param createUpdater a function that takes the existing input table's updater and returns a new validating + * updater + * @return a new input table that validates according to the provided {@code createUpdater} function + */ + protected static Table wrapUpdater(final Table input, final UnaryOperator createUpdater) { + final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); + return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, createUpdater.apply(updater))); + } + /** * Construct a new validating input table that wraps the given input table. * diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java index 4514d881e15..d01fb99ffac 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java @@ -16,7 +16,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * This is an example of an {@link InputTableUpdater} that validates that the values in a Double column are within a @@ -51,10 +50,7 @@ public class DoubleRangeValidatingInputTable extends AbstractBaseValidatingInput public static Table make(Table input, final String column, final double min, final double max) { - final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); - final DoubleRangeValidatingInputTable validatedUpdater = - new DoubleRangeValidatingInputTable(updater, column, min, max); - return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + return wrapUpdater(input, updater -> new DoubleRangeValidatingInputTable(updater, column, min, max)); } diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java index 9fbbe6304b8..3807f697b98 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java @@ -5,8 +5,6 @@ import com.google.protobuf.Any; import io.deephaven.engine.primitive.iterator.CloseableIterator; -import io.deephaven.engine.rowset.RowSequence; -import io.deephaven.engine.table.ColumnSource; import io.deephaven.engine.table.Table; import io.deephaven.engine.util.input.InputTableUpdater; import io.deephaven.engine.util.input.InputTableValidationException; @@ -18,7 +16,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * This is an example of an {@link InputTableUpdater} that validates that the values in a String column are not empty. @@ -46,9 +43,7 @@ public class NonEmptyValidatingInputTable extends AbstractBaseValidatingInputTab * @return a new input table that validates {@code column} is not empty */ public static Table make(Table input, final String column) { - final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); - final NonEmptyValidatingInputTable validatedUpdater = new NonEmptyValidatingInputTable(updater, column); - return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + return wrapUpdater(input, updater -> new NonEmptyValidatingInputTable(updater, column)); } @@ -84,7 +79,6 @@ private NonEmptyValidatingInputTable(InputTableUpdater wrapped, final String col public void validateAddOrModify(Table tableToApply) { final List errors = new ArrayList<>(); final MutableInt position = new MutableInt(0); - final ColumnSource columnSource = tableToApply.getColumnSource(column, String.class); try (final CloseableIterator it = tableToApply.columnIterator(column)) { it.forEachRemaining(value -> { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java index df5f1162bb2..c9dd68078c8 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java @@ -5,7 +5,6 @@ import com.google.protobuf.Any; import io.deephaven.engine.primitive.iterator.CloseableIterator; -import io.deephaven.engine.table.ColumnSource; import io.deephaven.engine.table.Table; import io.deephaven.engine.util.input.InputTableUpdater; import io.deephaven.engine.util.input.InputTableValidationException; @@ -17,7 +16,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * This is an example of an {@link InputTableUpdater} that validates that the values in a column are not null. @@ -45,9 +43,7 @@ public class NotNullValidatingInputTable extends AbstractBaseValidatingInputTabl * @return a new input table that validates {@code column} is not null */ public static Table make(Table input, final String column) { - final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); - final NotNullValidatingInputTable validatedUpdater = new NotNullValidatingInputTable(updater, column); - return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + return wrapUpdater(input, updater -> new NotNullValidatingInputTable(updater, column)); } @@ -78,7 +74,6 @@ private NotNullValidatingInputTable(InputTableUpdater wrapped, final String colu public void validateAddOrModify(Table tableToApply) { final List errors = new ArrayList<>(); final MutableInt position = new MutableInt(0); - final ColumnSource columnSource = tableToApply.getColumnSource(column); try (final CloseableIterator it = tableToApply.columnIterator(column)) { it.forEachRemaining(value -> { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java index d45679ab1da..027aa700aed 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java @@ -16,7 +16,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * This is an example of an {@link InputTableUpdater} that validates that the values in an Integer column are within a @@ -51,9 +50,7 @@ public class RangeValidatingInputTable extends AbstractBaseValidatingInputTable public static Table make(Table input, final String column, final int min, final int max) { - final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); - final RangeValidatingInputTable validatedUpdater = new RangeValidatingInputTable(updater, column, min, max); - return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + return wrapUpdater(input, updater -> new RangeValidatingInputTable(updater, column, min, max)); } diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java index 0525cf23dcd..100e13360e4 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java @@ -5,8 +5,6 @@ import com.google.protobuf.Any; import io.deephaven.engine.primitive.iterator.CloseableIterator; -import io.deephaven.engine.rowset.RowSequence; -import io.deephaven.engine.table.ColumnSource; import io.deephaven.engine.table.Table; import io.deephaven.engine.util.input.InputTableUpdater; import io.deephaven.engine.util.input.InputTableValidationException; @@ -18,7 +16,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Set; /** @@ -51,10 +48,7 @@ public class StringListValidatingInputTable extends AbstractBaseValidatingInputT * @return a new input table that validates {@code column} values are in the allowed set */ public static Table make(Table input, final String column, final String... allowedValues) { - final InputTableUpdater updater = (InputTableUpdater) input.getAttribute(Table.INPUT_TABLE_ATTRIBUTE); - final StringListValidatingInputTable validatedUpdater = - new StringListValidatingInputTable(updater, column, allowedValues); - return input.withAttributes(Map.of(Table.INPUT_TABLE_ATTRIBUTE, validatedUpdater)); + return wrapUpdater(input, updater -> new StringListValidatingInputTable(updater, column, allowedValues)); } @@ -94,7 +88,6 @@ private StringListValidatingInputTable(InputTableUpdater wrapped, final String c public void validateAddOrModify(Table tableToApply) { final List errors = new ArrayList<>(); final MutableInt position = new MutableInt(0); - final ColumnSource columnSource = tableToApply.getColumnSource(column, String.class); try (final CloseableIterator it = tableToApply.columnIterator(column)) { it.forEachRemaining(value -> { From e3282380fe843cadc60fecfe437d6925f6925c8c Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 08:33:43 -0600 Subject: [PATCH 28/35] remove for test only comments --- .../table/inputtables/AbstractBaseValidatingInputTable.java | 5 ----- .../table/inputtables/DoubleRangeValidatingInputTable.java | 5 ----- .../table/inputtables/NonEmptyValidatingInputTable.java | 5 ----- .../table/inputtables/NotNullValidatingInputTable.java | 5 ----- .../server/table/inputtables/RangeValidatingInputTable.java | 5 ----- .../table/inputtables/StringListValidatingInputTable.java | 5 ----- 6 files changed, 30 deletions(-) diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java index 86b070cdac9..ae54bd9c1a2 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/AbstractBaseValidatingInputTable.java @@ -24,11 +24,6 @@ * should override {@link #getColumnRestrictions(String)} and {@link #validateAddOrModify(Table)} to provide custom * validation logic. *

    - * - *

    - * This class is intended for testing and demonstrating validation functionality, it is not production ready and may - * be changed or removed at any time. - *

    */ @TestUseOnly public abstract class AbstractBaseValidatingInputTable implements InputTableUpdater { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java index d01fb99ffac..af29dd0d84e 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/DoubleRangeValidatingInputTable.java @@ -25,11 +25,6 @@ * This class wraps an existing input table, and before performing the underlying validation performs its own validation * on the range of the column. *

    - * - *

    - * This class is intended for testing and demonstrating validation functionality, it is not production ready and may - * be changed or removed at any time. - *

    */ @TestUseOnly public class DoubleRangeValidatingInputTable extends AbstractBaseValidatingInputTable { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java index 3807f697b98..dab7e37f840 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NonEmptyValidatingInputTable.java @@ -24,11 +24,6 @@ * This class wraps an existing input table, and before performing the underlying validation performs its own validation * that the column does not contain empty strings. *

    - * - *

    - * This class is intended for testing and demonstrating validation functionality, it is not production ready and may - * be changed or removed at any time. - *

    */ @TestUseOnly public class NonEmptyValidatingInputTable extends AbstractBaseValidatingInputTable { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java index c9dd68078c8..19d04ed9996 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/NotNullValidatingInputTable.java @@ -24,11 +24,6 @@ * This class wraps an existing input table, and before performing the underlying validation performs its own validation * that the column does not contain null values. *

    - * - *

    - * This class is intended for testing and demonstrating validation functionality, it is not production ready and may - * be changed or removed at any time. - *

    */ @TestUseOnly public class NotNullValidatingInputTable extends AbstractBaseValidatingInputTable { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java index 027aa700aed..95dfbd7483b 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/RangeValidatingInputTable.java @@ -25,11 +25,6 @@ * This class wraps an existing input table, and before performing the underlying validation performs its own validation * on the range of the column. *

    - * - *

    - * This class is intended for testing and demonstrating validation functionality, it is not production ready and may - * be changed or removed at any time. - *

    */ @TestUseOnly public class RangeValidatingInputTable extends AbstractBaseValidatingInputTable { diff --git a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java index 100e13360e4..180a3922b38 100644 --- a/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java +++ b/server/src/main/java/io/deephaven/server/table/inputtables/StringListValidatingInputTable.java @@ -26,11 +26,6 @@ * This class wraps an existing input table, and before performing the underlying validation performs its own validation * that the column values are in the allowed set (or null). *

    - * - *

    - * This class is intended for testing and demonstrating validation functionality, it is not production ready and may - * be changed or removed at any time. - *

    */ @TestUseOnly public class StringListValidatingInputTable extends AbstractBaseValidatingInputTable { From 28f86d928c7a8336bb5162926b53acc51027a044 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 09:14:04 -0600 Subject: [PATCH 29/35] groovy doc update --- docs/groovy/how-to-guides/input-tables.md | 75 +++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/docs/groovy/how-to-guides/input-tables.md b/docs/groovy/how-to-guides/input-tables.md index 6e3cf58e4aa..ba5c1075abb 100644 --- a/docs/groovy/how-to-guides/input-tables.md +++ b/docs/groovy/how-to-guides/input-tables.md @@ -172,6 +172,81 @@ result = AppendOnlyArrayBackedInputTable.make(definition) ![Manually adding a clickable link to an input table](../assets/how-to/groovy-input-table-link.gif) +## Input table validators + +Input table validators allow you to add validation rules to input tables, ensuring that data entered (either programmatically or manually through the UI) meets specific criteria. Validators wrap an existing input table and check data before it's added, throwing validation exceptions if the data doesn't meet the requirements. + +Deephaven provides several built-in validators: + +- **RangeValidatingInputTable** - Validates that integer values fall within a specified range (min/max inclusive) +- **DoubleRangeValidatingInputTable** - Validates that double values fall within a specified range (min/max inclusive) +- **NotNullValidatingInputTable** - Validates that values in a column are not null +- **NonEmptyValidatingInputTable** - Validates that string values are not empty +- **StringListValidatingInputTable** - Validates that string values belong to a predefined set of allowed values + +### Creating validated input tables + +To create a validated input table, first create a base input table, then wrap it with one or more validators. Here's an example showing all available validators: + +```groovy order=intRangeValidator,doubleRangeValidator,notNullValidator,notNullValidatorInt,nonEmptyValidator,stringListValidator +import io.deephaven.engine.table.impl.util.KeyedArrayBackedInputTable +import io.deephaven.server.table.inputtables.RangeValidatingInputTable +import io.deephaven.server.table.inputtables.DoubleRangeValidatingInputTable +import io.deephaven.server.table.inputtables.NotNullValidatingInputTable +import io.deephaven.server.table.inputtables.NonEmptyValidatingInputTable +import io.deephaven.server.table.inputtables.StringListValidatingInputTable + +// Create source table with various column types +_source = newTable( + stringCol("Key", "Apple", "Banana", "Carrot", "Date", "Eggplant"), + intCol("IntValue", 1, 2, 3, 50, 75), + doubleCol("DoubleValue", 1.5, 2.5, 3.5, 50.5, 75.5), + stringCol("Category", "Fruit", "Fruit", "Vegetable", "Fruit", "Vegetable"), + stringCol("Description", "Red", "Yellow", "Orange", "Sweet", "Purple") +) + +// Example 1: Integer Range Validator (0-100) +intRangeValidator = RangeValidatingInputTable.make( + KeyedArrayBackedInputTable.make(_source, "Key"), + "IntValue", + 0, + 100 +) + +// Example 2: Double Range Validator (0.0-100.0) +doubleRangeValidator = DoubleRangeValidatingInputTable.make( + KeyedArrayBackedInputTable.make(_source, "Key"), + "DoubleValue", + 0.0, + 100.0 +) + +// Example 3: Not Null Validator on Category column +notNullValidator = NotNullValidatingInputTable.make( + KeyedArrayBackedInputTable.make(_source, "Key"), + "Category" +) + +// Example 3.1: Not Null Validator on IntValue column +notNullValidatorInt = NotNullValidatingInputTable.make( + KeyedArrayBackedInputTable.make(_source, "Key"), + "IntValue" +) + +// Example 4: Non-Empty Validator on Description column +nonEmptyValidator = NonEmptyValidatingInputTable.make( + KeyedArrayBackedInputTable.make(_source, "Key"), + "Description" +) + +// Example 5: String List Validator - Category must be "Fruit", "Vegetable", or "Grain" +stringListValidator = StringListValidatingInputTable.make( + KeyedArrayBackedInputTable.make(_source, "Key"), + "Category", + "Fruit", "Vegetable", "Grain" +) +``` + ## Related documentation - [Input Table](../reference/table-operations/create/InputTable.md) From a9394c58b32c0c8fdb572d55edeaf9f497258a44 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 09:16:46 -0600 Subject: [PATCH 30/35] python docs --- docs/python/how-to-guides/input-tables.md | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/docs/python/how-to-guides/input-tables.md b/docs/python/how-to-guides/input-tables.md index 18b06063343..b6da9c798e8 100644 --- a/docs/python/how-to-guides/input-tables.md +++ b/docs/python/how-to-guides/input-tables.md @@ -222,6 +222,85 @@ result = input_table(col_defs=my_col_defs) ![Manually adding a clickable link to an input table](../assets/how-to/ui/clickable_link_gif.gif) +## Input table validators + +Input table validators allow you to add validation rules to input tables, ensuring that data entered (either programmatically or manually through the UI) meets specific criteria. Validators wrap an existing input table and check data before it's added, throwing validation exceptions if the data doesn't meet the requirements. + +Deephaven provides several built-in validators: + +- **RangeValidatingInputTable** - Validates that integer values fall within a specified range (min/max inclusive) +- **DoubleRangeValidatingInputTable** - Validates that double values fall within a specified range (min/max inclusive) +- **NotNullValidatingInputTable** - Validates that values in a column are not null +- **NonEmptyValidatingInputTable** - Validates that string values are not empty +- **StringListValidatingInputTable** - Validates that string values belong to a predefined set of allowed values + +### Creating validated input tables + +To create a validated input table, first create a base input table, then wrap it with one or more validators. Here's an example showing all available validators: + +```python order=intRangeValidator,doubleRangeValidator,notNullValidator,notNullValidatorInt,nonEmptyValidator,stringListValidator +from deephaven import new_table, input_table +from deephaven.column import string_col, int_col, double_col + +# Create source table with various column types +_source = new_table([ + string_col("Key", ["Apple", "Banana", "Carrot", "Date", "Eggplant"]), + int_col("IntValue", [1, 2, 3, 50, 75]), + double_col("DoubleValue", [1.5, 2.5, 3.5, 50.5, 75.5]), + string_col("Category", ["Fruit", "Fruit", "Vegetable", "Fruit", "Vegetable"]), + string_col("Description", ["Red", "Yellow", "Orange", "Sweet", "Purple"]) +]) + +# Import Java classes for validators (not available in Python API yet) +import jpy +RangeValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.RangeValidatingInputTable") +DoubleRangeValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.DoubleRangeValidatingInputTable") +NotNullValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.NotNullValidatingInputTable") +NonEmptyValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.NonEmptyValidatingInputTable") +StringListValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.StringListValidatingInputTable") + +# Example 1: Integer Range Validator (0-100) +intRangeValidator = RangeValidatingInputTable.make( + input_table(init_table=_source, key_cols="Key").j_table, + "IntValue", + 0, + 100 +) + +# Example 2: Double Range Validator (0.0-100.0) +doubleRangeValidator = DoubleRangeValidatingInputTable.make( + input_table(init_table=_source, key_cols="Key").j_table, + "DoubleValue", + 0.0, + 100.0 +) + +# Example 3: Not Null Validator on Category column +notNullValidator = NotNullValidatingInputTable.make( + input_table(init_table=_source, key_cols="Key").j_table, + "Category" +) + +# Example 3.1: Not Null Validator on IntValue column +notNullValidatorInt = NotNullValidatingInputTable.make( + input_table(init_table=_source, key_cols="Key").j_table, + "IntValue" +) + +# Example 4: Non-Empty Validator on Description column +nonEmptyValidator = NonEmptyValidatingInputTable.make( + input_table(init_table=_source, key_cols="Key").j_table, + "Description" +) + +# Example 5: String List Validator - Category must be "Fruit" or "Vegetable" +stringListValidator = StringListValidatingInputTable.make( + input_table(init_table=_source, key_cols="Key").j_table, + "Category", + "Fruit", "Vegetable", "Grain" +) +``` + ## Related documentation - [`input_table`](../reference/table-operations/create/input-table.md) From 4c21aa593874a58b33dad3953dbca3f11cdf6cd7 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 09:19:13 -0600 Subject: [PATCH 31/35] fix case --- docs/python/how-to-guides/input-tables.md | 38 +++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/python/how-to-guides/input-tables.md b/docs/python/how-to-guides/input-tables.md index b6da9c798e8..c7fe5bc59ab 100644 --- a/docs/python/how-to-guides/input-tables.md +++ b/docs/python/how-to-guides/input-tables.md @@ -238,12 +238,12 @@ Deephaven provides several built-in validators: To create a validated input table, first create a base input table, then wrap it with one or more validators. Here's an example showing all available validators: -```python order=intRangeValidator,doubleRangeValidator,notNullValidator,notNullValidatorInt,nonEmptyValidator,stringListValidator +```python order=int_range_validator,double_range_validator,not_null_validator,not_null_validator_int,non_empty_validator,string_list_validator from deephaven import new_table, input_table from deephaven.column import string_col, int_col, double_col # Create source table with various column types -_source = new_table([ +source = new_table([ string_col("Key", ["Apple", "Banana", "Carrot", "Date", "Eggplant"]), int_col("IntValue", [1, 2, 3, 50, 75]), double_col("DoubleValue", [1.5, 2.5, 3.5, 50.5, 75.5]), @@ -253,49 +253,49 @@ _source = new_table([ # Import Java classes for validators (not available in Python API yet) import jpy -RangeValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.RangeValidatingInputTable") -DoubleRangeValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.DoubleRangeValidatingInputTable") -NotNullValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.NotNullValidatingInputTable") -NonEmptyValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.NonEmptyValidatingInputTable") -StringListValidatingInputTable = jpy.get_type("io.deephaven.server.table.inputtables.StringListValidatingInputTable") +range_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.RangeValidatingInputTable") +double_range_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.DoubleRangeValidatingInputTable") +not_null_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.NotNullValidatingInputTable") +non_empty_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.NonEmptyValidatingInputTable") +string_list_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.StringListValidatingInputTable") # Example 1: Integer Range Validator (0-100) -intRangeValidator = RangeValidatingInputTable.make( - input_table(init_table=_source, key_cols="Key").j_table, +int_range_validator = range_validating_input_table.make( + input_table(init_table=source, key_cols="Key").j_table, "IntValue", 0, 100 ) # Example 2: Double Range Validator (0.0-100.0) -doubleRangeValidator = DoubleRangeValidatingInputTable.make( - input_table(init_table=_source, key_cols="Key").j_table, +double_range_validator = double_range_validating_input_table.make( + input_table(init_table=source, key_cols="Key").j_table, "DoubleValue", 0.0, 100.0 ) # Example 3: Not Null Validator on Category column -notNullValidator = NotNullValidatingInputTable.make( - input_table(init_table=_source, key_cols="Key").j_table, +not_null_validator = not_null_validating_input_table.make( + input_table(init_table=source, key_cols="Key").j_table, "Category" ) # Example 3.1: Not Null Validator on IntValue column -notNullValidatorInt = NotNullValidatingInputTable.make( - input_table(init_table=_source, key_cols="Key").j_table, +not_null_validator_int = not_null_validating_input_table.make( + input_table(init_table=source, key_cols="Key").j_table, "IntValue" ) # Example 4: Non-Empty Validator on Description column -nonEmptyValidator = NonEmptyValidatingInputTable.make( - input_table(init_table=_source, key_cols="Key").j_table, +non_empty_validator = non_empty_validating_input_table.make( + input_table(init_table=source, key_cols="Key").j_table, "Description" ) # Example 5: String List Validator - Category must be "Fruit" or "Vegetable" -stringListValidator = StringListValidatingInputTable.make( - input_table(init_table=_source, key_cols="Key").j_table, +string_list_validator = string_list_validating_input_table.make( + input_table(init_table=source, key_cols="Key").j_table, "Category", "Fruit", "Vegetable", "Grain" ) From 5b5b29116a446021f2c00bf95b1ad82f0247573a Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 09:38:32 -0600 Subject: [PATCH 32/35] format --- docs/python/how-to-guides/input-tables.md | 62 +++++++++++++---------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/docs/python/how-to-guides/input-tables.md b/docs/python/how-to-guides/input-tables.md index c7fe5bc59ab..6e8571e4365 100644 --- a/docs/python/how-to-guides/input-tables.md +++ b/docs/python/how-to-guides/input-tables.md @@ -243,61 +243,67 @@ from deephaven import new_table, input_table from deephaven.column import string_col, int_col, double_col # Create source table with various column types -source = new_table([ - string_col("Key", ["Apple", "Banana", "Carrot", "Date", "Eggplant"]), - int_col("IntValue", [1, 2, 3, 50, 75]), - double_col("DoubleValue", [1.5, 2.5, 3.5, 50.5, 75.5]), - string_col("Category", ["Fruit", "Fruit", "Vegetable", "Fruit", "Vegetable"]), - string_col("Description", ["Red", "Yellow", "Orange", "Sweet", "Purple"]) -]) +source = new_table( + [ + string_col("Key", ["Apple", "Banana", "Carrot", "Date", "Eggplant"]), + int_col("IntValue", [1, 2, 3, 50, 75]), + double_col("DoubleValue", [1.5, 2.5, 3.5, 50.5, 75.5]), + string_col("Category", ["Fruit", "Fruit", "Vegetable", "Fruit", "Vegetable"]), + string_col("Description", ["Red", "Yellow", "Orange", "Sweet", "Purple"]), + ] +) # Import Java classes for validators (not available in Python API yet) import jpy -range_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.RangeValidatingInputTable") -double_range_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.DoubleRangeValidatingInputTable") -not_null_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.NotNullValidatingInputTable") -non_empty_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.NonEmptyValidatingInputTable") -string_list_validating_input_table = jpy.get_type("io.deephaven.server.table.inputtables.StringListValidatingInputTable") + +range_validating_input_table = jpy.get_type( + "io.deephaven.server.table.inputtables.RangeValidatingInputTable" +) +double_range_validating_input_table = jpy.get_type( + "io.deephaven.server.table.inputtables.DoubleRangeValidatingInputTable" +) +not_null_validating_input_table = jpy.get_type( + "io.deephaven.server.table.inputtables.NotNullValidatingInputTable" +) +non_empty_validating_input_table = jpy.get_type( + "io.deephaven.server.table.inputtables.NonEmptyValidatingInputTable" +) +string_list_validating_input_table = jpy.get_type( + "io.deephaven.server.table.inputtables.StringListValidatingInputTable" +) # Example 1: Integer Range Validator (0-100) int_range_validator = range_validating_input_table.make( - input_table(init_table=source, key_cols="Key").j_table, - "IntValue", - 0, - 100 + input_table(init_table=source, key_cols="Key").j_table, "IntValue", 0, 100 ) # Example 2: Double Range Validator (0.0-100.0) double_range_validator = double_range_validating_input_table.make( - input_table(init_table=source, key_cols="Key").j_table, - "DoubleValue", - 0.0, - 100.0 + input_table(init_table=source, key_cols="Key").j_table, "DoubleValue", 0.0, 100.0 ) # Example 3: Not Null Validator on Category column not_null_validator = not_null_validating_input_table.make( - input_table(init_table=source, key_cols="Key").j_table, - "Category" + input_table(init_table=source, key_cols="Key").j_table, "Category" ) # Example 3.1: Not Null Validator on IntValue column not_null_validator_int = not_null_validating_input_table.make( - input_table(init_table=source, key_cols="Key").j_table, - "IntValue" + input_table(init_table=source, key_cols="Key").j_table, "IntValue" ) # Example 4: Non-Empty Validator on Description column non_empty_validator = non_empty_validating_input_table.make( - input_table(init_table=source, key_cols="Key").j_table, - "Description" + input_table(init_table=source, key_cols="Key").j_table, "Description" ) # Example 5: String List Validator - Category must be "Fruit" or "Vegetable" string_list_validator = string_list_validating_input_table.make( input_table(init_table=source, key_cols="Key").j_table, - "Category", - "Fruit", "Vegetable", "Grain" + "Category", + "Fruit", + "Vegetable", + "Grain", ) ``` From 1d006226b74919f54f479803e1eccb9bbba7e2a3 Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 09:48:07 -0600 Subject: [PATCH 33/35] update snapshots --- docs/groovy/snapshots/52261de3cea59a05c348dcdee2078741.json | 1 + docs/python/snapshots/1c93fc2136637c6bbc241e13ab956466.json | 1 + 2 files changed, 2 insertions(+) create mode 100644 docs/groovy/snapshots/52261de3cea59a05c348dcdee2078741.json create mode 100644 docs/python/snapshots/1c93fc2136637c6bbc241e13ab956466.json diff --git a/docs/groovy/snapshots/52261de3cea59a05c348dcdee2078741.json b/docs/groovy/snapshots/52261de3cea59a05c348dcdee2078741.json new file mode 100644 index 00000000000..18948727473 --- /dev/null +++ b/docs/groovy/snapshots/52261de3cea59a05c348dcdee2078741.json @@ -0,0 +1 @@ +{"file":"how-to-guides/input-tables.md","objects":{"_source":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"intRangeValidator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"doubleRangeValidator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"notNullValidator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"notNullValidatorInt":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"nonEmptyValidator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"stringListValidator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}}}} \ No newline at end of file diff --git a/docs/python/snapshots/1c93fc2136637c6bbc241e13ab956466.json b/docs/python/snapshots/1c93fc2136637c6bbc241e13ab956466.json new file mode 100644 index 00000000000..cab4e621e40 --- /dev/null +++ b/docs/python/snapshots/1c93fc2136637c6bbc241e13ab956466.json @@ -0,0 +1 @@ +{"file":"how-to-guides/input-tables.md","objects":{"source":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"int_range_validator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"double_range_validator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"not_null_validator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"not_null_validator_int":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"non_empty_validator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}},"string_list_validator":{"type":"Table","data":{"columns":[{"name":"Key","type":"java.lang.String"},{"name":"IntValue","type":"int"},{"name":"DoubleValue","type":"double"},{"name":"Category","type":"java.lang.String"},{"name":"Description","type":"java.lang.String"}],"rows":[[{"value":"Apple"},{"value":"1"},{"value":"1.5000"},{"value":"Fruit"},{"value":"Red"}],[{"value":"Banana"},{"value":"2"},{"value":"2.5000"},{"value":"Fruit"},{"value":"Yellow"}],[{"value":"Carrot"},{"value":"3"},{"value":"3.5000"},{"value":"Vegetable"},{"value":"Orange"}],[{"value":"Date"},{"value":"50"},{"value":"50.5000"},{"value":"Fruit"},{"value":"Sweet"}],[{"value":"Eggplant"},{"value":"75"},{"value":"75.5000"},{"value":"Vegetable"},{"value":"Purple"}]]}}}} \ No newline at end of file From 136a5b44646d16aef3601dd0141ffcfe9ca565c8 Mon Sep 17 00:00:00 2001 From: dgodinez-dh Date: Wed, 29 Apr 2026 13:03:14 -0600 Subject: [PATCH 34/35] Apply suggestions from code review Co-authored-by: margaretkennedy <82049573+margaretkennedy@users.noreply.github.com> --- docs/groovy/how-to-guides/input-tables.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/groovy/how-to-guides/input-tables.md b/docs/groovy/how-to-guides/input-tables.md index ba5c1075abb..489b23149f4 100644 --- a/docs/groovy/how-to-guides/input-tables.md +++ b/docs/groovy/how-to-guides/input-tables.md @@ -178,11 +178,11 @@ Input table validators allow you to add validation rules to input tables, ensuri Deephaven provides several built-in validators: -- **RangeValidatingInputTable** - Validates that integer values fall within a specified range (min/max inclusive) -- **DoubleRangeValidatingInputTable** - Validates that double values fall within a specified range (min/max inclusive) -- **NotNullValidatingInputTable** - Validates that values in a column are not null -- **NonEmptyValidatingInputTable** - Validates that string values are not empty -- **StringListValidatingInputTable** - Validates that string values belong to a predefined set of allowed values +- **`RangeValidatingInputTable`** - Validates that integer values fall within a specified range (min/max inclusive). +- **`DoubleRangeValidatingInputTable`** - Validates that double values fall within a specified range (min/max inclusive). +- **`NotNullValidatingInputTable`** - Validates that values in a column are not null. +- **`NonEmptyValidatingInputTable`** - Validates that string values are not empty. +- **`StringListValidatingInputTable`** - Validates that string values belong to a predefined set of allowed values. ### Creating validated input tables From 3fe5ae79867e541cdb4d5fa2419d08a46ac4d2fe Mon Sep 17 00:00:00 2001 From: davidgodinez Date: Wed, 29 Apr 2026 13:06:52 -0600 Subject: [PATCH 35/35] python doc style update --- docs/python/how-to-guides/input-tables.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/python/how-to-guides/input-tables.md b/docs/python/how-to-guides/input-tables.md index 6e8571e4365..028015e0b49 100644 --- a/docs/python/how-to-guides/input-tables.md +++ b/docs/python/how-to-guides/input-tables.md @@ -228,11 +228,11 @@ Input table validators allow you to add validation rules to input tables, ensuri Deephaven provides several built-in validators: -- **RangeValidatingInputTable** - Validates that integer values fall within a specified range (min/max inclusive) -- **DoubleRangeValidatingInputTable** - Validates that double values fall within a specified range (min/max inclusive) -- **NotNullValidatingInputTable** - Validates that values in a column are not null -- **NonEmptyValidatingInputTable** - Validates that string values are not empty -- **StringListValidatingInputTable** - Validates that string values belong to a predefined set of allowed values +- **`RangeValidatingInputTable`** - Validates that integer values fall within a specified range (min/max inclusive). +- **`DoubleRangeValidatingInputTable`** - Validates that double values fall within a specified range (min/max inclusive). +- **`NotNullValidatingInputTable`** - Validates that values in a column are not null. +- **`NonEmptyValidatingInputTable`** - Validates that string values are not empty. +- **`StringListValidatingInputTable`** - Validates that string values belong to a predefined set of allowed values. ### Creating validated input tables