From e85d62a158845d1a60e076a64d7e16e68b995e52 Mon Sep 17 00:00:00 2001 From: Michael Innerberger <michael.innerberger@asc.tuwien.ac.at> Date: Wed, 9 Aug 2023 11:59:35 -0400 Subject: [PATCH] Finish refactoring type system --- lib/storage/@LevelData/LevelData.m | 2 +- lib/storage/@LevelData/set.m | 7 +- lib/storage/@LevelData/setAbsolute.m | 7 +- lib/storage/@LevelData/setTime.m | 7 +- .../LevelDataCollection.m | 4 +- lib/storage/Type/ValueDetails.m | 135 ++++++------------ lib/storage/Type/splitIntoLevelWiseData.m | 64 +++++++++ 7 files changed, 122 insertions(+), 104 deletions(-) create mode 100644 lib/storage/Type/splitIntoLevelWiseData.m diff --git a/lib/storage/@LevelData/LevelData.m b/lib/storage/@LevelData/LevelData.m index bd02419..dcfae08 100644 --- a/lib/storage/@LevelData/LevelData.m +++ b/lib/storage/@LevelData/LevelData.m @@ -295,7 +295,7 @@ classdef LevelData < handle separator = '\n'; end t = obj.type.(obj.scalarVariable{j}); - spec = [spec, '%', obj.getWidth(t), t.rawType.formatSpec, separator]; + spec = [spec, '%', obj.getWidth(t), t.formatSpec, separator]; end end diff --git a/lib/storage/@LevelData/set.m b/lib/storage/@LevelData/set.m index 7a8e287..07a284d 100644 --- a/lib/storage/@LevelData/set.m +++ b/lib/storage/@LevelData/set.m @@ -43,18 +43,17 @@ function set(obj, jLevel, variableName, value) name = variableName{j}; val = value{j}; % Determine type of data - if isa(val, 'Type') + if isa(val, 'ValueDetails') % Copy type argument currentType = val; valueList = repmat({nan}, length(jLevel), 1); else % Determine type by example data - [currentType, valueList] = ... - ValueDetails.determineTypeValue(length(jLevel), name, val); + [valueList, currentType] = splitIntoLevelWiseData(length(jLevel), val); end % Store type if ~ismember(name, obj.label) - obj.type.(name) = currentType; + obj.type.(name) = currentType.adaptPrintWidthTo(name); end % Store level-oriented data for k = 1:length(jLevel) diff --git a/lib/storage/@LevelData/setAbsolute.m b/lib/storage/@LevelData/setAbsolute.m index 0466416..0c3f750 100644 --- a/lib/storage/@LevelData/setAbsolute.m +++ b/lib/storage/@LevelData/setAbsolute.m @@ -46,19 +46,18 @@ function setAbsolute(obj, jLevel, variableName, value) name = variableName{j}; val = value{j}; % Determine type of data - if isa(val, 'Type') + if isa(val, 'ValueDetails') % Copy type argument currentType = val; valueList = repmat({nan}, length(jLevel), 1); else % Determine type by example data - [currentType, valueList] = ... - ValueDetails.determineTypeValue(length(jLevel), name, val); + [valueList, currentType] = splitIntoLevelWiseData(length(jLevel), val); end if ~ismember(name, obj.absoluteVariable) % Store type - obj.type.(name) = currentType; + obj.type.(name) = currentType.adaptPrintWidthTo(name); % Store variable as absolute variable obj.absoluteVariable{end+1} = name; end diff --git a/lib/storage/@LevelData/setTime.m b/lib/storage/@LevelData/setTime.m index a908568..f61a6a8 100644 --- a/lib/storage/@LevelData/setTime.m +++ b/lib/storage/@LevelData/setTime.m @@ -46,18 +46,17 @@ function setTime(obj, jLevel, variableName, value) name = variableName{j}; val = value{j}; % Determine type of data - if isa(val, 'Type') + if isa(val, 'ValueDetails') % Copy type argument currentType = val; valueList = repmat({nan}, length(jLevel), 1); else % Determine type by example data - [currentType, valueList] = ... - ValueDetails.determineTypeValue(length(jLevel), name, val); + [valueList, currentType] = splitIntoLevelWiseData(length(jLevel), val); end % Store type if ~ismember(name, obj.label) - obj.type.(name) = currentType; + obj.type.(name) = currentType.adaptPrintWidthTo(name); end % Store variable as absolute variable if ~ismember(name, obj.timeVariable) diff --git a/lib/storage/@LevelDataCollection/LevelDataCollection.m b/lib/storage/@LevelDataCollection/LevelDataCollection.m index d0d56d5..b39eb8c 100644 --- a/lib/storage/@LevelDataCollection/LevelDataCollection.m +++ b/lib/storage/@LevelDataCollection/LevelDataCollection.m @@ -144,9 +144,9 @@ classdef LevelDataCollection < handle for j = 1:obj.nTimeVariable t = obj.item{1}.type.(obj.timeVariable{j}); if j < obj.nTimeVariable - spec = [spec, '%', obj.getWidth(t), t.rawType.formatSpec, obj.separator]; + spec = [spec, '%', obj.getWidth(t), t.formatSpec, obj.separator]; else - spec = [spec, '%', obj.getWidth(t), t.rawType.formatSpec, '\n']; + spec = [spec, '%', obj.getWidth(t), t.formatSpec, '\n']; end end if obj.nTimeVariable == 0 diff --git a/lib/storage/Type/ValueDetails.m b/lib/storage/Type/ValueDetails.m index 6dc7b66..e82680e 100644 --- a/lib/storage/Type/ValueDetails.m +++ b/lib/storage/Type/ValueDetails.m @@ -29,122 +29,79 @@ classdef ValueDetails end properties (Dependent) + % Format specification for printing + formatSpec % Boolean determining if type is scalar isScalar (1,1) boolean end methods %% CONSTRUCTOR - function obj = ValueDetails(name, value) - %%TYPE creates an object of the Type class given an example + function obj = ValueDetails(rawType, shape, printWidth) + %%VALUEDETAILS creates an instance of ValueDetails given an example %value of the corresponding data - % obj = TYPE(name, value) + % obj = VALUEDETAILS(rawType, shape, printWidth) + + obj.rawType = rawType; + obj.shape = shape; + obj.printWidth = printWidth; + end + + function obj = adaptPrintWidthTo(obj, name) + %ADAPTPRINTWIDTHTO updates the print width considering the length of + %variable name + obj.printWidth = max(obj.printWidth, length(name)); + end + + function formatSpec = get.formatSpec(obj) + formatSpec = obj.rawType.formatSpec; + end + + function bool = get.isScalar(obj) + bool = strcmp(obj.shape, 's'); + end + end + + % AUXILIARY + methods (Static) + function details = fromExample(value) + %%FROMEXAMPLE creates an instance of ValueDetails given an example + %value of the corresponding data + % obj = FROMEXAMPLE(value) % Determine data type and width if isnan(value) % NaN is treated as integer to avoid problems with plotting - obj.rawType = RawType.INT; - obj.printWidth = 3; + rawType = RawType.INT; + printWidth = 3; elseif isinteger(value) - obj.rawType = RawType.INT; + rawType = RawType.INT; S = cellstr(num2str(value)); - obj.printWidth = max(cellfun(@(x) length(x), S(:))); + printWidth = max(cellfun(@(x) length(x), S(:))); elseif isfloat(value) - obj.rawType = RawType.FLOAT; + rawType = RawType.FLOAT; floatPrecision = str2double(RawType.FLOAT.formatSpec(2)); - obj.printWidth = 6 + floatPrecision; + printWidth = 6 + floatPrecision; elseif ischar(value) - obj.rawType = RawType.TEXT; - obj.printWidth = length(value); + rawType = RawType.TEXT; + printWidth = length(value); elseif isstring(value) - obj.rawType = RawType.TEXT; - obj.printWidth = max(cellfun(@(x) length(x), value(:))); + rawType = RawType.TEXT; + printWidth = max(cellfun(@(x) length(x), value(:))); else error('Invalid data type'); end % Determine shape if isscalar(value) - obj.shape = 's'; + shape = 's'; elseif isvector(value) - obj.shape = 'v'; + shape = 'v'; else - obj.shape = 'a'; + shape = 'a'; end - % Update width considering length of the name - obj.printWidth = max(obj.printWidth, length(name)); - end - - function bool = get.isScalar(obj) - bool = strcmp(obj.shape, 's'); - end - end - - % AUXILIARY - methods (Static) - function [type, valueList] = determineTypeValue(nLevel, variableName, value) - %%DETERMINETYPEVALUE auxiliary function for determining type from an array - %of values - % [type, valueList] = DETERMINETYPEVALUE(nLevel, variableName, value) - - % Copyright 2023 Philipp Bringmann - % - % This program is free software: you can redistribute it and/or modify - % it under the terms of the GNU General Public License as published by - % the Free Software Foundation, either version 3 of the License, or - % (at your option) any later version. - % - % This program is distributed in the hope that it will be useful, - % but WITHOUT ANY WARRANTY; without even the implied warranty of - % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - % GNU General Public License for more details. - % - % You should have received a copy of the GNU General Public License - % along with this program. If not, see <http://www.gnu.org/licenses/>. - % - - - if nLevel == 1 - % Array value represents data on a single level - type = ValueDetails(variableName, value); - valueList = {value}; - else - if isvector(value) - % Array value represents a vector of scalar values for each - % level - assert(length(value) == nLevel, ... - 'Length of argument invalid'); - type = ValueDetails(variableName, value(1)); - valueList = mat2cell(value(:), ones(nLevel, 1)); - else - % Array value represents an higher dimensional objects for each - % level - dims = size(value); - if any(dims == nLevel) - levelDim = find(dims == nLevel); - remainingDim = 1:ndims(value); - remainingDim(levelDim) = []; - if sum(dims == nLevel) > 1 - % Several dimensions could represent the level - % information - warning(['Unclear dimensions of argument. ', ... - 'Assume level-oriented data in dimension ', ... - num2str(levelDim)]); - end - % Extract and rearrange values - value = permute(value, [levelDim, remainingDim]); - valueList = cell(nLevel, 1); - ind = repmat({':'}, length(remainingDim), 1); - type = ValueDetails(variableName, value(1, ind{:})); - for k = 1:nLevel - valueList{k} = value(k, ind{:}); - end - else - error('Unable to extract data from argument'); - end - end - end + details = ValueDetails(rawType, shape, printWidth); end end end diff --git a/lib/storage/Type/splitIntoLevelWiseData.m b/lib/storage/Type/splitIntoLevelWiseData.m new file mode 100644 index 0000000..d399959 --- /dev/null +++ b/lib/storage/Type/splitIntoLevelWiseData.m @@ -0,0 +1,64 @@ + +function [valueList, type] = splitIntoLevelWiseData(nLevel, value) +%%DETERMINETYPEVALUE auxiliary function for determining type from an array +%of values +% [type, valueList] = DETERMINETYPEVALUE(nLevel, variableName, value) + +% Copyright 2023 Philipp Bringmann +% +% This program is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program. If not, see <http://www.gnu.org/licenses/>. +% + + if nLevel == 1 + % Array value represents data on a single level + type = ValueDetails.fromExample(value); + valueList = {value}; + else + if isvector(value) + % Array value represents a vector of scalar values for each % level + assert(length(value) == nLevel, 'Length of argument invalid'); + type = ValueDetails.fromExample(value(1)); + valueList = mat2cell(value(:), ones(nLevel, 1)); + else + % Array value represents an higher dimensional objects for each % level + dims = size(value); + [levelDim, idx] = extractSingleLevelDimension(dims, nLevel); + % Extract values + valueList = cell(nLevel, 1); + for k = 1:nLevel + idx(levelDim) = k; + valueList{k} = subsref(value, idx); + end + type = ValueDetails.fromExample(valueList{end}); + end + end +end + +function [levelDim, idx] = extractSingleLevelDimension(dims, nLevel) + levelDim = find(dims == nLevel); + if isempty(levelDim) + error('Unable to extract data from argument'); + end + + if length(levelDim) > 1 + % Several dimensions could represent the level information + levelDim = levelDim(1); + warning('Unclear dimensions of argument. Assume level-oriented data in dimension %d.', levelDim); + end + + % index that is ':' for all dimensions, and where the index in the level + % dimension can be changed + idx.subs = repmat({':'}, 1, ndims(value)); + idx.type = '()'; +end \ No newline at end of file -- GitLab