changeset 19298:f0dcb44826f6

Overhaul appdata family of functions (getappdata, isappdata, rmappdata, setappdata). * getappdata.m: Redo docstring. Match function variable names to documentation. Place input validation first and make error messages more specific. Add BIST tests. * isappdata.m: Redo docstring. Match function variable names to documentation. Make input validation more precise. Use try/catch block for performance improvement. Add BIST tests. * rmappdata.m: Redo docstring. Improve input validation. Remove one for loop through vectorization. Add BIST tests. * setappdata.m: Redo docstring. Make input validation more precise. Use try/catch block for 80% performance improvement. Add BIST tests.
author Rik <rik@octave.org>
date Sat, 18 Oct 2014 18:10:52 -0700
parents 281a36ad4907
children d0f02639f560
files scripts/miscellaneous/getappdata.m scripts/miscellaneous/isappdata.m scripts/miscellaneous/rmappdata.m scripts/miscellaneous/setappdata.m
diffstat 4 files changed, 193 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/miscellaneous/getappdata.m	Fri Oct 17 21:35:19 2014 -0700
+++ b/scripts/miscellaneous/getappdata.m	Sat Oct 18 18:10:52 2014 -0700
@@ -19,47 +19,84 @@
 ## -*- texinfo -*-
 ## @deftypefn  {Function File} {@var{value} =} getappdata (@var{h}, @var{name})
 ## @deftypefnx {Function File} {@var{appdata} =} getappdata (@var{h})
-## 
-## Return the @var{value} for named application data for the object(s) with
-## handle(s) @var{h}.
+## Return the @var{value} of the application data @var{name} for the graphics
+## object with handle @var{h}.
 ## 
-## @code{getappdata(@var{h})} returns a structure, @var{appdata}, whose fields
-## correspond to the appdata properties.
+## @var{h} may also be a vector of graphics handles.  If no second argument
+## @var{name} is given then @code{getappdata} returns a structure,
+## @var{appdata}, whose fields correspond to the appdata properties.
 ##
-## @seealso{setappdata, guidata, get, set, getpref, setpref}
+## @seealso{setappdata, isappdata, rmappdata, guidata, get, set, getpref, setpref}
 ## @end deftypefn
 
 ## Author: Ben Abbott <bpabbott@mac.com>
 ## Created: 2010-07-15
 
-function val = getappdata (h, name)
+function value = getappdata (h, name)
+
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  endif
 
-  if (all (ishandle (h)) && nargin == 2 && ischar (name))
+  if (! all (ishandle (h(:))))
+    error ("getappdata: H must be a scalar or vector of graphic handles");
+  endif 
+
+  if (nargin == 2)
+    if (! ischar (name))
+      error ("getappdata: NAME must be a string");
+    endif
+    
     ## FIXME: Is there a better way to handle non-existent appdata
     ##        and missing fields?
-    val = cell (numel (h), 1);
+    value = cell (numel (h), 1);
     appdata = struct ();
-    for nh = 1:numel (h)
+    for i = 1:numel (h)
       try
-        appdata = get (h(nh), "__appdata__");
+        appdata = get (h(i), "__appdata__");
       end_try_catch
-      if (! isfield (appdata, name))
-        appdata.(name) = [];
+      if (isfield (appdata, name))
+        value(i) = {appdata.(name)};
+      else
+        value(i) = {[]};
       endif
-      val(nh) = {appdata.(name)};
     endfor
-    if (nh == 1)
-      val = val{1};
+
+    if (i == 1)
+      value = value{1};
     endif
-  elseif (ishandle (h) && numel (h) == 1 && nargin == 1)
+
+  else  # nargin == 1
+    if (numel (h) != 1)
+      error ("getappdata: Only one handle H may be used when fetching appdata");
+    endif
     try
-      val = get (h, "__appdata__");
+      value = get (h, "__appdata__");
     catch
-      val = struct ();
+      value = struct ();
     end_try_catch
-  else
-    error ("getappdata: invalid input");
   endif
 
 endfunction
 
+
+%!test
+%! unwind_protect
+%!   setappdata (0, "%data1%", ones (3), "%data2%", "hello world");
+%!   assert (getappdata (0, "%data1%"), ones (3));
+%!   assert (getappdata (0, "%data2%"), "hello world");
+%!   appdata = getappdata (0);
+%!   name1 = "%data1%";  name2 = "%data2%";
+%!   assert (appdata.(name1), ones (3));
+%!   assert (appdata.(name2), "hello world");
+%! unwind_protect_cleanup
+%!   rmappdata (0, "%data1%", "%data2%");
+%! end_unwind_protect
+
+## Test input validation
+%!error getappdata ()
+%!error getappdata (1,2,3)
+%!error <H must be a scalar .* graphic handle> getappdata (-1, "hello")
+%!error <NAME must be a string> getappdata (0, 1)
+%!error <Only one handle H may be used when fetching appdata> getappdata ([0 0])
+
--- a/scripts/miscellaneous/isappdata.m	Fri Oct 17 21:35:19 2014 -0700
+++ b/scripts/miscellaneous/isappdata.m	Sat Oct 18 18:10:52 2014 -0700
@@ -17,35 +17,55 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {@var{V} =} isappdata (@var{h}, @var{name})
+## @deftypefn {Function File} {@var{valid} =} isappdata (@var{h}, @var{name})
 ## Return true if the named application data, @var{name}, exists for the
-## object with handle @var{h}.
-## @seealso{getappdata, setappdata, rmappdata}
+## graphics object with handle @var{h}.
+##
+## @var{h} may also be a vector of graphics handles.
+## @seealso{getappdata, setappdata, rmappdata, guidata, get, set, getpref, setpref}
 ## @end deftypefn
 
 ## Author: Ben Abbott <bpabbott@mac.com>
 ## Created: 2010-07-15
 
-function res = isappdata (h, name)
+function valid = isappdata (h, name)
 
-  if (! (all (ishandle (h)) && ischar (name)))
-    error ("isappdata: invalid input");
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
   endif
 
-  for nh = 1:numel (h)
-    data = get (h(nh));
-    if (isfield (data, "__appdata__") && isfield (data.__appdata__, name))
-      res(nh) = true;
-    else
-      res(nh) = false;
-    endif
+  if (! all (ishandle (h(:))))
+    error ("isappdata: H must be a scalar or vector of graphic handles");
+  elseif (! ischar (name))
+    error ("isappdata: NAME must be a string");
+  endif 
+
+  valid = false (size (h));
+  for i = 1:numel (h)
+    try
+      appdata = get (h(i), "__appdata__");
+      if (isfield (appdata, name))
+        valid(i) = true;
+      endif
+    end_try_catch
   endfor
 
 endfunction
 
 
 %!test
-%! setappdata (0, "hello", "world");
-%! assert (isappdata (0, "hello"), true);
-%! assert (isappdata (0, "foobar"), false);
+%! unwind_protect
+%!   setappdata (0, "%hello%", "world");
+%!   assert (isappdata (0, "%hello%"), true);
+%!   assert (isappdata ([0 0], "%hello%"), [true, true]);
+%!   assert (isappdata (0, "%foobar%"), false);
+%! unwind_protect_cleanup
+%!   rmappdata (0, "%hello%");
+%! end_unwind_protect
 
+## Test input validation
+%!error isappdata ()
+%!error isappdata (1,2,3)
+%!error <H must be a scalar .* graphic handle> isappdata (-1, "hello")
+%!error <NAME must be a string> isappdata (0, 1)
+
--- a/scripts/miscellaneous/rmappdata.m	Fri Oct 17 21:35:19 2014 -0700
+++ b/scripts/miscellaneous/rmappdata.m	Sat Oct 18 18:10:52 2014 -0700
@@ -17,9 +17,15 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {} rmappdata (@var{h}, @var{name})
-## Delete the named application data for the object(s) with
-## handle(s) @var{h}.
+## @deftypefn  {Function File} {} rmappdata (@var{h}, @var{name})
+## @deftypefnx {Function File} {} rmappdata (@var{h}, @var{name1}, @var{name2}, @dots{})
+## Delete the application data @var{name} from the graphics object with handle
+## @var{h}.
+##
+## @var{h} may also be a vector of graphics handles.  Multiple application data
+## names may be supplied to delete several properties at once.
+##
+## @seealso{setappdata, getappdata, isappdata}
 ## @end deftypefn
 
 ## Author: Ben Abbott <bpabbott@mac.com>
@@ -27,21 +33,28 @@
 
 function rmappdata (h, varargin)
 
-  if (! (all (ishandle (h)) && iscellstr (varargin)))
-    error ("rmappdata: invalid input");
+  if (nargin < 2)
+    print_usage ();
+  endif
+
+  h = h(:).';
+  if (! all (ishandle (h)))
+    error ("rmappdata: H must be a scalar or vector of graphic handles");
+  elseif (! iscellstr (varargin))
+    error ("rmappdata: NAME must be a string");
   endif
 
-  for nh = 1:numel (h)
-    if (isprop (h(nh), "__appdata__"))
-      appdata = get (h(nh), "__appdata__");
-      for v = 1:numel(varargin)
-        if (isfield (appdata, varargin{v}))
-          appdata = rmfield (appdata, varargin{v});
-        else
-          error ("rmappdata: appdata '%s' is not present")
-        endif
-      endfor
-      set (h(nh), "__appdata__", appdata);
+  for hg = h
+    if (isprop (hg, "__appdata__"))
+      appdata = get (hg, "__appdata__");
+      vld = isfield (appdata, varargin);
+      if (! all (vld))
+        ## FIXME: Should we bother to error out?  Or just silently continue?
+        missing = varargin{find (! vld, 1)};
+        error ("rmappdata: appdata '%s' is not present", missing);
+      endif 
+      appdata = rmfield (appdata, varargin);
+      set (hg, "__appdata__", appdata);
     endif
   endfor
 
@@ -49,14 +62,21 @@
 
 
 %!test
-%! setappdata (0, "hello", "world");
-%! rmappdata (0, "hello");
-%! assert (isappdata (0, "hello"), false);
+%! setappdata (0, "%hello%", "world");
+%! rmappdata (0, "%hello%");
+%! assert (isappdata (0, "%hello%"), false);
 
 %!test
-%! setappdata (0, "data1", rand (3));
-%! setappdata (0, "data2", {"hello", "world"});
-%! rmappdata (0, "data1", "data2");
-%! assert (isappdata (0, "data1"), false);
-%! assert (isappdata (0, "data2"), false);
+%! setappdata (0, "%data1%", ones (3));
+%! setappdata (0, "%data2%", {"hello", "world"});
+%! rmappdata (0, "%data1%", "%data2%");
+%! assert (isappdata (0, "%data1%"), false);
+%! assert (isappdata (0, "%data2%"), false);
 
+## Test input validation
+%!error rmappdata ()
+%!error rmappdata (1)
+%!error <H must be a scalar .* graphic handle> rmappdata (-1, "hello")
+%!error <NAME must be a string> rmappdata (0, 1)
+%!error <appdata 'foobar' is not present> rmappdata (0, "foobar")
+
--- a/scripts/miscellaneous/setappdata.m	Fri Oct 17 21:35:19 2014 -0700
+++ b/scripts/miscellaneous/setappdata.m	Sat Oct 18 18:10:52 2014 -0700
@@ -17,11 +17,16 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {} setappdata (@var{h}, @var{name}, @var{value})
-## Set the named application data to @var{value} for the object(s) with
-## handle(s) @var{h}.  If the application data with the specified name does
-## not exist, it is created.
-## @seealso{getappdata, guidata, get, set, getpref, setpref}
+## @deftypefn  {Function File} {} setappdata (@var{h}, @var{name}, @var{value})
+## @deftypefnx {Function File} {} setappdata (@var{h}, @var{name1}, @var{value1}, @var{name2}, @var{value3}, @dots{})
+## Set the application data @var{name} to @var{value} for the graphics object
+## with handle @var{h}.
+##
+## @var{h} may also be a vector of graphics handles.  If the application data
+## with the specified @var{name} does not exist, it is created.  Multiple
+## @var{name}/@var{value} pairs can be specified at a time.
+##
+## @seealso{getappdata, isappdata, rmappdata, guidata, get, set, getpref, setpref}
 ## @end deftypefn
 
 ## Author: Ben Abbott <bpabbott@mac.com>
@@ -29,35 +34,62 @@
 
 function setappdata (h, varargin)
 
-  if (! (all (ishandle (h)) && mod (numel (varargin), 2) == 0))
-    error ("setappdata: invalid input");
+  h = h(:).';
+  if (! all (ishandle (h)))
+    error ("setappdata: H must be a scalar or vector of graphic handles");
+  elseif (mod (numel (varargin), 2) != 0)
+    error ("setappdata: NAME/VALUE arguments must occur in pairs");
   endif
 
-  for nh = 1:numel (h)
-    if (! isfield (get (h(nh)), "__appdata__"))
-      addproperty ("__appdata__", h(nh), "any", struct ());
-    endif
-    appdata = get (h(nh), "__appdata__");
+  for hg = h
+    try
+      appdata = get (hg, "__appdata__");
+    catch
+      appdata = struct ();
+      addproperty ("__appdata__", hg, "any", appdata);
+    end_try_catch
+
     for narg = 1:2:numel (varargin)
-      if (iscellstr (varargin{narg}))
+      if (ischar (varargin{narg}))
+        appdata.(varargin{narg}) = varargin{narg+1};
+      elseif (iscellstr (varargin{narg}))
         ## Handle cell arrays like set() does.
-        set (h(nh), "__appdata__", appdata);
-        setappdata (h(nh), vertcat (varargin{narg}', varargin{narg+1}'){:});
-        appdata = get (h(nh), "__appdata__");
-      elseif (ischar (varargin{narg}))
-        appdata.(varargin{narg}) = varargin{narg+1};
+        set (hg, "__appdata__", appdata);
+        setappdata (hg, vertcat (varargin{narg}', varargin{narg+1}'){:});
+        appdata = get (hg, "__appdata__");
       else
-        error ("setappdata: invalid input");
+        error ("setappdata: NAME must be a string or cellstr");
       endif
     endfor
-    set (h(nh), "__appdata__", appdata);
+
+    set (hg, "__appdata__", appdata);
   endfor
 
 endfunction
 
 
 %!test
-%! setappdata (0, "hello", "world");
-%! assert (isappdata (0, "hello"), true);
-%! assert (getappdata (0, "hello"), "world");
+%! unwind_protect
+%!   setappdata (0, "%hello%", "world");
+%!   assert (isappdata (0, "%hello%"), true);
+%!   assert (getappdata (0, "%hello%"), "world");
+%! unwind_protect_cleanup
+%!   rmappdata (0, "%hello%");
+%! end_unwind_protect
 
+%!test
+%! unwind_protect
+%!   setappdata (0, "%data1%", ones (3), "%data2%", "hello world");
+%!   assert (getappdata (0, "%data1%"), ones (3));
+%!   assert (getappdata (0, "%data2%"), "hello world");
+%!   setappdata (0, "%data1%", zeros (3));
+%!   assert (getappdata (0, "%data1%"), zeros (3));
+%! unwind_protect_cleanup
+%!   rmappdata (0, "%data1%", "%data2%");
+%! end_unwind_protect
+
+## Test input validation
+%!error <H must be a scalar .* graphic handle> rmappdata (-1, "hello")
+%!error <NAME must be a string> setappdata (0, 1, 2)
+%!error <NAME/VALUE arguments must occur in pairs> setappdata (0, "1")
+