+## Copyright (C) 2009,2010,2011,2012 Philip Nienhuis <prnienhuis at users.sf.net>
+##
+## 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/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} [ @var{numarr}, @var{txtarr}, @var{lim} ] = parsecell (@var{rawarr})
+## @deftypefnx {Function File} [ @var{numarr}, @var{txtarr}, @var{lim} ] = parsecell (@var{rawarr}, @var{limits})
+##
+## Divide a heterogeneous 2D cell array into a 2D numeric array and a
+## 2D cell array containing only strings. Both returned arrays are
+## trimmed from empty outer rows and columns.
+## This function is particularly useful for parsing cell arrays returned
+## by functions reading spreadsheets (e.g., xlsread, odsread).
+##
+## Optional return argument @var{lim} contains two field with the outer
+## column and row numbers of @var{numarr} and @var{txtarr} in the
+## original array @var{rawarr}.
+## Optional input argument @var{limits} can either be the spreadsheet
+## data limits returned in the spreadsheet file pointer struct
+## (field xls.limits or ods.limits), or the file ptr struct itself.
+## If one of these is specified, optional return argument @var{lim}
+## will contain the real spreadsheet row & column numbers enclosing
+## the origins of the numerical and text data returned in @var{numarr}
+## and @var{txtarr}.
+##
+## Examples:
+##
+## @example
+## [An, Tn] = parsecell (Rn);
+## (which returns the numeric contents of Rn into array An and the
+## text data into array Tn)
+## @end example
+##
+## @example
+## [An, Tn, lims] = parsecell (Rn, xls.limits);
+## (which returns the numeric contents of Rn into array An and the
+## text data into array Tn.)
+## @end example
+##
+## @seealso {xlsread, odsread, xls2oct, ods2oct}
+##
+## @end deftypefn
+
+## Author: Philip Nienhuis
+## Created: 2009-12-13
+## Updates:
+## 2009-12-29
+## 2010-08-25 Added option for second argument to be a file ptr
+## 2010-10-15 Simplified code for numerical array
+## 2011-05-17 Fixed subscript indexing bug in cropping section when rawarr is
+## " numeric scalar
+## 2011-09-08 Copyright string updated
+## 2012-01-26 Fixed "seealso" help string
+
+function [ numarr, txtarr, lim ] = parsecell (rawarr, arg2=[])
+
+ if (isstruct (arg2))
+ # Assume a file ptr has been supplied
+ if (isfield (arg2, 'limits'))
+ rawlimits = arg2.limits;
+ else
+ warning ("Invalid file ptr supplied to parsecell() - limits ignored.");
+ endif
+ else
+ rawlimits = arg2;
+ endif
+
+ lim = struct ( "numlimits", [], "txtlimits", []);
+
+ numarr = [];
+ txtarr = {};
+
+ if (~isempty (rawarr))
+ # Valid data returned. Divide into numeric & text arrays
+ no_txt = 0; no_num = 0;
+ if (isnumeric ([rawarr{:}]))
+ numarr = num2cell (rawarr);
+ no_txt = 1;
+ elseif (iscellstr (rawarr))
+ txtarr = cellstr (rawarr);
+ no_num = 1;
+ endif
+ # Prepare parsing
+ [nrows, ncols] = size (rawarr);
+
+ # Find text entries in raw data cell array
+ txtptr = cellfun ('isclass', rawarr, 'char');
+ if (~no_txt)
+ # Prepare text array. Create placeholder for text cells
+ txtarr = cell (size (rawarr));
+ txtarr(:) = {''};
+ if (any (any (txtptr)))
+ # Copy any text cells found into place holder
+ txtarr(txtptr) = rawarr(txtptr);
+ # Clean up text array (find leading / trailing empty
+ # rows & columns)
+ irowt = 1;
+ while (~any (txtptr(irowt, :))) irowt++; endwhile
+ irowb = nrows;
+ while (~any (txtptr(irowb, :))) irowb--; endwhile
+ icoll = 1;
+ while (~any (txtptr(:, icoll))) icoll++; endwhile
+ icolr = ncols;
+ while (~any (txtptr(:, icolr))) icolr--; endwhile
+ # Crop textarray
+ txtarr = txtarr(irowt:irowb, icoll:icolr);
+ lim.txtlimits = [icoll, icolr; irowt, irowb];
+ if (~isempty (rawlimits))
+ correction = [1; 1];
+ lim.txtlimits (:,1) = lim.txtlimits(:,1) + rawlimits(:,1) - correction;
+ lim.txtlimits (:,2) = lim.txtlimits(:,2) + rawlimits(:,1) - correction;
+ endif
+ else
+ # If no text cells found return empty text array
+ txtarr = {};
+ endif
+ endif
+
+ if (~no_num)
+ # Prepare numeric array. Set all text & empty cells to
+ # NaN. First get their locations
+ emptr = cellfun ('isempty', rawarr);
+ emptr(find (txtptr)) = 1;
+ if (all (all (emptr)))
+ numarr= [];
+ else
+ # Find leading & trailing empty rows
+ irowt = 1;
+ while (all(emptr(irowt, :))) irowt++; endwhile
+ irowb = nrows;
+ while (all(emptr(irowb, :))) irowb--; endwhile
+ icoll = 1;
+ while (all(emptr(:, icoll))) icoll++; endwhile
+ icolr = ncols;
+ while (all(emptr(:, icolr))) icolr--; endwhile
+
+ # Pre-crop rawarr
+ rawarr = rawarr (irowt:irowb, icoll:icolr);
+ # Build numerical array
+ numarr = zeros (irowb-irowt+1, icolr-icoll+1);
+ # Watch out for scalar (non-empty) numarr where emptr = 0
+ if (sum (emptr(:)) > 0)
+ numarr (emptr(irowt:irowb, icoll:icolr)) = NaN;
+ endif
+ numarr(~emptr(irowt:irowb, icoll:icolr)) = cell2mat (rawarr(~emptr(irowt:irowb, icoll:icolr)));
+ # Save limits
+ lim.numlimits = [icoll, icolr; irowt, irowb];
+ if (~isempty (rawlimits))
+ correction = [1; 1];
+ lim.numlimits(:,1) = lim.numlimits(:,1) + rawlimits(:,1) - correction(:);
+ lim.numlimits(:,2) = lim.numlimits(:,2) + rawlimits(:,1) - correction(:);
+ endif
+ endif
+ endif
+
+ lim.rawlimits = rawlimits;
+
+ endif
+
+endfunction