]> Creatis software - CreaPhase.git/blobdiff - octave_packages/io-1.0.19/getusedrange.m
Add a useful package (from Source forge) for octave
[CreaPhase.git] / octave_packages / io-1.0.19 / getusedrange.m
diff --git a/octave_packages/io-1.0.19/getusedrange.m b/octave_packages/io-1.0.19/getusedrange.m
new file mode 100644 (file)
index 0000000..c2d153b
--- /dev/null
@@ -0,0 +1,659 @@
+## Copyright (C) 2010,2011,2012 Philip Nienhuis <pr.nienhuis@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{toprow#}, @var{bottomrow#}, @var{leftcol#}, @var{rightcol#} ] = getusedrange (@var{spptr}, @var{shindex#})
+## Find occupied data range in worksheet @var{shindex#} in a spreadsheet
+## pointed to in struct @var{spptr} (either MS-Excel or
+## OpenOffice_org Calc).
+##
+## @var{shindex#} must be numeric and is 1-based. @var{spptr} can either
+## refer to an MS-Excel spreadsheet (spptr returned by xlsopen) or an
+## OpenOffice.org Calc spreadsheet (spptr returned by odsopen).
+## None of these inputs are checked!
+##
+## Be aware that especially for OpenOffice.org Calc (ODS) spreadsheets 
+## the results can only be obtained by counting all cells in all rows;
+## this can be fairly time-consuming. Reliable ods data size results can
+## only be obtained using UNO interface.
+## For the ActiveX (COM) interface the underlying Visual Basic call relies
+## on cached range values and counts empty cells with only formatting too,
+## so COM returns only approximate (but then usually too big) range values.
+##
+## Examples:
+##
+## @example
+##   [trow, brow, lcol, rcol] = getusedrange (ods2, 3);
+##   (which returns the outermost row & column numbers of the rectangle
+##    enveloping the occupied cells in the third sheet of an OpenOffice_org
+##    Calc spreadsheet pointedto in struct ods2)
+## @end example
+##
+## @example
+##   [trow, brow, lcol, rcol] = getusedrange (xls3, 3);
+##   (which returns the outermost row & column numbers of the rectangle
+##    enveloping the occupied cells in the third sheet of an Excel
+##    spreadsheet pointed to in struct xls3)
+## @end example
+##
+## @seealso {xlsopen, xlsclose, odsopen, odsclose, xlsfinfo, odsfinfo}
+##
+## @end deftypefn
+
+## Author: Philip Nienhuis <philip@JVC741>
+## Created: 2010-03-18 (First usable version) for ODS (java/OTK)
+## Updates:
+## 2010-03-20 Added Excel support (Java/POI)
+## 2010-05-23 Added in support for jOpenDocument ODS
+## 2010-05-31 Fixed bugs in getusedrange_jod.m
+## 2010-08-24 Added support for odfdom 0.8.6 (ODF Toolkit)
+## 2010-08-27 Added checks for input arguments
+##     ''     Indentation changed from tab to doublespace
+## 2010-10-07 Added COM support (at last!)
+## 2011-05-06 Experimental support for Java/UNO bridge
+## 2011-06-13 OpenXLS support added
+## 2011-09-08 Style & layout updates
+## 2012-01-26 Fixed "seealso" help string
+## 2012-06-08 Replaced tabs by double space
+##     ''     Added COM and OXS to message about supported interfaces
+##
+## Last subfunc update: 2012-06-08 (OTK)
+
+function [ trow, lrow, lcol, rcol ] = getusedrange (spptr, ii)
+
+  # Some checks
+  if ~isstruct (spptr), error ("Illegal spreadsheet pointer argument"); endif
+
+  if (strcmp (spptr.xtype, 'OTK'))
+    [ trow, lrow, lcol, rcol ] = getusedrange_otk (spptr, ii);
+  elseif (strcmp (spptr.xtype, 'JOD'))
+    [ trow, lrow, lcol, rcol ] = getusedrange_jod (spptr, ii);
+  elseif (strcmp (spptr.xtype, 'UNO'))
+    [ trow, lrow, lcol, rcol ] = getusedrange_uno (spptr, ii);
+  elseif (strcmp (spptr.xtype, 'COM'))
+    [ trow, lrow, lcol, rcol ] = getusedrange_com (spptr, ii);
+  elseif (strcmp (spptr.xtype, 'POI'))
+    [ trow, lrow, lcol, rcol ] = getusedrange_poi (spptr, ii);
+  elseif (strcmp (spptr.xtype, 'JXL'))
+    [ trow, lrow, lcol, rcol ] = getusedrange_jxl (spptr, ii);
+  elseif (strcmp (spptr.xtype, 'OXS'))
+    [ trow, lrow, lcol, rcol ] = getusedrange_oxs (spptr, ii);
+  else
+    error ("Only OTK, JOD, COM, POI, JXL and OXS interface implemented");
+  endif
+
+endfunction
+
+
+## Copyright (C) 2010,2011,2012 Philip Nienhuis, pr.nienhuis -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/>.
+
+## getusedrange_otk - get used range from ODS spreadsheet using ODF Toolkit
+
+## Author: Philip Nienhuis <philip@JVC741>
+## Created: 2010-03-18 (First usable version)
+## Updates:
+## 2010-08-24 Support for odfdom (ODF Toolkit) 0.8.6 checked; we still need 
+##            => TableTable API because 0.8.6 is fooled by empty lowermost
+##            filler rows comprising number-rows-repeated table attribute :-(
+##     ''     Indentation changed from tab to double space
+## 2010-11-13 Catched jOpenDocument bug (1.2bx) where string cells have no office:value-type
+##            attrib set (by JOD). Somehow OTK is more robust as it catches these cells;
+##            Currently this fix is just commented.
+## 2011-06-06 Fixed wrong if clause for finding last filler cells (L.160 & L.176)
+## 2011-09-12 Support for odfdom-0.8.7 added (API change for XPATH)
+## 2012-06-08 Support for odsfdom-0.8.8-incubator
+
+function [ trow, lrow, lcol, rcol ] = getusedrange_otk (ods, ii)
+
+  odfcont = ods.workbook;  # Local copy just in case
+
+  if (isfield (ods, 'odfvsn'))
+    if (strcmp (ods.odfvsn, '0.8.6') || strcmp (ods.odfvsn, '0.7.5'))
+      xpath = ods.app.getXPath;
+    else
+      # API changed in odfdom-0.8.7
+      xpath = ods.workbook.getXPath;
+    endif
+  else
+    error ("ODS file ptr struct for OTK interface seems broken.");
+  endif
+  
+  # Create an instance of type NODESET for use in subsequent statement
+  NODESET = java_get ('javax.xml.xpath.XPathConstants', 'NODESET');
+  # Get table-rows in sheet no. wsh. Sheet count = 1-based (!)
+  str = sprintf ("//table:table[%d]/table:table-row", ii);
+  sh = xpath.evaluate (str, odfcont, NODESET);
+  nr_of_trows = sh.getLength();
+
+  jj = 0;                  # Table row counter
+  trow = 0; drows = 0;     # Top data row, actual data row range
+  nrows = 0; reprows = 0;  # Scratch counter
+  rcol = 0; lcol = 1024;   # Rightmost and leftmost data column
+  while jj < nr_of_trows
+    row = sh.item(jj);
+    # Check for data rows
+    rw_char = char (row) (1:min(500, length (char (row))));
+    if (findstr ('office:value-type', rw_char) || findstr ('<text:', rw_char))
+      ++drows;
+      # Check for uppermost data row
+      if (~trow) 
+        trow = nrows + 1;
+        nrows = 0;
+      else
+        drows = drows + reprows;
+        reprows = 0;
+      endif
+
+    # Get leftmost cell column number
+      lcell = row.getFirstChild ();
+      cl_char = char (lcell);
+    # Swap the following lines into comment to catch a jOpenDocument bug which foobars OTK
+    # (JOD doesn't set <office:value-type='string'> attribute when writing strings
+      #if (isempty (findstr ('office:value-type', cl_char)) || isempty (findstr ('<text:', cl_char)))
+      if (isempty (findstr ('office:value-type', cl_char)))
+        lcol = min (lcol, lcell.getTableNumberColumnsRepeatedAttribute () + 1);
+      else
+        lcol = 1;
+      endif
+
+      # if rcol is already 1024 no more exploring for rightmost column is needed
+      if ~(rcol == 1024)
+        # Get rightmost cell column number by counting....
+        rc = 0;
+        for kk=1:row.getLength()
+          lcell = row.item(kk - 1);
+          rc = rc + lcell.getTableNumberColumnsRepeatedAttribute ();
+        endfor
+        # Watch out for filler tablecells
+        if (isempty (findstr ('office:value-type', char (lcell))) || isempty (findstr ('<text:', char (lcell))))
+          rc = rc - lcell.getTableNumberColumnsRepeatedAttribute ();
+        endif
+        rcol = max (rcol, rc);
+      endif
+    else
+      # Check for repeated tablerows
+      nrows = nrows + row.getTableNumberRowsRepeatedAttribute ();
+      if (trow)
+        reprows = reprows + row.getTableNumberRowsRepeatedAttribute ();
+      endif
+    endif
+    ++jj;
+  endwhile
+
+  if (trow)
+    lrow = trow + drows - 1;
+  else
+    # Empty sheet
+    lrow = 0; lcol = 0; rcol = 0;
+  endif
+
+endfunction
+
+## Copyright (C) 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/>.
+
+## getusedrange_jod
+
+## Author: Philip <Philip@DESKPRN>
+## Created: 2010-05-25
+## Last updates:
+## 2010-05-31 Fixed ignoring table-covered-cells; fixed count of sheets comprising just A1:A1
+##            Added option for wsh being a string argument 
+## 2010-08-12 Little textual adaptations
+## 2010-11-13 Catched jOpenDocument bug (1.2bx) where string cells have no office:value-type
+##     ''     attrb set (by JOD). Somehow OTK is more robust as it catches these cells
+## 2012-04-09 Fixed rowrepcnt (str2num not applied to tablerow)
+## 2012-04-18 Added getUsedRange() method for JOD 1.3x and above 
+
+function [ trow, brow, lcol, rcol ] = getusedrange_jod (ods, wsh)
+
+  # This function works by virtue of sheets in JOD actually being a Java string.
+  # It works outside of the Java memory/heap space which is an added benefit...
+  # (Read: this is one big dirty hack...... prone to crash Java on BIG spreadsheets)
+
+  if (isnumeric (wsh))
+    sh = char (ods.workbook.getSheet (wsh - 1));
+  else
+    sh = char (ods.workbook.getSheet (wsh));
+  endif
+
+  try
+    # Let's see if we have JOD v. 1.3x. If not, next call fails & we'll fall back to the old hack
+    sh_rng = char (sh.getUsedRange ());
+    if (isempty (sh_rng))
+      # Empty sheet
+      trow = brow = lcol = rcol = 0;
+    else
+      # Strip sheet name
+      sh_rng = sh_rng(length (sh.getName) + 2 : end);
+      # Get rid of period
+      sh_rng = strrep (sh_rng, '.', '');
+      [~, nr, nc, trow, lcol] = parse_sp_range (sh_rng);
+      brow = trow + nr - 1;
+      rcol = lcol + nc - 1;
+    endif
+  
+  catch
+    # Fall back to the old hack :-(
+    sh = char (sh);
+
+    # 1. Get table-row pointers
+    id_trow = strfind (sh, '<table:table-row');
+    id = strfind (sh, '</table:table>') - 1;
+    id_trow = [id_trow id];
+
+    trow = rcol = 0;
+    lcol = 1024; brow = 0;
+    if (~isempty (id))
+      # 2. Loop over all table-rows
+      rowrepcnt = 0;
+      for irow = 1:length (id_trow)-1
+        # Isolate single table-row
+        tablerow = sh(id_trow(irow):id_trow(irow+1)-1);
+        # Search table-cells. table-c covers both table-cell & table-covered-cell
+        id_tcell = strfind (tablerow, '<table:table-c'); 
+        id_tcell = [id_tcell id];
+        rowl = length (tablerow);
+        if (isempty (id_tcell(1:end-1)))
+          rowend = rowl;
+        else
+          rowend = id_tcell(1);
+        endif
+        # Add in table-number-rows-repeated attribute values
+        rowrept = strfind (tablerow(1:rowend), 'number-rows-repeated');
+        if (~isempty (rowrept))
+          [st, en] = regexp (tablerow(rowrept:min (rowend, rowrept+30)), '\d+');
+          rowrepcnt += str2num (tablerow(rowrept+st-1:min (rowend, rowrept+en-1))) - 1;
+        endif
+
+        # 3. Search table-cells. table-c is a table-covered-cell that is considered empty
+        id_tcell = strfind (tablerow, '<table:table-c');
+        if (~isempty (id_tcell))
+          # OK, this row has a value cell. Now table-covered-cells must be included.
+          id_tcell2 = strfind (tablerow, '<table:covered-t');
+          if (~isempty (id_tcell2)) id_tcell = sort ([id_tcell id_tcell2]); endif
+          id_tcell = [id_tcell rowl];
+          # Search for non-empty cells (i.e., with an office:value-type attribute). But:
+          # jOpenDocument 1.2b3 has a bug: it often doesn't set this attr for string cells
+          id_valtcell = strfind (tablerow, 'office:value-type=');
+          id_textonlycell = strfind (tablerow, '<text:');
+          id_valtcell = sort ([id_valtcell id_textonlycell]);
+          id_valtcell = [id_valtcell rowl];
+          if (~isempty (id_valtcell(1:end-1)))
+            brow = irow + rowrepcnt;
+            # First set trow if it hadn't already been found
+            if (~trow) trow = irow; endif
+            # Search for repeated table-cells
+            id_reptcell = strfind (tablerow, 'number-columns-repeated');
+            id_reptcell = [id_reptcell rowl];
+            # Search for leftmost non-empty table-cell. llcol = counter for this table-row
+            llcol = 1;
+            while (id_tcell (llcol) < id_valtcell(1) && llcol <= length (id_tcell) - 1)
+              ++llcol;
+            endwhile
+            --llcol;
+            # Compensate for repeated cells. First count all repeats left of llcol
+            ii = 1;
+            repcnt = 0;
+            if (~isempty (id_reptcell(1:end-1)))
+              # First try lcol
+              while (ii <= length (id_reptcell) - 1 && id_reptcell(ii) < id_valtcell(1))
+                # Add all repeat counts left of leftmost data tcell minus 1 for each
+                [st, en] = regexp (tablerow(id_reptcell(ii):id_reptcell(ii)+30), '\d+');
+                repcnt += str2num (tablerow(id_reptcell(ii)+st-1:id_reptcell(ii)+en-1)) - 1;
+                ++ii;
+              endwhile
+              # Next, add current repcnt value to llcol and update lcol
+              lcol = min (lcol, llcol + repcnt);
+              # Get last value table-cell in table-cell idx
+              jj = 1;
+              while (id_tcell (jj) < id_valtcell(length (id_valtcell)-1))
+                ++jj;
+              endwhile
+
+              # Get rest of column repeat counts in value table-cell range
+              while (ii < length (id_reptcell) && id_reptcell(ii) < id_tcell(jj))
+                # Add all repeat counts minus 1 for each tcell in value tcell range
+                [st, en] = regexp (tablerow(id_reptcell(ii):id_reptcell(ii)+30), '\d+');
+                repcnt += str2num (tablerow(id_reptcell(ii)+st-1:id_reptcell(ii)+en-1)) - 1;
+                ++ii;
+              endwhile
+            else
+              # In case left column = A
+              lcol = min (lcol, llcol);
+            endif
+            # Count all table-cells in value table-cell-range
+            ii = 1;       # Indexes cannot be negative
+            while (ii < length (id_tcell) && id_tcell(ii) < id_valtcell(length (id_valtcell) - 1))
+              ++ii;
+            endwhile
+            --ii;
+            rcol = max (rcol, ii + repcnt);
+          endif
+        endif
+      endfor
+    else
+      # No data found, empty sheet
+      lcol = rcol = brow = trow = 0;
+    endif
+
+  end_try_catch
+
+endfunction
+
+
+## Copyright (C) 2011,2012 Philip Nienhuis
+##
+## 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/>.
+
+## getusedrange_uno
+
+## Author: Philip Nienhuis <prnienhuis@users.sf.net>
+## Created: 2011-05-06
+## Updates:
+## 2011-06-29 Fixed wrong address range inference in case of sheet names containing period(s)
+## 2012-03-02 Adapted code to assess nr of range blocks to ';' separator fo LO3.5+
+
+function [ srow, erow, scol, ecol ] = getusedrange_uno (ods, ii)
+
+  # Get desired sheet
+  sheets = ods.workbook.getSheets ();
+  sh_names = sheets.getElementNames ();
+  unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XSpreadsheet');
+  sh = sheets.getByName(sh_names(ii)).getObject.queryInterface (unotmp);
+
+  # Prepare cell range query
+  unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XCellRangesQuery');
+  xRQ = sh.queryInterface (unotmp);
+  Cellflgs = javaObject ("java.lang.Short", "23");
+  ccells = xRQ.queryContentCells (Cellflgs);
+
+  # Get addresses of all blocks containing data
+  addrs = ccells.getRangeAddressesAsString ();
+
+  # Strip sheet name from addresses. Watch out, in LO3.5 they changed
+  # the separator from ',' to ';' (without telling me!)
+  # 1. Get nr of range blocks
+  nblks = numel (strfind (addrs, sh_names(ii)));
+  # 2. First try with ',' separator...
+  adrblks = strsplit (addrs, ',');
+  if (numel (adrblks) < nblks)
+    # Apparently we have a ';' separator, so try with semicolon
+    adrblks = strsplit (addrs, ';');
+  endif
+  if (isempty (adrblks))
+    srow = erow = scol = ecol = 0;
+    return
+  endif
+
+  # Find leftmost & rightmost columns, and highest and lowest row with data
+  srow = scol = 1e10;
+  erow = ecol = 0;
+  for ii=1:numel (adrblks)
+    # Check if address contains a sheet name in quotes (happens if name contains a period)
+    if (int8 (adrblks{ii}(1)) == 39)
+      # Strip sheet name part
+      idx = findstr (adrblks{ii}, "'.");
+      range = adrblks{ii}(idx+2 : end);
+    else
+      # Same, but tru strsplit()
+      range = strsplit (adrblks{ii}, '.'){2};
+    endif
+    [dummy, nrows, ncols, trow, lcol] = parse_sp_range (range);
+    brow = trow + nrows - 1;
+    rcol = lcol + ncols - 1;
+    srow = min (srow, trow);
+    scol = min (scol, lcol);
+    erow = max (erow, brow);
+    ecol = max (ecol, rcol);
+  endfor
+
+endfunction
+
+
+## Copyright (C) 2010,2011,2012 Philip Nienhuis
+##
+## 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/>.
+
+## getusedrange_com
+
+## Author: Philip Nienhuis <prnienhuis@users.sf.net>
+## Created: 2010-10-07
+
+function [ trow, brow, lcol, rcol ] = getusedrange_com (xls, ii)
+
+  sh = xls.workbook.Worksheets (ii);
+  
+  # Decipher used range. Beware, UsedRange() returns *cached* rectangle of
+  # all spreadsheet cells containing *anything*, including just formatting
+  # (i.e., empty cells are included too). ==> This is an approximation only
+  allcells = sh.UsedRange;
+  
+  # Get top left cell as a Range object
+  toplftcl = allcells.Columns(1).Rows(1);
+  
+  # Count number of rows & cols in virtual range from A1 to top left cell
+  lcol = sh.Range ("A1", toplftcl).columns.Count;
+  trow = sh.Range ("A1", toplftcl).rows.Count;
+  
+  # Add real occupied rows & cols to obtain end row & col
+  brow = trow + allcells.rows.Count() - 1;
+  rcol = lcol + allcells.columns.Count() - 1;
+  
+  # Check if there are real data
+  if ((lcol == rcol) && (trow = brow))
+    if (isempty (toplftcl.Value))
+      trow = brow = lcol = rcol = 0;
+    endif
+  endif
+
+endfunction
+
+
+## Copyright (C) 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/>.
+
+## getusedrange_poi - get range of occupied data cells from Excel using java/POI
+
+## Author: Philip <Philip@DESKPRN>
+## Created: 2010-03-20
+
+function [ trow, brow, lcol, rcol ] = getusedrange_poi (xls, ii)
+
+  persistent cblnk; cblnk = java_get ('org.apache.poi.ss.usermodel.Cell', 'CELL_TYPE_BLANK');
+
+  sh = xls.workbook.getSheetAt (ii-1);         # Java POI starts counting at 0 
+
+  trow = sh.getFirstRowNum ();                 # 0-based
+  brow = sh.getLastRowNum ();                  # 0-based
+  # Get column range
+  lcol = 1048577;  # OOXML (xlsx) max. + 1
+  rcol = 0;
+  botrow = brow;
+  for jj=trow:brow
+    irow = sh.getRow (jj);
+    if (~isempty (irow))
+      scol = (irow.getFirstCellNum).intValue ();
+      lcol = min (lcol, scol);
+      ecol = (irow.getLastCellNum).intValue () - 1;
+      rcol = max (rcol, ecol);
+      # Keep track of lowermost non-empty row as getLastRowNum() is unreliable
+      if ~(irow.getCell(scol).getCellType () == cblnk && irow.getCell(ecol).getCellType () == cblnk)
+        botrow = jj;
+      endif
+    endif
+  endfor
+  if (lcol > 1048576)
+    # Empty sheet
+    trow = 0; brow = 0; lcol = 0; rcol = 0;
+  else
+    brow = min (brow, botrow) + 1; ++trow; ++lcol; ++rcol;    # 1-based return values
+  endif
+
+endfunction
+
+
+## Copyright (C) 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/>.
+
+## getusedrange_jxl - get occupied data cell range from Excel sheet
+## using java/JExcelAPI
+
+## Author: Philip <Philip@DESKPRN>
+## Created: 2010-03-20
+
+function [ trow, brow, lcol, rcol ] = getusedrange_jxl (xls, wsh)
+
+  persistent emptycell = (java_get ('jxl.CellType', 'EMPTY')).toString ();
+
+  sh = xls.workbook.getSheet (wsh - 1);      # JXL sheet count 0-based
+
+  brow = sh.getRows ();
+  rcol = sh.getColumns ();
+  
+  if (brow == 0 || rcol == 0)
+    # Empty sheet
+    trow = 0; lcol = 0; brow = 0; rcol = 0;
+  else
+    trow = brow + 1;
+    lcol = rcol + 1;
+    for ii=0:brow-1    # For loop coz we must check ALL rows for leftmost column
+      emptyrow = 1;
+      jj = 0;
+      while (jj < rcol && emptyrow)   # While loop => only til first non-empty cell
+        cell = sh.getCell (jj, ii);
+        if ~(strcmp (char (cell.getType ()), emptycell))
+          lcol = min (lcol, jj + 1);
+          emptyrow = 0;
+        endif
+        ++jj;
+      endwhile
+      if ~(emptyrow) trow = min (trow, ii + 1); endif
+    endfor
+  endif
+
+endfunction
+
+
+## Copyright (C) 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/>.
+
+## getusedrange_oxs
+
+## Author: Philip <Philip@DESKPRN>
+## Created: 2011-06-13
+## Updates:
+## 2011-06-29 try-catch to be able to skip non-data (e.g., graph) sheets
+
+function [ trow, brow, lcol, rcol ] = getusedrange_oxs (xls, wsh)
+
+  sh = xls.workbook.getWorkSheet (wsh - 1);
+  try
+    # Intriguing:  sh.getFirst<> is off by one, sh.getLast<> = OK.... 8-Z 
+    trow = sh.getFirstRow () + 1;
+    brow = sh.getLastRow ();
+    lcol = sh.getFirstCol () + 1;
+    rcol = sh.getLastCol ();
+  catch
+    # Might be an empty sheet
+    trow = brow = lcol = rcol = 0;
+  end_try_catch
+  # Check for empty sheet
+  if ((trow > brow) || (lcol > rcol)), trow = brow = lcol = rcol = 0; endif
+
+endfunction