diff --git a/matlab/src/cpp/arrow/matlab/tabular/proxy/schema.cc b/matlab/src/cpp/arrow/matlab/tabular/proxy/schema.cc index ec1ac1eecb2fd..023381e005969 100644 --- a/matlab/src/cpp/arrow/matlab/tabular/proxy/schema.cc +++ b/matlab/src/cpp/arrow/matlab/tabular/proxy/schema.cc @@ -34,7 +34,6 @@ namespace arrow::matlab::tabular::proxy { REGISTER_METHOD(Schema, getFieldByName); REGISTER_METHOD(Schema, getNumFields); REGISTER_METHOD(Schema, getFieldNames); - REGISTER_METHOD(Schema, toString); } libmexclass::proxy::MakeResult Schema::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { @@ -141,14 +140,4 @@ namespace arrow::matlab::tabular::proxy { context.outputs[0] = field_names_mda; } - void Schema::toString(libmexclass::proxy::method::Context& context) { - namespace mda = ::matlab::data; - mda::ArrayFactory factory; - - const auto str_utf8 = schema->ToString(); - MATLAB_ASSIGN_OR_ERROR_WITH_CONTEXT(const auto str_utf16, arrow::util::UTF8StringToUTF16(str_utf8), context, error::UNICODE_CONVERSION_ERROR_ID); - auto str_mda = factory.createScalar(str_utf16); - context.outputs[0] = str_mda; - } - } diff --git a/matlab/src/cpp/arrow/matlab/tabular/proxy/schema.h b/matlab/src/cpp/arrow/matlab/tabular/proxy/schema.h index 30883bc2a85ac..9ca4a94e53071 100644 --- a/matlab/src/cpp/arrow/matlab/tabular/proxy/schema.h +++ b/matlab/src/cpp/arrow/matlab/tabular/proxy/schema.h @@ -39,7 +39,6 @@ namespace arrow::matlab::tabular::proxy { void getFieldByName(libmexclass::proxy::method::Context& context); void getNumFields(libmexclass::proxy::method::Context& context); void getFieldNames(libmexclass::proxy::method::Context& context); - void toString(libmexclass::proxy::method::Context& context); std::shared_ptr schema; }; diff --git a/matlab/src/matlab/+arrow/+tabular/+internal/displaySchema.m b/matlab/src/matlab/+arrow/+tabular/+internal/displaySchema.m new file mode 100644 index 0000000000000..8d6740b195abc --- /dev/null +++ b/matlab/src/matlab/+arrow/+tabular/+internal/displaySchema.m @@ -0,0 +1,50 @@ +%DISPLAYSCHEMA Generates arrow.tabular.Schema display text. + +% 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 text = displaySchema(schema) + fields = schema.Fields; + names = [fields.Name]; + types = [fields.Type]; + typeIDs = string([types.ID]); + + % Use as the sentinel for field names with zero characters. + 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 + + text = names + ": " + typeIDs; + text = " " + strjoin(text, " | "); +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 f679b1e0bc22c..3ee40f0e14293 100644 --- a/matlab/src/matlab/+arrow/+tabular/Schema.m +++ b/matlab/src/matlab/+arrow/+tabular/Schema.m @@ -97,18 +97,29 @@ end end - methods (Access = private) + methods (Access=protected) - function str = toString(obj) - str = obj.Proxy.toString(); + function header = getHeader(obj) + name = matlab.mixin.CustomDisplay.getClassNameForHeader(obj); + numFields = obj.NumFields; + if numFields == 0 + header = compose(" Arrow %s with 0 fields" + newline, name); + elseif numFields == 1 + header = compose(" Arrow %s with %d field:" + newline, name, numFields); + else + header = compose(" Arrow %s with %d fields:" + newline, name, numFields); + end end - end + function displayScalarObject(obj) + disp(getHeader(obj)); + numFields = obj.NumFields; - methods (Access=protected) + if numFields > 0 + text = arrow.tabular.internal.displaySchema(obj); + disp(text + newline); + end - function displayScalarObject(obj) - disp(obj.toString()); end end diff --git a/matlab/test/arrow/tabular/tSchema.m b/matlab/test/arrow/tabular/tSchema.m index e4c706d9a3d6c..bb95c1823b9fc 100644 --- a/matlab/test/arrow/tabular/tSchema.m +++ b/matlab/test/arrow/tabular/tSchema.m @@ -526,7 +526,70 @@ function TestIsEqualFalse(testCase) % Compare schema to double testCase.verifyFalse(isequal(schema4, 5)); + end + + function TestDisplaySchemaZeroFields(testCase) + import arrow.internal.test.display.makeLinkString + + schema = arrow.schema(arrow.type.Field.empty(0, 0)); %#ok + classnameLink = makeLinkString(FullClassName="arrow.tabular.Schema",... + ClassName="Schema", BoldFont=true); + expectedDisplay = " Arrow " + classnameLink + " with 0 fields" + newline; + expectedDisplay = char(expectedDisplay + newline); + actualDisplay = evalc('disp(schema)'); + testCase.verifyEqual(actualDisplay, char(expectedDisplay)); + end + + function TestDisplaySchemaOneField(testCase) + import arrow.internal.test.display.makeLinkString + + schema = arrow.schema(arrow.field("TestField", arrow.boolean())); %#ok + classnameLink = makeLinkString(FullClassName="arrow.tabular.Schema",... + ClassName="Schema", BoldFont=true); + header = " Arrow " + classnameLink + " with 1 field:" + newline; + indent = " "; + + if usejava("desktop") + type = makeLinkString(FullClassName="arrow.type.BooleanType", ... + ClassName="Boolean", BoldFont=true); + name = "TestField: "; + fieldLine = indent + name + type + newline; + else + fieldLine = indent + "TestField: Boolean" + newline; + end + expectedDisplay = join([header, fieldLine], newline); + expectedDisplay = char(expectedDisplay + newline); + actualDisplay = evalc('disp(schema)'); + testCase.verifyEqual(actualDisplay, char(expectedDisplay)); + end + function TestDisplaySchemaField(testCase) + import arrow.internal.test.display.makeLinkString + + field1 = arrow.field("Field1", arrow.timestamp()); + field2 = arrow.field("Field2", arrow.string()); + schema = arrow.schema([field1, field2]); %#ok + classnameLink = makeLinkString(FullClassName="arrow.tabular.Schema",... + ClassName="Schema", BoldFont=true); + header = " Arrow " + classnameLink + " with 2 fields:" + newline; + + indent = " "; + if usejava("desktop") + type1 = makeLinkString(FullClassName="arrow.type.TimestampType", ... + ClassName="Timestamp", BoldFont=true); + field1String = "Field1: " + type1; + type2 = makeLinkString(FullClassName="arrow.type.StringType", ... + ClassName="String", BoldFont=true); + field2String = "Field2: " + type2; + fieldLine = indent + field1String + " | " + field2String + newline; + else + fieldLine = indent + "Field1: Timestamp | Field2: String" + newline; + end + + expectedDisplay = join([header, fieldLine], newline); + expectedDisplay = char(expectedDisplay + newline); + actualDisplay = evalc('disp(schema)'); + testCase.verifyEqual(actualDisplay, char(expectedDisplay)); end end