From b41a4d7c4494e7c8efe1ffdf0e215c3f3827e7bb Mon Sep 17 00:00:00 2001 From: kriyanshii Date: Fri, 27 Feb 2026 23:24:52 +0530 Subject: [PATCH 1/3] GH-37958: [MATLAB] Centralize Command Window hyperlink formatting Reduce duplicate hyperlink formatting logic in Command Window display helpers. * Add a shared hyperlink utility in MATLAB display helpers. * Use it in schema string rendering and test helpers. Not run (manual check in MATLAB Command Window). Yes. Command Window display now uses a shared hyperlink helper. * GitHub Issue: #37958 --- .../+internal/+display/makeLinkString.m | 40 +++++++++++++++++++ .../+internal/+test/+display/makeLinkString.m | 12 ++---- .../+internal/+display/getSchemaString.m | 25 +++--------- 3 files changed, 50 insertions(+), 27 deletions(-) create mode 100644 matlab/src/matlab/+arrow/+internal/+display/makeLinkString.m diff --git a/matlab/src/matlab/+arrow/+internal/+display/makeLinkString.m b/matlab/src/matlab/+arrow/+internal/+display/makeLinkString.m new file mode 100644 index 000000000000..c93cbda4d3e4 --- /dev/null +++ b/matlab/src/matlab/+arrow/+internal/+display/makeLinkString.m @@ -0,0 +1,40 @@ +%MAKELINKSTRING Creates a hyperlink string if possible. + +% Licensed to the Apache Software Foundation (ASF) under one or more +% contributor license agreements. See the NOTICE file distributed with +% this work for additional information regarding copyright ownership. +% The ASF licenses this file to you under the Apache License, Version +% 2.0 (the "License"); you may not use this file except in compliance +% with the License. You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +% implied. See the License for the specific language governing +% permissions and limitations under the License. + +function link = makeLinkString(opts) + arguments + opts.HelpTarget(1, 1) string {mustBeNonzeroLengthText} + opts.Text(1, 1) string {mustBeNonzeroLengthText} + % When displaying heterogeneous arrays, only the name of the + % closest shared ancestor class is displayed in bold. All other + % class names are not bolded. + opts.BoldFont(1, 1) logical = false + end + + if usejava("desktop") + if opts.BoldFont + link = compose("%s", ... + opts.HelpTarget, opts.Text); + else + link = compose("%s", ... + opts.HelpTarget, opts.Text); + end + else + link = opts.Text; + end +end diff --git a/matlab/src/matlab/+arrow/+internal/+test/+display/makeLinkString.m b/matlab/src/matlab/+arrow/+internal/+test/+display/makeLinkString.m index e99dd7d78488..37daa0091608 100644 --- a/matlab/src/matlab/+arrow/+internal/+test/+display/makeLinkString.m +++ b/matlab/src/matlab/+arrow/+internal/+test/+display/makeLinkString.m @@ -25,12 +25,8 @@ opts.BoldFont(1, 1) logical end - if opts.BoldFont - link = compose("%s", ... - opts.FullClassName, opts.ClassName); - else - link = compose("%s", ... - opts.FullClassName, opts.ClassName); - end + link = arrow.internal.display.makeLinkString( ... + HelpTarget=opts.FullClassName, ... + Text=opts.ClassName, ... + BoldFont=opts.BoldFont); end \ No newline at end of file diff --git a/matlab/src/matlab/+arrow/+tabular/+internal/+display/getSchemaString.m b/matlab/src/matlab/+arrow/+tabular/+internal/+display/getSchemaString.m index 724b4873c92e..665a9f4ef374 100644 --- a/matlab/src/matlab/+arrow/+tabular/+internal/+display/getSchemaString.m +++ b/matlab/src/matlab/+arrow/+tabular/+internal/+display/getSchemaString.m @@ -26,25 +26,12 @@ idx = strlength(names) == 0; names(idx) = ""; - if usejava("desktop") - % When in desktop mode, the Command Window can interpret HTML tags - % to display bold font and hyperlinks. - names = compose("%s", names); - classNames = arrayfun(@(type) string(class(type)), types); - - % Creates a string array with the following form: - % - % ["arrow.type.BooleanType" "Boolean" "arrow.type.StringType" "String" ...] - % - % This string array is passed to the compose call below. The - % format specifier operator supplied to compose contains two - % formatting operators (%s), so compose uses two elements from the - % string array (classNameAndIDs) at a time. - classNameAndIDs = strings([1 numel(typeIDs) * 2]); - classNameAndIDs(1:2:end-1) = classNames; - classNameAndIDs(2:2:end) = typeIDs; - typeIDs = compose("%s", classNameAndIDs); - end + % When in desktop mode, the Command Window can interpret HTML tags + % to display bold font and hyperlinks. + names = arrayfun(@arrow.internal.display.boldFontIfPossible, names); + classNames = arrayfun(@(type) string(class(type)), types); + typeIDs = arrayfun(@(className, typeID) arrow.internal.display.makeLinkString( ... + HelpTarget=className, Text=typeID, BoldFont=true), classNames, typeIDs); text = names + ": " + typeIDs; text = strjoin(text, " | "); From b9acf7a3f18bb1369e54d3489ab9dc073bf8db82 Mon Sep 17 00:00:00 2001 From: kriyanshii Date: Tue, 3 Mar 2026 22:39:00 +0530 Subject: [PATCH 2/3] review change: removed unncessary comment --- .../+arrow/+tabular/+internal/+display/getSchemaString.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/matlab/src/matlab/+arrow/+tabular/+internal/+display/getSchemaString.m b/matlab/src/matlab/+arrow/+tabular/+internal/+display/getSchemaString.m index 665a9f4ef374..12b51ed60e18 100644 --- a/matlab/src/matlab/+arrow/+tabular/+internal/+display/getSchemaString.m +++ b/matlab/src/matlab/+arrow/+tabular/+internal/+display/getSchemaString.m @@ -26,12 +26,12 @@ idx = strlength(names) == 0; names(idx) = ""; - % When in desktop mode, the Command Window can interpret HTML tags - % to display bold font and hyperlinks. names = arrayfun(@arrow.internal.display.boldFontIfPossible, names); classNames = arrayfun(@(type) string(class(type)), types); - typeIDs = arrayfun(@(className, typeID) arrow.internal.display.makeLinkString( ... - HelpTarget=className, Text=typeID, BoldFont=true), classNames, typeIDs); + if usejava("desktop") + typeIDs = arrayfun(@(className, typeID) arrow.internal.display.makeLinkString( ... + HelpTarget=className, Text=typeID, BoldFont=true), classNames, typeIDs); + end text = names + ": " + typeIDs; text = strjoin(text, " | "); From be848d4c957509b4296efaffd4c3e0bffc01a322 Mon Sep 17 00:00:00 2001 From: kriyanshii Date: Mon, 9 Mar 2026 19:36:41 +0530 Subject: [PATCH 3/3] Fix display tests by using makeLinkString for class names - Add getClassNameForDisplay, getNumString, makeDimensionString helpers - Update Array, Schema, Tabular, Type, Field display to use short class names with optional hyperlinks - Fix tSchema TestIsEqualTrue/False: use table() instead of table for empty schema --- .../+array/+internal/+display/getHeader.m | 22 +++++------ matlab/src/matlab/+arrow/+array/Array.m | 4 +- .../+display/getClassNameForDisplay.m | 31 ++++++++++++++++ .../+arrow/+internal/+display/getNumString.m | 25 +++++++++++++ .../+internal/+display/makeDimensionString.m | 21 +++++++++++ .../+internal/+display/getTabularHeader.m | 12 +++--- matlab/src/matlab/+arrow/+tabular/Schema.m | 13 +++++-- matlab/src/matlab/+arrow/+type/Field.m | 8 ++++ matlab/src/matlab/+arrow/+type/Type.m | 37 ++++++++++++++++++- matlab/test/arrow/tabular/tSchema.m | 6 +-- 10 files changed, 153 insertions(+), 26 deletions(-) create mode 100644 matlab/src/matlab/+arrow/+internal/+display/getClassNameForDisplay.m create mode 100644 matlab/src/matlab/+arrow/+internal/+display/getNumString.m create mode 100644 matlab/src/matlab/+arrow/+internal/+display/makeDimensionString.m diff --git a/matlab/src/matlab/+arrow/+array/+internal/+display/getHeader.m b/matlab/src/matlab/+arrow/+array/+internal/+display/getHeader.m index 85301ddefaad..f171cb618025 100644 --- a/matlab/src/matlab/+arrow/+array/+internal/+display/getHeader.m +++ b/matlab/src/matlab/+arrow/+array/+internal/+display/getHeader.m @@ -15,24 +15,24 @@ % implied. See the License for the specific language governing % permissions and limitations under the License. -function header = getHeader(className, numElements, numNulls) +function header = getHeader(fullClassName, numElements, numNulls) import arrow.internal.display.pluralizeStringIfNeeded - elementString = pluralizeStringIfNeeded(numElements, "element"); + import arrow.internal.display.getClassNameForDisplay + import arrow.internal.display.getNumString + className = getClassNameForDisplay(fullClassName); + elementString = pluralizeStringIfNeeded(numElements, "element"); nullString = pluralizeStringIfNeeded(numNulls, "null value"); - - numString = "%d"; - if usejava("desktop") - % Bold the number of elements and nulls if the desktop is enabled - numString = compose("%s", numString); - end - formatSpec = " %s with " + numString + " %s and " + numString + " %s"; + numElementString = getNumString(numElements); + numNullString = getNumString(numNulls); + + formatSpec = " %s with %s %s and %s %s"; if numElements > 0 formatSpec = formatSpec + ":"; end formatSpec = formatSpec + newline; - - header = compose(formatSpec, className, numElements, elementString, numNulls, nullString); + + header = compose(formatSpec, className, numElementString, elementString, numNullString, nullString); header = char(header); end \ No newline at end of file diff --git a/matlab/src/matlab/+arrow/+array/Array.m b/matlab/src/matlab/+arrow/+array/Array.m index 09932abec78c..5d1e3c1a6d9c 100644 --- a/matlab/src/matlab/+arrow/+array/Array.m +++ b/matlab/src/matlab/+arrow/+array/Array.m @@ -82,13 +82,13 @@ function validate(obj, opts) methods (Access=protected) function header = getHeader(obj) - name = matlab.mixin.CustomDisplay.getClassNameForHeader(obj); + fullClassName = matlab.mixin.CustomDisplay.getClassNameForHeader(obj); numElements = obj.NumElements; % TODO: Add NumValid and NumNull as properties to Array to % avoid materializing the Valid property. This will improve % performance for large arrays. numNulls = nnz(~obj.Valid); - header = arrow.array.internal.display.getHeader(name, numElements, numNulls); + header = arrow.array.internal.display.getHeader(fullClassName, numElements, numNulls); end function displayScalarObject(obj) diff --git a/matlab/src/matlab/+arrow/+internal/+display/getClassNameForDisplay.m b/matlab/src/matlab/+arrow/+internal/+display/getClassNameForDisplay.m new file mode 100644 index 000000000000..87a08d9d2142 --- /dev/null +++ b/matlab/src/matlab/+arrow/+internal/+display/getClassNameForDisplay.m @@ -0,0 +1,31 @@ +%GETCLASSNAMEFORDISPLAY Returns a display-ready class name string, optionally +%with a hyperlink when the MATLAB desktop is available. + +% Licensed to the Apache Software Foundation (ASF) under one or more +% contributor license agreements. See the NOTICE file distributed with +% this work for additional information regarding copyright ownership. +% The ASF licenses this file to you under the Apache License, Version +% 2.0 (the "License"); you may not use this file except in compliance +% with the License. You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OF CONDITIONS OF ANY KIND, either express or +% implied. See the License for the specific language governing +% permissions and limitations under the License. + +function displayName = getClassNameForDisplay(fullClassName, opts) + arguments + fullClassName(1, 1) string + opts.BoldFont(1, 1) logical = true + end + % Extract the short class name (e.g., "BooleanArray" from "arrow.array.BooleanArray") + parts = split(fullClassName, "."); + shortName = parts(end); + displayName = arrow.internal.display.makeLinkString(... + HelpTarget=fullClassName, ... + Text=shortName, ... + BoldFont=opts.BoldFont); +end diff --git a/matlab/src/matlab/+arrow/+internal/+display/getNumString.m b/matlab/src/matlab/+arrow/+internal/+display/getNumString.m new file mode 100644 index 000000000000..bfdc4e80eee0 --- /dev/null +++ b/matlab/src/matlab/+arrow/+internal/+display/getNumString.m @@ -0,0 +1,25 @@ +%GETNUMSTRING Returns a string representation of a number, optionally bolded +%when the MATLAB desktop is available. + +% Licensed to the Apache Software Foundation (ASF) under one or more +% contributor license agreements. See the NOTICE file distributed with +% this work for additional information regarding copyright ownership. +% The ASF licenses this file to you under the Apache License, Version +% 2.0 (the "License"); you may not use this file except in compliance +% with the License. You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OF CONDITIONS OF ANY KIND, either express or +% implied. See the License for the specific language governing +% permissions and limitations under the License. + +function str = getNumString(num) + if usejava("desktop") + str = compose("%d", num); + else + str = compose("%d", num); + end +end diff --git a/matlab/src/matlab/+arrow/+internal/+display/makeDimensionString.m b/matlab/src/matlab/+arrow/+internal/+display/makeDimensionString.m new file mode 100644 index 000000000000..0b300e1221dd --- /dev/null +++ b/matlab/src/matlab/+arrow/+internal/+display/makeDimensionString.m @@ -0,0 +1,21 @@ +%MAKEDIMENSIONSTRING Creates a string representation of array dimensions. + +% Licensed to the Apache Software Foundation (ASF) under one or more +% contributor license agreements. See the NOTICE file distributed with +% this work for additional information regarding copyright ownership. +% The ASF licenses this file to you under the Apache License, Version +% 2.0 (the "License"); you may not use this file except in compliance +% with the License. You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OF CONDITIONS OF ANY KIND, either express or +% implied. See the License for the specific language governing +% permissions and limitations under the License. + +function dimensionString = makeDimensionString(arraySize) + dimensionString = string(arraySize); + dimensionString = join(dimensionString, char(215)); +end diff --git a/matlab/src/matlab/+arrow/+tabular/+internal/+display/getTabularHeader.m b/matlab/src/matlab/+arrow/+tabular/+internal/+display/getTabularHeader.m index 4c647986ce05..9ea43bfff0b2 100644 --- a/matlab/src/matlab/+arrow/+tabular/+internal/+display/getTabularHeader.m +++ b/matlab/src/matlab/+arrow/+tabular/+internal/+display/getTabularHeader.m @@ -16,17 +16,19 @@ % implied. See the License for the specific language governing % permissions and limitations under the License. -function header = getTabularHeader(className, numRows, numColumns) - import arrow.internal.display.boldFontIfPossible +function header = getTabularHeader(fullClassName, numRows, numColumns) + import arrow.internal.display.getClassNameForDisplay + import arrow.internal.display.getNumString import arrow.internal.display.pluralizeStringIfNeeded - numRowsString = boldFontIfPossible(numRows); - numColsString = boldFontIfPossible(numColumns); + className = getClassNameForDisplay(fullClassName); + numRowsString = getNumString(numRows); + numColsString = getNumString(numColumns); rowWordString = pluralizeStringIfNeeded(numRows, "row"); colWordString = pluralizeStringIfNeeded(numColumns, "column"); formatSpec = " Arrow %s with %s %s and %s %s"; if numColumns > 0 formatSpec = formatSpec + ":"; end - header = compose(formatSpec,className, numRowsString, rowWordString, numColsString, colWordString); + header = compose(formatSpec, className, numRowsString, rowWordString, numColsString, colWordString); end \ No newline at end of file diff --git a/matlab/src/matlab/+arrow/+tabular/Schema.m b/matlab/src/matlab/+arrow/+tabular/Schema.m index a50522c6b528..2f9da03bdc12 100644 --- a/matlab/src/matlab/+arrow/+tabular/Schema.m +++ b/matlab/src/matlab/+arrow/+tabular/Schema.m @@ -100,14 +100,19 @@ methods (Access=protected) function header = getHeader(obj) - name = matlab.mixin.CustomDisplay.getClassNameForHeader(obj); + import arrow.internal.display.getClassNameForDisplay + import arrow.internal.display.getNumString + + fullClassName = matlab.mixin.CustomDisplay.getClassNameForHeader(obj); + className = getClassNameForDisplay(fullClassName); numFields = obj.NumFields; + numFieldsString = getNumString(numFields); if numFields == 0 - header = compose(" Arrow %s with 0 fields" + newline, name); + header = compose(" Arrow %s with %s fields" + newline, className, numFieldsString); elseif numFields == 1 - header = compose(" Arrow %s with %d field:" + newline, name, numFields); + header = compose(" Arrow %s with %s field:" + newline, className, numFieldsString); else - header = compose(" Arrow %s with %d fields:" + newline, name, numFields); + header = compose(" Arrow %s with %s fields:" + newline, className, numFieldsString); end end diff --git a/matlab/src/matlab/+arrow/+type/Field.m b/matlab/src/matlab/+arrow/+type/Field.m index d6e03f61fbea..6846564d878d 100644 --- a/matlab/src/matlab/+arrow/+type/Field.m +++ b/matlab/src/matlab/+arrow/+type/Field.m @@ -92,6 +92,14 @@ end methods (Access=protected) + function header = getHeader(obj) + import arrow.internal.display.getClassNameForDisplay + + fullClassName = matlab.mixin.CustomDisplay.getClassNameForHeader(obj); + className = getClassNameForDisplay(fullClassName); + header = " " + className + " with properties:" + newline; + end + function groups = getPropertyGroups(~) targets = ["Name", "Type"]; groups = matlab.mixin.util.PropertyGroup(targets); diff --git a/matlab/src/matlab/+arrow/+type/Type.m b/matlab/src/matlab/+arrow/+type/Type.m index 2a2ba145a644..7b41b4533773 100644 --- a/matlab/src/matlab/+arrow/+type/Type.m +++ b/matlab/src/matlab/+arrow/+type/Type.m @@ -73,7 +73,42 @@ methods (Sealed, Access = protected) function header = getHeader(obj) - header = getHeader@matlab.mixin.CustomDisplay(obj); + import arrow.internal.display.getClassNameForDisplay + import arrow.internal.display.makeLinkString + import arrow.internal.display.makeDimensionString + + if isempty(obj) + fullClassName = "arrow.type.Type"; + typeLink = getClassNameForDisplay(fullClassName); + dimensionString = makeDimensionString(size(obj)); + header = " " + dimensionString + " " + typeLink + " array with properties:" + newline; + elseif isscalar(obj) + fullClassName = string(class(obj)); + typeLink = getClassNameForDisplay(fullClassName); + header = " " + typeLink + " with properties:" + newline; + else + dimensionString = makeDimensionString(size(obj)); + classNames = arrayfun(@(x) string(class(x)), obj); + uniqueClasses = unique(classNames); + if numel(uniqueClasses) == 1 + typeLink = getClassNameForDisplay(uniqueClasses(1)); + header = " " + dimensionString + " " + typeLink + " array with properties:" + newline; + else + heterogeneousLink = makeLinkString(HelpTarget="matlab.mixin.Heterogeneous", Text="heterogeneous", BoldFont=false); + baseClassName = string(class(obj)); + baseClassLink = getClassNameForDisplay(baseClassName, BoldFont=true); + typeLinkParts = strings(size(uniqueClasses)); + for ii = 1:numel(uniqueClasses) + c = uniqueClasses(ii); + parts = split(c, "."); + shortName = parts(end); + typeLinkParts(ii) = makeLinkString(HelpTarget=c, Text=shortName, BoldFont=false); + end + typeLinksStr = join(typeLinkParts, ", "); + header = " " + dimensionString + " " + heterogeneousLink + " " + baseClassLink + ... + " (" + typeLinksStr + ") array with properties:" + newline; + end + end end function groups = getPropertyGroups(obj) diff --git a/matlab/test/arrow/tabular/tSchema.m b/matlab/test/arrow/tabular/tSchema.m index 4acb17b3a236..a33d5dade1fa 100644 --- a/matlab/test/arrow/tabular/tSchema.m +++ b/matlab/test/arrow/tabular/tSchema.m @@ -485,8 +485,8 @@ function TestIsEqualTrue(testCase) ]); % Create a Schema with zero fields - schema3 = arrow.recordBatch(table).Schema; - schema4 = arrow.recordBatch(table).Schema; + schema3 = arrow.recordBatch(table()).Schema; + schema4 = arrow.recordBatch(table()).Schema; testCase.verifyTrue(isequal(schema1, schema2)); testCase.verifyTrue(isequal(schema3, schema4)); @@ -514,7 +514,7 @@ function TestIsEqualFalse(testCase) ]); % Create a Schema with zero fields - schema5 = arrow.recordBatch(table).Schema; + schema5 = arrow.recordBatch(table()).Schema; % Have different number of fields testCase.verifyFalse(isequal(schema1, schema2));