From 3d1e8db66dfc5eeff2bd52e025b44fd06eb60c7c Mon Sep 17 00:00:00 2001
From: Michael Innerberger <michael.innerberger@asc.tuwien.ac.at>
Date: Tue, 8 Aug 2023 14:09:55 -0400
Subject: [PATCH] Use argument blocks in get/set methods

---
 lib/storage/@LevelData/LevelData.m   | 16 +++---
 lib/storage/@LevelData/get.m         | 78 ++++++++++++++++------------
 lib/storage/@LevelData/set.m         | 44 ++++++++--------
 lib/storage/@LevelData/setAbsolute.m | 55 ++++++++++----------
 lib/storage/@LevelData/setTime.m     | 49 +++++++++--------
 5 files changed, 131 insertions(+), 111 deletions(-)

diff --git a/lib/storage/@LevelData/LevelData.m b/lib/storage/@LevelData/LevelData.m
index e02a8c0..5b6d3f3 100644
--- a/lib/storage/@LevelData/LevelData.m
+++ b/lib/storage/@LevelData/LevelData.m
@@ -18,9 +18,10 @@ classdef LevelData < handle
 %
 
     properties
+        % map for general metadata
         metaData
         % Root folder for file storage
-        root = 'results'
+        root
         % Structure array storing string representations of variables
         dictionary
         % Cell array of level data
@@ -79,10 +80,10 @@ classdef LevelData < handle
             %file storage is initialised in the specified optional path
             %   leveldata = LEVELDATA(rootpath)
 
-            % Set root path for file storage
-            if nargin >= 1
-                obj.root = rootpath;
+            arguments
+                rootpath {mustBeTextScalar} = 'results'
             end
+            obj.root = rootpath;
 
             % TODO: set output of hostname to string and remove cast
             obj.metaData = dictionary(...
@@ -106,7 +107,10 @@ classdef LevelData < handle
 
         %% SET GLOBAL VARIABLES
         function set.root(obj, path)
-            assert(ischar(path), 'Insert character array as path name.');
+            arguments
+                obj
+                path {mustBeTextScalar}
+            end
             obj.root = path;
         end
 
@@ -221,7 +225,7 @@ classdef LevelData < handle
         end
 
         %% READ LEVEL DATA
-        data = get(obj, jLevel, varargin)
+        data = get(obj, jLevel, variableName)
 
         %% MODIFY LEVEL DATA
         set(obj, jLevel, varargin)
diff --git a/lib/storage/@LevelData/get.m b/lib/storage/@LevelData/get.m
index 12eac2f..d801d43 100644
--- a/lib/storage/@LevelData/get.m
+++ b/lib/storage/@LevelData/get.m
@@ -1,9 +1,9 @@
-function data = get(obj, jLevel, varargin)
+function data = get(obj, jLevel, variableName)
 %%GET extracts data from this LevelData object on a given list jLevel of 
-%level numbers, the cell array varargin must contain the names of the
-%variables to return
-%   data = GET(obj, jLevel, varargin)
-%   data = GET(obj, ':', varargin)
+%level numbers. One or more variables to be returned can be specified by their
+%names.
+%   data = GET(obj, jLevel, variableName, ...)
+%   data = GET(obj, ':', variableName, ...)
 
 % Copyright 2023 Philipp Bringmann
 %
@@ -21,48 +21,58 @@ function data = get(obj, jLevel, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
+    arguments
+        obj
+        jLevel {mustBeIndexVector} = ':'
+    end
+
+    arguments (Repeating)
+        variableName {mustBeTextScalar}
+    end
 
-    % Proceed input
     if strcmp(jLevel, ':')
         jLevel = 1:obj.nLevel;
     end
 
     % Initialise return variable
-    data = nan(length(jLevel), length(varargin));
+    data = nan(length(jLevel), length(variableName));
     containsCharOnly = true;
 
+    % filter non-existing variables
+    existingVariables = ismember(variableName, obj.label);
+    if any(~existingVariables)
+        warning(['Variable(s) ', strjoin(variableName(~existingVariables), ', '), ' not found']);
+    end
+    variableName = variableName(existingVariables);
+
     % Iterate over all variables
-    for jVariable = 1:length(varargin)
-        variableName = varargin{jVariable};
-        % Check for existence of specified variable
-        if ~ismember(variableName, obj.label)
-            warning(['Variable ', variableName, ' not found']);
-            value = [];
-        else
-            % Check for strings/character arrays
-            if ~strcmp(obj.type.(variableName).type, 'c') && ...
-                    ~strcmp(obj.type.(variableName).type, 's')
-                containsCharOnly = false;
+    for jVariable = 1:length(variableName)
+        name = variableName{jVariable};
+
+        % Check for strings/character arrays
+        if ~strcmp(obj.type.(name).type, 'c') && ...
+                ~strcmp(obj.type.(name).type, 's')
+            containsCharOnly = false;
+        end
+        % Determine index of current variable
+        idx = obj.getIndex(name);
+        if obj.isScalar(idx)
+            % Extract scalar variables for each level
+            value = zeros(length(jLevel), 1);
+            for k = 1:length(jLevel)
+                value(k) = obj.level(jLevel(k)).(name);
             end
-            % Determine index of current variable
-            idx = obj.getIndex(variableName);
-            if obj.isScalar(idx)
-                % Extract scalar variables for each level
-                value = zeros(length(jLevel), 1);
-                for k = 1:length(jLevel)
-                    value(k) = obj.level(jLevel(k)).(variableName);
-                end
+        else
+            % Extract non-scalar variables only in case of a single level
+            if length(jLevel) > 1
+                warning('Export of level-oriented scalar variables only');
+                value = [];
             else
-                % Extract non-scalar variables only in case of a single level
-                if length(jLevel) > 1
-                    warning('Export of level-oriented scalar variables only');
-                    value = [];
-                else
-                    data(1,jVariable) = obj.level(jLevel).(variableName);
-                    return
-                end
+                data(1,jVariable) = obj.level(jLevel).(name);
+                return
             end
         end
+
         % Save extracted data to return variable
         data(:,jVariable) = value;
         % Post-process character arrays
diff --git a/lib/storage/@LevelData/set.m b/lib/storage/@LevelData/set.m
index aeca5ad..8aa3efa 100644
--- a/lib/storage/@LevelData/set.m
+++ b/lib/storage/@LevelData/set.m
@@ -1,10 +1,9 @@
-function set(obj, jLevel, varargin)
+function set(obj, jLevel, variableName, value)
 %%SET stores specified data to this LevelData object for the specified list
-%jLevel of levels, the cell array varargin must contain pairs of variable
-%names and data (numeric or string/characters), dimension one of data must
-%correspond to number of levels
-%   SET(obj, jLevel, varargin)
-%   SET(obj, ':', varargin)
+%jLevel of levels. The variableName/value pairs can be repeating, their first
+%dimension must correspond to number of levels
+%   SET(obj, jLevel, variableName, value, ...)
+%   SET(obj, ':', variableName, value, ...)
 
 % Copyright 2023 Philipp Bringmann
 %
@@ -22,41 +21,44 @@ function set(obj, jLevel, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
+    arguments
+        obj
+        jLevel {mustBeIndexVector} = ':'
+    end
+
+    arguments (Repeating)
+        variableName {mustBeTextScalar}
+        value
+    end
 
-    % Proceed input
     if strcmp(jLevel, ':')
         jLevel = 1:obj.nLevel;
     end
 
-    % Check number of variable input
-    assert(mod(length(varargin), 2) == 0, ...
-           'Invalid number of arguments');
-
     % Determine number of arguments
-    nArgument = length(varargin) / 2;
+    nArgument = length(variableName);
 
     % Store data to LevelData object
     for j = 1:nArgument
-        variableName = varargin{2*j-1};
+        name = variableName{j};
+        val = value{j};
         % Determine type of data
-        if isa(varargin{2*j}, 'Type')
+        if isa(val, 'Type')
             % Copy type argument
-            currentType = varargin{2*j};
+            currentType = val;
             valueList = repmat({nan}, length(jLevel), 1);
         else
             % Determine type by example data
             [currentType, valueList] = ...
-                Type.determineTypeValue(length(jLevel), ...
-                                        variableName, ...
-                                        varargin{2*j});
+                Type.determineTypeValue(length(jLevel), name, val);
         end
         % Store type
-        if ~ismember(variableName, obj.label)
-            obj.type.(variableName) = currentType;
+        if ~ismember(name, obj.label)
+            obj.type.(name) = currentType;
         end
         % Store level-oriented data
         for k = 1:length(jLevel)
-            obj.level(jLevel(k)).(variableName) = valueList{k};
+            obj.level(jLevel(k)).(name) = valueList{k};
         end
     end
 end
\ No newline at end of file
diff --git a/lib/storage/@LevelData/setAbsolute.m b/lib/storage/@LevelData/setAbsolute.m
index 080ceef..3eeba6a 100644
--- a/lib/storage/@LevelData/setAbsolute.m
+++ b/lib/storage/@LevelData/setAbsolute.m
@@ -1,11 +1,10 @@
-function setAbsolute(obj, jLevel, varargin)
+function setAbsolute(obj, jLevel, variableName, value)
 %%SETABSOLUTE stores specified data of absolute-niveau type (constants / 
-%eigenvalues / efficiency indices / ...) to this
-%LevelData object for the specified list jLevel of levels, the cell array
-%varargin must contain pairs of variable names and data, dimension one of
-%data must correspond to number of levels
-%   SETABSOLUTE(obj, jLevel, varargin)
-%   SETABSOLUTE(obj, ':', varargin)
+%eigenvalues / efficiency indices / ...) to this LevelData object for the
+%specified list jLevel of levels, repeated pairs of variable names and values,
+%first dimension of data must correspond to number of levels
+%   SETABSOLUTE(obj, jLevel, variableName, value, ...)
+%   SETABSOLUTE(obj, ':', variableName, value, ...)
 %
 %   See also LevelData/set
 
@@ -25,45 +24,47 @@ function setAbsolute(obj, jLevel, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
+    arguments
+        obj
+        jLevel {mustBeIndexVector} = ':'
+    end
+
+    arguments (Repeating)
+        variableName {mustBeTextScalar}
+        value
+    end
 
-    % Proceed input
     if strcmp(jLevel, ':')
         jLevel = 1:obj.nLevel;
     end
 
-    % Check number of variable input
-    assert(mod(length(varargin), 2) == 0, ...
-           'Invalid number of arguments');
-
     % Determine number of arguments
-    nArgument = length(varargin) / 2;
+    nArgument = length(variableName);
 
     % Store data to LevelData object
     for j = 1:nArgument
-        variableName = varargin{2*j-1};
+        name = variableName{j};
+        val = value{j};
         % Determine type of data
-        if isa(varargin{2*j}, 'Type')
+        if isa(val, 'Type')
             % Copy type argument
-            currentType = varargin{2*j};
+            currentType = val;
             valueList = repmat({nan}, length(jLevel), 1);
         else
             % Determine type by example data
             [currentType, valueList] = ...
-                Type.determineTypeValue(length(jLevel), ...
-                                        variableName, ...
-                                        varargin{2*j});
-        end
-        % Store type
-        if ~ismember(variableName, obj.absoluteVariable)
-            obj.type.(variableName) = currentType;
+                Type.determineTypeValue(length(jLevel), name, val);
         end
-        % Store variable as absolute variable
-        if ~ismember(variableName, obj.absoluteVariable)
-            obj.absoluteVariable{end+1} = variableName;
+
+        if ~ismember(name, obj.absoluteVariable)
+            % Store type
+            obj.type.(name) = currentType;
+            % Store variable as absolute variable
+            obj.absoluteVariable{end+1} = name;
         end
         % Store level-oriented data
         for k = 1:length(jLevel)
-            obj.level(jLevel(k)).(variableName) = valueList{k};
+            obj.level(jLevel(k)).(name) = valueList{k};
         end
     end
 end
\ No newline at end of file
diff --git a/lib/storage/@LevelData/setTime.m b/lib/storage/@LevelData/setTime.m
index 207c532..cc2c005 100644
--- a/lib/storage/@LevelData/setTime.m
+++ b/lib/storage/@LevelData/setTime.m
@@ -1,10 +1,10 @@
-function setTime(obj, jLevel, varargin)
+function setTime(obj, jLevel, variableName, value)
 %%SETTIME stores specified data of increasing type (time / ndof / ...) to 
-%this LevelData object for the specified list jLevel of levels, the cell
-%array varargin must contain pairs of variable names and data, dimension 
-%one of data must correspond to number of levels
-%   SETTIME(obj, jLevel, varargin)
-%   SETTIME(obj, ':', varargin)
+%this LevelData object for the specified list jLevel of levels, repeated pairs
+%of variable names and values, first dimension of data must correspond to number
+%of levels
+%   SETTIME(obj, jLevel, variableName, value, ...)
+%   SETTIME(obj, ':', variableName, value, ...)
 %
 %   See also LevelData/set
 
@@ -24,45 +24,48 @@ function setTime(obj, jLevel, varargin)
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
 %
 
+    arguments
+        obj
+        jLevel {mustBeIndexVector} = ':'
+    end
+
+    arguments (Repeating)
+        variableName {mustBeTextScalar}
+        value
+    end
 
-    % Proceed input
     if strcmp(jLevel, ':')
         jLevel = 1:obj.nLevel;
     end
 
-    % Check number of variable input
-    assert(mod(length(varargin), 2) == 0, ...
-           'Invalid number of arguments');
-
     % Determine number of arguments
-    nArgument = length(varargin) / 2;
+    nArgument = length(variableName);
 
     % Store data to LevelData object
     for j = 1:nArgument
-        variableName = varargin{2*j-1};
+        name = variableName{j};
+        val = value{j};
         % Determine type of data
-        if isa(varargin{2*j}, 'Type')
+        if isa(val, 'Type')
             % Copy type argument
-            currentType = varargin{2*j};
+            currentType = val;
             valueList = repmat({nan}, length(jLevel), 1);
         else
             % Determine type by example data
             [currentType, valueList] = ...
-                Type.determineTypeValue(length(jLevel), ...
-                                        variableName, ...
-                                        varargin{2*j});
+                Type.determineTypeValue(length(jLevel), name, val);
         end
         % Store type
-        if ~ismember(variableName, obj.label)
-            obj.type.(variableName) = currentType;
+        if ~ismember(name, obj.label)
+            obj.type.(name) = currentType;
         end
         % Store variable as absolute variable
-        if ~ismember(variableName, obj.timeVariable)
-            obj.timeVariable{end+1} = variableName;
+        if ~ismember(name, obj.timeVariable)
+            obj.timeVariable{end+1} = name;
         end
         % Store level-oriented data
         for k = 1:length(jLevel)
-            obj.level(jLevel(k)).(variableName) = valueList{k};
+            obj.level(jLevel(k)).(name) = valueList{k};
         end
     end
 end
\ No newline at end of file
-- 
GitLab